diff options
Diffstat (limited to 'tests/auto/qml')
1127 files changed, 26200 insertions, 5879 deletions
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt index 77663f350d..6b81f4c616 100644 --- a/tests/auto/qml/CMakeLists.txt +++ b/tests/auto/qml/CMakeLists.txt @@ -17,8 +17,9 @@ add_subdirectory(qjsonbinding) add_subdirectory(qqmlfile) if(NOT INTEGRITY) - # The INTEGRITY linker tends to crash on tst_qqmlfileselector + # The INTEGRITY linker tends to crash on tst_qqmlfileselector and tst_qml_common add_subdirectory(qqmlfileselector) + add_subdirectory(common) endif() # Limit set of tests to run for static Qt builds. @@ -33,7 +34,9 @@ add_subdirectory(qqmlerror) add_subdirectory(qqmlincubator) add_subdirectory(qqmlinfo) add_subdirectory(qqmllistreference) -add_subdirectory(qqmllocale) +if(QT_FEATURE_qml_locale) + add_subdirectory(qqmllocale) +endif() add_subdirectory(qqmlmetaobject) if(NOT ANDROID) # QTBUG-100003 add_subdirectory(qqmlmoduleplugin) @@ -45,7 +48,9 @@ add_subdirectory(qqmlpromise) add_subdirectory(qtqmlmodules) add_subdirectory(qquickfolderlistmodel) add_subdirectory(qqmlapplicationengine) -add_subdirectory(qqmlsettings) +if(QT_FEATURE_settings) + add_subdirectory(qqmlsettings) +endif() if(NOT INTEGRITY) # There's no mounted filesystem on INTEGRITY therefore skipping qmldiskcache @@ -55,7 +60,7 @@ endif() add_subdirectory(qqmlmetatype) if(TARGET Qt::Quick) add_subdirectory(qmltc_manual) - # QML Type Compiler tests cannot be run on webOS due to missing support for multiple + # QML type compiler tests cannot be run on webOS due to missing support for multiple # consecutive window creation from a single QtWayland client if(NOT WEBOS) add_subdirectory(qmltc) @@ -76,6 +81,7 @@ if(TARGET Qt::Widgets) add_subdirectory(qjsengine) add_subdirectory(qjsvalue) add_subdirectory(qjsmanagedvalue) + add_subdirectory(qwidgetsinqml) endif() if(QT_FEATURE_process AND QT_FEATURE_qml_debug) add_subdirectory(debugger) @@ -140,9 +146,14 @@ if(QT_FEATURE_private_tests) add_subdirectory(qqmltreemodeltotablemodel) add_subdirectory(qv4assembler) add_subdirectory(qv4mm) + add_subdirectory(qv4estable) add_subdirectory(qv4identifiertable) add_subdirectory(qv4regexp) - if(QT_FEATURE_process AND NOT QNX) + add_subdirectory(qv4urlobject) + if(QT_FEATURE_process AND NOT QNX AND NOT + (ANDROID AND CLANG AND + CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0" AND + CMAKE_CXX_COMPILER_VERSION VERSION_LESS "15.0.0")) add_subdirectory(ecmascripttests) endif() add_subdirectory(bindingdependencyapi) diff --git a/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt b/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt index 03099ef616..9e6ba7fcb6 100644 --- a/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt +++ b/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qabstractanimationjob Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractanimationjob LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractanimationjob SOURCES tst_qabstractanimationjob.cpp diff --git a/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp b/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp index e2e5e59e95..40eb8a6910 100644 --- a/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp +++ b/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/private/qabstractanimationjob_p.h> @@ -53,8 +53,7 @@ void tst_QAbstractAnimationJob::construction() void tst_QAbstractAnimationJob::destruction() { - TestableQAbstractAnimation *anim = new TestableQAbstractAnimation; - delete anim; + std::unique_ptr<TestableQAbstractAnimation> anim = std::make_unique<TestableQAbstractAnimation>(); } void tst_QAbstractAnimationJob::currentLoop() diff --git a/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt b/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt index 286bdeed4a..db46ef71dc 100644 --- a/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt +++ b/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qanimationgroupjob Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qanimationgroupjob LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qanimationgroupjob SOURCES tst_qanimationgroupjob.cpp diff --git a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp index cac9581803..a3fe188b65 100644 --- a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -236,7 +236,7 @@ void tst_QAnimationGroupJob::addChildTwice() { QAbstractAnimationJob *subGroup; QAbstractAnimationJob *subGroup2; - auto *parent = new QSequentialAnimationGroupJob(); + auto parent = std::make_unique<QSequentialAnimationGroupJob>(); subGroup = new QAbstractAnimationJob; parent->appendAnimation(subGroup); @@ -266,7 +266,6 @@ void tst_QAnimationGroupJob::addChildTwice() QCOMPARE(parent->children()->first(), subGroup2); QCOMPARE(parent->children()->last(), subGroup); - delete parent; } QTEST_MAIN(tst_QAnimationGroupJob) diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt b/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt index 74e3b3fed4..0a275aaaa1 100644 --- a/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt +++ b/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qparallelanimationgroupjob Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qparallelanimationgroupjob LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qparallelanimationgroupjob SOURCES tst_qparallelanimationgroupjob.cpp diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp index a11ae75033..91c47586c1 100644 --- a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -395,8 +395,8 @@ void tst_QParallelAnimationGroupJob::deleteChildrenWithRunningGroup() // test if children can be activated when their group is stopped QParallelAnimationGroupJob group; - TestAnimation *anim1 = new TestAnimation(200); - group.appendAnimation(anim1); + std::unique_ptr<TestAnimation> anim1 = std::make_unique<TestAnimation>(200); + group.appendAnimation(anim1.get()); QCOMPARE(group.duration(), anim1->duration()); @@ -406,7 +406,7 @@ void tst_QParallelAnimationGroupJob::deleteChildrenWithRunningGroup() QTRY_VERIFY(group.currentLoopTime() > 0); - delete anim1; + anim1.reset(); QVERIFY(group.children()->isEmpty()); QCOMPARE(group.duration(), 0); QCOMPARE(group.state(), QAnimationGroupJob::Stopped); @@ -793,34 +793,34 @@ void tst_QParallelAnimationGroupJob::addAndRemoveDuration() { QParallelAnimationGroupJob group; QCOMPARE(group.duration(), 0); - TestAnimation *test = new TestAnimation(250); // 0, duration = 250; - group.appendAnimation(test); + std::unique_ptr<TestAnimation> test = std::make_unique<TestAnimation>(250); // 0, duration = 250; + group.appendAnimation(test.get()); QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(&group)); QCOMPARE(test->duration(), 250); QCOMPARE(group.duration(), 250); - TestAnimation *test2 = new TestAnimation(750); // 1 - group.appendAnimation(test2); + std::unique_ptr<TestAnimation> test2 = std::make_unique<TestAnimation>(750); // 1 + group.appendAnimation(test2.get()); QCOMPARE(test2->group(), static_cast<QAnimationGroupJob*>(&group)); QCOMPARE(group.duration(), 750); - TestAnimation *test3 = new TestAnimation(500); // 2 - group.appendAnimation(test3); + std::unique_ptr<TestAnimation> test3 = std::make_unique<TestAnimation>(500); // 2 + group.appendAnimation(test3.get()); QCOMPARE(test3->group(), static_cast<QAnimationGroupJob*>(&group)); QCOMPARE(group.duration(), 750); - group.removeAnimation(test2); // remove the one with duration = 750 - delete test2; + group.removeAnimation(test2.get()); // remove the one with duration = 750 + test2.reset(); QCOMPARE(group.duration(), 500); - group.removeAnimation(test3); // remove the one with duration = 500 - delete test3; + group.removeAnimation(test3.get()); // remove the one with duration = 500 + test3.reset(); QCOMPARE(group.duration(), 250); - group.removeAnimation(test); // remove the last one (with duration = 250) + group.removeAnimation(test.get()); // remove the last one (with duration = 250) QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(nullptr)); QCOMPARE(group.duration(), 0); - delete test; + test.reset(); } void tst_QParallelAnimationGroupJob::pauseResume() @@ -878,16 +878,16 @@ void tst_QParallelAnimationGroupJob::pauseResume() void tst_QParallelAnimationGroupJob::crashWhenRemovingUncontrolledAnimation() { QParallelAnimationGroupJob group; - TestAnimation *anim = new TestAnimation; + std::unique_ptr<TestAnimation> anim = std::make_unique<TestAnimation>(); anim->setLoopCount(-1); - TestAnimation *anim2 = new TestAnimation; + std::unique_ptr<TestAnimation> anim2 = std::make_unique<TestAnimation>(); anim2->setLoopCount(-1); - group.appendAnimation(anim); - group.appendAnimation(anim2); + group.appendAnimation(anim.get()); + group.appendAnimation(anim2.get()); group.start(); - delete anim; + anim.reset(); // it would crash here because the internals of the group would still have a reference to anim - delete anim2; + anim2.reset(); } void tst_QParallelAnimationGroupJob::uncontrolledWithLoops() diff --git a/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt b/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt index 5f67083ea4..2dc9e42f90 100644 --- a/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt +++ b/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qpauseanimationjob Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpauseanimationjob LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpauseanimationjob SOURCES tst_qpauseanimationjob.cpp diff --git a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp index db433af8c6..c658f590c9 100644 --- a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp +++ b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt b/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt index e9a680ace5..fa7ad06a03 100644 --- a/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt +++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qsequentialanimationgroupjob Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsequentialanimationgroupjob LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsequentialanimationgroupjob SOURCES tst_qsequentialanimationgroupjob.cpp diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp index cc67df420c..71aa2240ff 100644 --- a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQml/private/qsequentialanimationgroupjob_p.h> @@ -110,7 +110,7 @@ public: { states << newState; if (beEvil) { - delete job->group(); + delete job->group(); //manual delete intentional groupDeleted = true; } } @@ -1090,8 +1090,8 @@ void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup() // test if children can be activated when their group is stopped QSequentialAnimationGroupJob group; - TestAnimation *anim1 = new TestAnimation(200); - group.appendAnimation(anim1); + std::unique_ptr<TestAnimation> anim1 = std::make_unique<TestAnimation>(200); + group.appendAnimation(anim1.get()); QCOMPARE(group.duration(), anim1->duration()); @@ -1102,7 +1102,7 @@ void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup() QTest::qWait(100); QTRY_VERIFY(group.currentLoopTime() > 0); - delete anim1; + anim1.reset(); QVERIFY(group.children()->isEmpty()); QCOMPARE(group.duration(), 0); QCOMPARE(group.state(), QAnimationGroupJob::Stopped); diff --git a/tests/auto/qml/bindingdependencyapi/CMakeLists.txt b/tests/auto/qml/bindingdependencyapi/CMakeLists.txt index e8ab157548..febc0bcfdd 100644 --- a/tests/auto/qml/bindingdependencyapi/CMakeLists.txt +++ b/tests/auto/qml/bindingdependencyapi/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_bindingdependencyapi Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_bindingdependencyapi LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp index 5fbf28b15b..6b1f4e1622 100644 --- a/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp +++ b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -104,12 +104,12 @@ void tst_bindingdependencyapi::testSingleDep() QQmlEngine engine; QQmlComponent c(&engine); c.setData(code, QUrl()); - QObject *rect = c.create(); + std::unique_ptr<QObject> rect { c.create() }; QTest::qWait(10); - QVERIFY(rect != nullptr); + QVERIFY(rect.get() != nullptr); QObject *text = rect->findChildren<QQuickText *>().front(); - QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName); + QObject *referencedObject = rect->objectName() == referencedObjectName ? rect.get() : rect->findChild<QObject *>(referencedObjectName); auto data = QQmlData::get(text); QVERIFY(data); @@ -125,8 +125,6 @@ void tst_bindingdependencyapi::testSingleDep() QCOMPARE(dependency.property().name(), "labelText"); QCOMPARE(dependency.read().toString(), QStringLiteral("Hello world!")); QCOMPARE(dependency, QQmlProperty(referencedObject, "labelText")); - - delete rect; } bool tst_bindingdependencyapi::findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value) @@ -183,12 +181,12 @@ void tst_bindingdependencyapi::testManyDeps() QQmlEngine engine; QQmlComponent c(&engine); c.setData(code, QUrl()); - QObject *rect = c.create(); + std::unique_ptr<QObject> rect { c.create() }; if (c.isError()) { qWarning() << c.errorString(); } QTest::qWait(100); - QVERIFY(rect != nullptr); + QVERIFY(rect.get() != nullptr); QObject *text = rect->findChildren<QQuickText *>().front(); QObject *configObj = rect->findChild<QObject *>("config"); @@ -201,11 +199,9 @@ void tst_bindingdependencyapi::testManyDeps() auto dependencies = binding->dependencies(); QCOMPARE(dependencies.size(), 3); - QVERIFY(findProperties(dependencies, rect, "name", "world")); + QVERIFY(findProperties(dependencies, rect.get(), "name", "world")); QVERIFY(findProperties(dependencies, text, "greeting", "Hello")); QVERIFY(findProperties(dependencies, configObj, "helloWorldTemplate", "%1 %2!")); - - delete rect; } void tst_bindingdependencyapi::testConditionalDependencies_data() @@ -263,12 +259,12 @@ void tst_bindingdependencyapi::testConditionalDependencies() QQmlEngine engine; QQmlComponent c(&engine); c.setData(code, QUrl()); - QObject *rect = c.create(); + std::unique_ptr<QObject> rect { c.create() }; QTest::qWait(10); - QVERIFY(rect != nullptr); + QVERIFY(rect.get() != nullptr); QObject *text = rect->findChildren<QQuickText *>().front(); - QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName); + QObject *referencedObject = rect->objectName() == referencedObjectName ? rect.get() : rect->findChild<QObject *>(referencedObjectName); auto data = QQmlData::get(text); QVERIFY(data); @@ -290,8 +286,6 @@ void tst_bindingdependencyapi::testConditionalDependencies() dependencies = binding->dependencies(); QCOMPARE(dependencies.size(), 1); QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false)); - - delete rect; } void tst_bindingdependencyapi::testBindingLoop() @@ -306,12 +300,12 @@ void tst_bindingdependencyapi::testBindingLoop() "text: labelText\n" "}\n" "}"), QUrl()); - QObject *rect = c.create(); + std::unique_ptr<QObject> rect { c.create() }; if (c.isError()) { qWarning() << c.errorString(); } QTest::qWait(100); - QVERIFY(rect != nullptr); + QVERIFY(rect.get() != nullptr); QObject *text = rect->findChildren<QQuickText *>().front(); auto data = QQmlData::get(text); @@ -324,10 +318,8 @@ void tst_bindingdependencyapi::testBindingLoop() QCOMPARE(dependencies.size(), 1); auto dependency = dependencies.front(); QVERIFY(dependency.isValid()); - QCOMPARE(quintptr(dependency.object()), quintptr(rect)); + QCOMPARE(quintptr(dependency.object()), quintptr(rect.get())); QCOMPARE(dependency.property().name(), "labelText"); - - delete rect; } void tst_bindingdependencyapi::testQproperty() diff --git a/tests/auto/qml/common/CMakeLists.txt b/tests/auto/qml/common/CMakeLists.txt new file mode 100644 index 0000000000..4b7714a11d --- /dev/null +++ b/tests/auto/qml/common/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_policy(SET QTP0001 NEW) + +qt_internal_add_test(tst_qml_common + SOURCES + tst_qml_common.cpp tst_qml_common.h + LIBRARIES + Qt::QmlPrivate +) diff --git a/tests/auto/qml/common/tst_qml_common.cpp b/tests/auto/qml/common/tst_qml_common.cpp new file mode 100644 index 0000000000..e9baae5d53 --- /dev/null +++ b/tests/auto/qml/common/tst_qml_common.cpp @@ -0,0 +1,168 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "tst_qml_common.h" + +using namespace Qt::StringLiterals; + +void tst_qml_common::tst_propertyNameToChangedSignalName_data() +{ + QTest::addColumn<QString>("property"); + QTest::addColumn<QString>("expected"); + + QTest::addRow("normalProperty") << u"helloWorld"_s << u"helloWorldChanged"_s; + QTest::addRow("changedProperty") << u"changed"_s << u"changedChanged"_s; + QTest::addRow("chängedProperty") << u"chänged"_s << u"chängedChanged"_s; +} +void tst_qml_common::tst_propertyNameToChangedSignalName() +{ + QFETCH(QString, property); + QFETCH(QString, expected); + + QVERIFY(QQmlSignalNames::isChangedSignalName(expected)); + QCOMPARE(QQmlSignalNames::propertyNameToChangedSignalName(property), expected); + QCOMPARE(QQmlSignalNames::changedSignalNameToPropertyName(expected).value(), property); +} + +void tst_qml_common::tst_propertyNameToChangedHandlerName_data() +{ + QTest::addColumn<QString>("property"); + QTest::addColumn<QString>("expected"); + + QTest::addRow("normalProperty") << u"helloWorld"_s << u"onHelloWorldChanged"_s; + QTest::addRow("changedProperty") << u"changed"_s << u"onChangedChanged"_s; + QTest::addRow("chängedProperty") << u"chänged"_s << u"onChängedChanged"_s; + QTest::addRow("äProperty") << u"ä"_s << u"onÄChanged"_s; + QTest::addRow("_Property") << u"_"_s << u"on_Changed"_s; + QTest::addRow("___123aProperty") << u"___123a"_s << u"on___123AChanged"_s; + QTest::addRow("___123Property") << u"___123"_s << u"on___123Changed"_s; + QTest::addRow("AProperty") << u"A"_s << u"onAChanged"_s; + QTest::addRow("_Property") << u"_"_s << u"on_Changed"_s; + QTest::addRow("$Property") << u"$"_s << u"on$Changed"_s; +} +void tst_qml_common::tst_propertyNameToChangedHandlerName() +{ + QFETCH(QString, property); + QFETCH(QString, expected); + + QVERIFY(QQmlSignalNames::isChangedHandlerName(expected)); + QCOMPARE(QQmlSignalNames::propertyNameToChangedHandlerName(property), expected); + auto reverse = QQmlSignalNames::changedHandlerNameToPropertyName(expected); + QVERIFY(reverse); + QEXPECT_FAIL("AProperty", + "Cannot distinguish between property names starting with upper case" + " from properties starting with a lower case letter.", + Continue); + QCOMPARE(reverse.value(), property); +} + +void tst_qml_common::tst_signalNameToHandlerName_data() +{ + QTest::addColumn<QString>("signalName"); + QTest::addColumn<QString>("expected"); + + QTest::addRow("normalProperty") << u"helloWorld"_s << u"onHelloWorld"_s; + QTest::addRow("changedProperty") << u"changed"_s << u"onChanged"_s; + QTest::addRow("chängedProperty") << u"chänged"_s << u"onChänged"_s; + QTest::addRow("äProperty") << u"ä"_s << u"onÄ"_s; + QTest::addRow("_Property") << u"_"_s << u"on_"_s; + QTest::addRow("___123aProperty") << u"___123a"_s << u"on___123A"_s; + QTest::addRow("___123Property") << u"___123"_s << u"on___123"_s; + QTest::addRow("AProperty") << u"A"_s << u"onA"_s; + QTest::addRow("_Property") << u"_"_s << u"on_"_s; + QTest::addRow("$Property") << u"$"_s << u"on$"_s; +} + +void tst_qml_common::tst_signalNameToHandlerName() +{ + QFETCH(QString, signalName); + QFETCH(QString, expected); + + QVERIFY(QQmlSignalNames::isHandlerName(expected)); + QCOMPARE(QQmlSignalNames::signalNameToHandlerName(signalName), expected); + + auto result = QQmlSignalNames::handlerNameToSignalName(expected); + QVERIFY(result.has_value()); + + QEXPECT_FAIL("AProperty", + "Cannot distinguish between signal names starting with upper case" + " from signal names starting with a lower case letter.", + Continue); + + QCOMPARE(result.value(), signalName); +} + +void tst_qml_common::tst_changedSignalNameToPropertyName_data() +{ + // only test when it should return nothing, see also tst_propertyNameToChangedSignalName. + QTest::addColumn<QString>("changedSignalName"); + + QTest::addRow("normalProperty") << u"helloWorld"_s; + QTest::addRow("Changed") << u"Changed"_s; + QTest::addRow("empty") << u""_s; +} + +void tst_qml_common::tst_changedSignalNameToPropertyName() +{ + QFETCH(QString, changedSignalName); + + QVERIFY(!QQmlSignalNames::changedSignalNameToPropertyName(changedSignalName).has_value()); +} + +void tst_qml_common::tst_changedHandlerNameToPropertyName_data() +{ + // only test when it should return nothing, see also tst_propertyNameToChangedHandler. + QTest::addColumn<QString>("changedHandler"); + + QTest::addRow("normalProperty") << u"helloWorld"_s; + QTest::addRow("Changed") << u"Changed"_s; + QTest::addRow("empty") << u""_s; + QTest::addRow("empty2") << u"onChanged"_s; + QTest::addRow("on") << u"on"_s; +} +void tst_qml_common::tst_changedHandlerNameToPropertyName() +{ + QFETCH(QString, changedHandler); + + QVERIFY(!QQmlSignalNames::changedHandlerNameToPropertyName(changedHandler).has_value()); +} + +void tst_qml_common::tst_handlerNameToSignalName_data() +{ + // only test when it should return nothing, see also tst_signalNameToHandlerName. + QTest::addColumn<QString>("handler"); + + QTest::addRow("normalProperty") << u"helloWorld"_s; + QTest::addRow("Changed") << u"Changed"_s; + QTest::addRow("empty") << u""_s; + QTest::addRow("on") << u"on"_s; +} +void tst_qml_common::tst_handlerNameToSignalName() +{ + QFETCH(QString, handler); + + QVERIFY(!QQmlSignalNames::handlerNameToSignalName(handler).has_value()); +} + +void tst_qml_common::tst_isChangedHandlerName_data() +{ + QTest::addColumn<QString>("name"); + QTest::addColumn<bool>("expected"); + + QTest::addRow("normalProperty") << u"helloWorld"_s << false; + QTest::addRow("Changed") << u"Changed"_s << false; + QTest::addRow("empty") << u""_s << false; + QTest::addRow("empty2") << u"onChanged"_s << false; + QTest::addRow("on") << u"on"_s << false; + QTest::addRow("on_Changed") << u"on_Changed"_s << true; + QTest::addRow("on$Changed") << u"on$Changed"_s << true; +} +void tst_qml_common::tst_isChangedHandlerName() +{ + QFETCH(QString, name); + QFETCH(bool, expected); + + QCOMPARE(QQmlSignalNames::isChangedHandlerName(name), expected); +} + +QTEST_MAIN(tst_qml_common) diff --git a/tests/auto/qml/common/tst_qml_common.h b/tests/auto/qml/common/tst_qml_common.h new file mode 100644 index 0000000000..98e462ded6 --- /dev/null +++ b/tests/auto/qml/common/tst_qml_common.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef TST_QML_COMMON_H +#define TST_QML_COMMON_H + +#include <QtCore/qstring.h> +#include <QtCore/qlibraryinfo.h> +#include <QtTest/qtest.h> +#include <QtQml/private/qqmlsignalnames_p.h> + +class tst_qml_common : public QObject +{ + Q_OBJECT + +private slots: + void tst_propertyNameToChangedSignalName_data(); + void tst_propertyNameToChangedSignalName(); + void tst_propertyNameToChangedHandlerName(); + void tst_propertyNameToChangedHandlerName_data(); + void tst_signalNameToHandlerName(); + void tst_signalNameToHandlerName_data(); + + void tst_changedSignalNameToPropertyName(); + void tst_changedSignalNameToPropertyName_data(); + void tst_changedHandlerNameToPropertyName(); + void tst_changedHandlerNameToPropertyName_data(); + void tst_handlerNameToSignalName(); + void tst_handlerNameToSignalName_data(); + + void tst_isChangedHandlerName(); + void tst_isChangedHandlerName_data(); +}; + +#endif // TST_QML_COMMON_H diff --git a/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt b/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt index d3df19bc40..07b6e26544 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt +++ b/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qdebugmessageservice Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdebugmessageservice LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml b/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml index b8134b37e6..bdb91d08e5 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml +++ b/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp index 11c4203429..cd39b51b25 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp +++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only //QQmlDebugTest #include "../shared/debugutil_p.h" diff --git a/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt b/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt index 146999b9bd..91941de381 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt +++ b/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qpacketprotocol Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpacketprotocol LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpacketprotocol SOURCES ../shared/debugutil.cpp ../shared/debugutil_p.h diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp index 81d8694406..2ce062b0cc 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp +++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QSignalSpy> #include <QTimer> @@ -18,9 +18,9 @@ class tst_QPacketProtocol : public QObject Q_OBJECT private: - QTcpServer *m_server; - QTcpSocket *m_client; - QTcpSocket *m_serverConn; + std::unique_ptr<QTcpServer> m_server; + std::unique_ptr<QTcpSocket> m_client; + std::unique_ptr<QTcpSocket> m_serverConn; private slots: void init(); @@ -34,34 +34,34 @@ private slots: void tst_QPacketProtocol::init() { - m_server = new QTcpServer(this); + m_server = std::make_unique<QTcpServer>(this); m_serverConn = nullptr; QVERIFY(m_server->listen(QHostAddress("127.0.0.1"))); - m_client = new QTcpSocket(this); + m_client = std::make_unique<QTcpSocket>(this); - QSignalSpy serverSpy(m_server, SIGNAL(newConnection())); - QSignalSpy clientSpy(m_client, SIGNAL(connected())); + QSignalSpy serverSpy(m_server.get(), SIGNAL(newConnection())); + QSignalSpy clientSpy(m_client.get(), SIGNAL(connected())); m_client->connectToHost(m_server->serverAddress(), m_server->serverPort()); QVERIFY(clientSpy.size() > 0 || clientSpy.wait()); QVERIFY(serverSpy.size() > 0 || serverSpy.wait()); - m_serverConn = m_server->nextPendingConnection(); + m_serverConn.reset(m_server->nextPendingConnection()); } void tst_QPacketProtocol::cleanup() { - delete m_client; - delete m_serverConn; - delete m_server; + m_client.reset(); + m_serverConn.reset(); + m_server.reset(); } void tst_QPacketProtocol::send() { - QPacketProtocol in(m_client); - QPacketProtocol out(m_serverConn); + QPacketProtocol in(m_client.get()); + QPacketProtocol out(m_serverConn.get()); QByteArray ba; int num; @@ -82,8 +82,8 @@ void tst_QPacketProtocol::packetsAvailable() { QFETCH(int, packetCount); - QPacketProtocol out(m_client); - QPacketProtocol in(m_serverConn); + QPacketProtocol out(m_client.get()); + QPacketProtocol in(m_serverConn.get()); QCOMPARE(out.packetsAvailable(), qint64(0)); QCOMPARE(in.packetsAvailable(), qint64(0)); @@ -109,8 +109,8 @@ void tst_QPacketProtocol::packetsAvailable_data() void tst_QPacketProtocol::read() { - QPacketProtocol in(m_client); - QPacketProtocol out(m_serverConn); + QPacketProtocol in(m_client.get()); + QPacketProtocol out(m_serverConn.get()); QVERIFY(in.read().isEmpty()); diff --git a/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt index 6b33ee0024..d0a63440da 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldebugclient Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebugclient LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmldebugclient SOURCES ../shared/debugutil.cpp ../shared/debugutil_p.h diff --git a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp index 509791b96d..d0e9c22f2c 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp +++ b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include "qqmldebugtestservice.h" @@ -44,11 +44,16 @@ void tst_QQmlDebugClient::initTestCase() m_service = new QQmlDebugTestService("tst_QQmlDebugClient::handshake()"); - foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) + const QStringList debuggerServices = QQmlDebuggingEnabler::debuggerServices(); + for (const QString &service : debuggerServices) QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); - foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) + + const QStringList inspectorServices = QQmlDebuggingEnabler::inspectorServices(); + for (const QString &service : inspectorServices) QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); - foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) + + const QStringList profilerServices = QQmlDebuggingEnabler::profilerServices(); + for (const QString &service : profilerServices) QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT); diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt index b73c2aa218..53b1e9c683 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldebuggingenabler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebuggingenabler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmldebuggingenabler SOURCES ../../shared/debugutil.cpp ../../shared/debugutil_p.h diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml index 9bd5130e71..2474016705 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp index ab599e9a23..03a65f56ca 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include "qqmldebugprocess_p.h" @@ -41,14 +41,14 @@ void tst_QQmlDebuggingEnabler::data() QTest::addColumn<bool>("blockMode"); QTest::addColumn<QStringList>("services"); - QStringList connectors({ + const QStringList connectors({ QLatin1String("QQmlDebugServer"), QLatin1String("QQmlNativeDebugConnector") }); - QList<bool> blockModes({ true, false }); + const QList<bool> blockModes({ true, false }); - QList<QStringList> serviceLists({ + const QList<QStringList> serviceLists({ QStringList(), QQmlDebuggingEnabler::nativeDebuggerServices(), QQmlDebuggingEnabler::debuggerServices(), @@ -57,9 +57,9 @@ void tst_QQmlDebuggingEnabler::data() QQmlDebuggingEnabler::debuggerServices() + QQmlDebuggingEnabler::inspectorServices() }); - foreach (const QString &connector, connectors) { - foreach (bool blockMode, blockModes) { - foreach (const QStringList &serviceList, serviceLists) { + for (const QString &connector : connectors) { + for (bool blockMode : blockModes) { + for (const QStringList &serviceList : serviceLists) { QString name = connector + QLatin1Char(',') + QLatin1String(blockMode ? "block" : "noblock") + QLatin1Char(',') + serviceList.join(QLatin1Char('-')); @@ -103,7 +103,7 @@ void tst_QQmlDebuggingEnabler::qmlscene() m_clients = QQmlDebugTest::createOtherClients(m_connection); m_connection->connectToHost("127.0.0.1", m_process->debugPort()); QVERIFY(m_connection->waitForConnected()); - foreach (QQmlDebugClient *client, m_clients) + for (QQmlDebugClient *client : std::as_const(m_clients)) QCOMPARE(client->state(), (services.isEmpty() || services.contains(client->name())) ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); } diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp index 33bb0d6d37..6baaf8f430 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qcoreapplication.h> #include <QtCore/qlibraryinfo.h> diff --git a/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt index e48f8e965d..3d65442ebf 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldebugjs Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebugjs LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml index 03bc85f5a4..6650096b55 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.11 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml index 3a846c0eeb..254924743e 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml index 2d747c28a5..3ebe44ea76 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml index a4952f79cc..0f02f62750 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml index e67713ba67..f58b474876 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml index dc3be646ce..807735573e 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml index 0af1188015..bcd8f4e9d5 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import "test.js" as Script diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml index 6cf2d7c030..4cc13a7952 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml index 026911d1cc..788cd86cce 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml index 4f21b53ff4..ab8566b77c 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import "quit.js" as Quit; diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml index f442a9c491..1de3078f19 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/test.js b/tests/auto/qml/debugger/qqmldebugjs/data/test.js index 7a28dafdd2..f631b1657b 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/test.js +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.js @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only function printMessage(msg) { diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/test.qml b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml index 687955dffc..bc84f7b3af 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml index 923acb72ad..a823a9a407 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index ac607df0a0..35a94aae78 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include "qqmldebugprocess_p.h" @@ -140,8 +140,8 @@ private slots: void letConstLocals(); private: - ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), - bool blockMode = true, bool restrictServices = false); + ConnectResult runAndConnect(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), + bool blockMode = true, bool restrictServices = false); QList<QQmlDebugClient *> createClients() override; QPointer<QV4DebugClient> m_client; @@ -163,7 +163,7 @@ void tst_QQmlDebugJS::initTestCase() QQmlDebugTest::initTestCase(); } #include <iostream> -QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile, +QQmlDebugTest::ConnectResult tst_QQmlDebugJS::runAndConnect(bool qmlscene, const QString &qmlFile, bool blockMode, bool restrictServices) { const QString executable = qmlscene @@ -195,7 +195,7 @@ void tst_QQmlDebugJS::connect() QFETCH(bool, restrictMode); QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess); m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(connected()))); } @@ -205,7 +205,7 @@ void tst_QQmlDebugJS::interrupt() //void connect() QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene), ConnectSuccess); m_client->connect(); m_client->interrupt(); @@ -217,7 +217,7 @@ void tst_QQmlDebugJS::getVersion() //void version() QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene), ConnectSuccess); m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(connected()))); @@ -231,7 +231,7 @@ void tst_QQmlDebugJS::getVersionWhenAttaching() //void version() QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); m_client->connect(); m_client->version(); @@ -244,7 +244,7 @@ void tst_QQmlDebugJS::disconnect() //void disconnect() QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene), ConnectSuccess); m_client->connect(); m_client->disconnect(); @@ -257,7 +257,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() QFETCH(bool, qmlscene); int sourceLine = 9; - QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -276,7 +276,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() QFETCH(bool, qmlscene); int sourceLine = 9; - QCOMPARE(init(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -294,7 +294,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() QFETCH(bool, qmlscene); int sourceLine = 10; - QCOMPARE(init(qmlscene, TIMER_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, TIMER_QMLFILE), ConnectSuccess); m_client->connect(); //We can set the breakpoint after connect() here because the timer is repeating and if we miss @@ -315,7 +315,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() QFETCH(bool, qmlscene); int sourceLine = 6; - QCOMPARE(init(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); m_client->connect(); @@ -335,7 +335,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComment() int sourceLine = 9; int actualLine = 11; - QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -356,7 +356,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() int sourceLine = 10; int actualLine = 11; - QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -376,7 +376,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() QFETCH(bool, qmlscene); int sourceLine = 14; - QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -395,7 +395,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() int out = 10; int sourceLine = 12; - QCOMPARE(init(qmlscene, CONDITION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, CONDITION_QMLFILE), ConnectSuccess); m_client->connect(); //The breakpoint is in a timer loop so we can set it after connect(). @@ -425,7 +425,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() { QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene, QUIT_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, QUIT_QMLFILE), ConnectSuccess); int sourceLine = 11; @@ -465,7 +465,7 @@ void tst_QQmlDebugJS::setBreakpointInJavaScript() QTRY_COMPARE(process.state(), QProcess::NotRunning); } - QCOMPARE(init(qmlscene, QUITINJS_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, QUITINJS_QMLFILE), ConnectSuccess); const int sourceLine = 2; @@ -488,7 +488,7 @@ void tst_QQmlDebugJS::setBreakpointInJavaScript() void tst_QQmlDebugJS::setBreakpointWhenAttaching() { int sourceLine = 10; - QCOMPARE(init(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); + QCOMPARE(runAndConnect(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); m_client->connect(); @@ -509,7 +509,7 @@ void tst_QQmlDebugJS::clearBreakpoint() int sourceLine1 = 12; int sourceLine2 = 13; - QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); m_client->connect(); //The breakpoints are in a timer loop so we can set them after connect(). @@ -556,7 +556,7 @@ void tst_QQmlDebugJS::changeBreakpoint() int sourceLine2 = 12; int sourceLine1 = 13; const QString file = QLatin1String(CHANGEBREAKPOINT_QMLFILE); - QCOMPARE(init(qmlscene, file), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, file), ConnectSuccess); bool isStopped = false; QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; }); @@ -629,7 +629,7 @@ void tst_QQmlDebugJS::setExceptionBreak() //void setExceptionBreak(QString type, bool enabled = false); QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess); m_client->setExceptionBreak(QV4DebugClient::All,true); m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); @@ -641,7 +641,7 @@ void tst_QQmlDebugJS::stepNext() QFETCH(bool, qmlscene); int sourceLine = 12; - QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -669,7 +669,7 @@ void tst_QQmlDebugJS::stepIn() int sourceLine = 16; int actualLine = 11; - QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); m_client->connect(); @@ -691,7 +691,7 @@ void tst_QQmlDebugJS::stepOut() int sourceLine = 12; int actualLine = 16; - QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -713,7 +713,7 @@ void tst_QQmlDebugJS::continueDebugging() int sourceLine1 = 16; int sourceLine2 = 13; - QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); @@ -736,7 +736,7 @@ void tst_QQmlDebugJS::backtrace() QFETCH(bool, qmlscene); int sourceLine = 9; - QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -752,7 +752,7 @@ void tst_QQmlDebugJS::getFrameDetails() QFETCH(bool, qmlscene); int sourceLine = 9; - QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -768,7 +768,7 @@ void tst_QQmlDebugJS::getScopeDetails() QFETCH(bool, qmlscene); int sourceLine = 9; - QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -781,7 +781,7 @@ void tst_QQmlDebugJS::getScopeDetails() void tst_QQmlDebugJS::evaluateInGlobalScope() { //void evaluate(QString expr, int frame = -1); - QCOMPARE(init(true), ConnectSuccess); + QCOMPARE(runAndConnect(true), ConnectSuccess); m_client->connect(); @@ -802,7 +802,7 @@ void tst_QQmlDebugJS::evaluateInLocalScope() QFETCH(bool, qmlscene); int sourceLine = 9; - QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); m_client->connect(); @@ -889,7 +889,7 @@ void tst_QQmlDebugJS::getScripts() //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); QFETCH(bool, qmlscene); - QCOMPARE(init(qmlscene), ConnectSuccess); + QCOMPARE(runAndConnect(qmlscene), ConnectSuccess); m_client->setBreakpoint(QString(TEST_QMLFILE), 10, -1, true); m_client->connect(); @@ -908,7 +908,7 @@ void tst_QQmlDebugJS::getScripts() void tst_QQmlDebugJS::encodeQmlScope() { QString file(ENCODEQMLSCOPE_QMLFILE); - QCOMPARE(init(true, file), ConnectSuccess); + QCOMPARE(runAndConnect(true, file), ConnectSuccess); int numFrames = 0; int numExpectedScopes = 0; @@ -968,7 +968,7 @@ void tst_QQmlDebugJS::encodeQmlScope() void tst_QQmlDebugJS::breakOnAnchor() { QString file(BREAKONANCHOR_QMLFILE); - QCOMPARE(init(true, file), ConnectSuccess); + QCOMPARE(runAndConnect(true, file), ConnectSuccess); int breaks = 0; bool stopped = false; @@ -1005,7 +1005,7 @@ void tst_QQmlDebugJS::breakOnAnchor() void tst_QQmlDebugJS::breakPointIds() { QString file(BREAKPOINTIDS_QMLFILE); - QCOMPARE(init(true, file), ConnectSuccess); + QCOMPARE(runAndConnect(true, file), ConnectSuccess); int breaks = 0; int breakPointIds[] = { -1, -1, -1, -1, -1, -1}; @@ -1038,7 +1038,7 @@ void tst_QQmlDebugJS::breakPointIds() void tst_QQmlDebugJS::letConstLocals() { QString file(LETCONSTLOCALS_QMLFILE); - QCOMPARE(init(true, file), ConnectSuccess); + QCOMPARE(runAndConnect(true, file), ConnectSuccess); QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { m_client->frame(); diff --git a/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp index b24e4c4445..4b3d7dc6fa 100644 --- a/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp +++ b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui/qguiapplication.h> #include <QtQml/qqmlengine.h> diff --git a/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt index 471336139d..03dcb24b3f 100644 --- a/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldebuglocal Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebuglocal LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmldebuglocal SOURCES ../shared/debugutil.cpp ../shared/debugutil_p.h diff --git a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp index 7449062332..bca70b7273 100644 --- a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp +++ b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qqmldebugtestservice.h" #include "debugutil_p.h" diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt index ea07333ec7..54fe36b7d5 100644 --- a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldebugprocess Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebugprocess LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmldebugprocess SOURCES ../../shared/qqmldebugprocess.cpp ../../shared/qqmldebugprocess_p.h diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp index 7b1faa1131..2a403fa361 100644 --- a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qqmldebugprocess_p.h> #include <QtTest> diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp index 595fe70e58..f275c2e836 100644 --- a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qdebug.h> #include <QtCore/qcoreapplication.h> diff --git a/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt index 33b7ba1c2f..2169cb8e0e 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldebugservice Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebugservice LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmldebugservice/data/test.qml b/tests/auto/qml/debugger/qqmldebugservice/data/test.qml index 687955dffc..bc84f7b3af 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebugservice/data/test.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp index 7dbff4279e..462caf930f 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp +++ b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp @@ -1,6 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qqmldebugtestservice.h" #include "debugutil_p.h" @@ -30,7 +29,7 @@ public: tst_QQmlDebugService(); private: - QQmlDebugConnection *m_conn; + std::unique_ptr<QQmlDebugConnection> m_conn; QQmlDebugTestService *m_service; private slots: @@ -60,20 +59,23 @@ void tst_QQmlDebugService::initTestCase() << QStringLiteral("tst_QQmlDebugService")); m_service = new QQmlDebugTestService("tst_QQmlDebugService", 2); - foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) + const QStringList debuggerServices = QQmlDebuggingEnabler::debuggerServices(); + for (const QString &service : debuggerServices) QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); - foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) + + const QStringList inspectorServices = QQmlDebuggingEnabler::inspectorServices(); + for (const QString &service : inspectorServices) QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); - foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) + + const QStringList profilerServices = QQmlDebuggingEnabler::profilerServices(); + for (const QString &service : profilerServices) QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT); QTest::ignoreMessage(QtDebugMsg, waitingMsg.toLatin1().constData()); QQmlDebuggingEnabler::startTcpDebugServer(PORT); - new QQmlEngine(this); - - m_conn = new QQmlDebugConnection(this); + m_conn = std::make_unique<QQmlDebugConnection>(this); for (int i = 0; i < 50; ++i) { // try for 5 seconds ... @@ -136,7 +138,7 @@ void tst_QQmlDebugService::state() QCOMPARE(m_service->state(), QQmlDebugService::Unavailable); { - QQmlDebugTestClient client("tst_QQmlDebugService", m_conn); + QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get()); QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled); QTRY_COMPARE(m_service->state(), QQmlDebugService::Enabled); } @@ -154,7 +156,7 @@ void tst_QQmlDebugService::state() void tst_QQmlDebugService::sendMessage() { - QQmlDebugTestClient client("tst_QQmlDebugService", m_conn); + QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get()); QByteArray msg = "hello!"; @@ -175,7 +177,7 @@ void tst_QQmlDebugService::sendMessage() void tst_QQmlDebugService::checkSupportForDataStreamVersion() { - QQmlDebugTestClient client("tst_QQmlDebugService", m_conn); + QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get()); QByteArray msg = "hello!"; @@ -192,22 +194,19 @@ void tst_QQmlDebugService::idForObject() { QCOMPARE(QQmlDebugService::idForObject(nullptr), -1); - QObject *objA = new QObject; + std::unique_ptr<QObject> objA = std::make_unique<QObject>(); - int idA = QQmlDebugService::idForObject(objA); + int idA = QQmlDebugService::idForObject(objA.get()); QVERIFY(idA >= 0); - QCOMPARE(QQmlDebugService::objectForId(idA), objA); + QCOMPARE(QQmlDebugService::objectForId(idA), objA.get()); - int idAA = QQmlDebugService::idForObject(objA); + int idAA = QQmlDebugService::idForObject(objA.get()); QCOMPARE(idAA, idA); - QObject *objB = new QObject; - int idB = QQmlDebugService::idForObject(objB); + std::unique_ptr<QObject> objB = std::make_unique<QObject>(); + int idB = QQmlDebugService::idForObject(objB.get()); QVERIFY(idB != idA); - QCOMPARE(QQmlDebugService::objectForId(idB), objB); - - delete objA; - delete objB; + QCOMPARE(QQmlDebugService::objectForId(idB), objB.get()); } void tst_QQmlDebugService::objectForId() @@ -215,19 +214,18 @@ void tst_QQmlDebugService::objectForId() QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(nullptr)); QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(nullptr)); - QObject *obj = new QObject; - int id = QQmlDebugService::idForObject(obj); - QCOMPARE(QQmlDebugService::objectForId(id), obj); + std::unique_ptr<QObject> obj = std::make_unique<QObject>(); + int id = QQmlDebugService::idForObject(obj.get()); + QCOMPARE(QQmlDebugService::objectForId(id), obj.get()); - delete obj; + obj.reset(); QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(nullptr)); } void tst_QQmlDebugService::checkSupportForOldDataStreamVersion() { //create a new connection; - delete m_conn; - m_conn = new QQmlDebugConnection(this); + m_conn = std::make_unique<QQmlDebugConnection>(this); m_conn->setMaximumDataStreamVersion(QDataStream::Qt_5_0); for (int i = 0; i < 50; ++i) { // try for 5 seconds ... @@ -238,7 +236,7 @@ void tst_QQmlDebugService::checkSupportForOldDataStreamVersion() } QVERIFY(m_conn->isConnected()); - QQmlDebugTestClient client("tst_QQmlDebugService", m_conn); + QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get()); QByteArray msg = "hello!"; diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt index fd44b5d32f..56c2e8d88a 100644 --- a/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qqmldebugtranslationclient Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebugtranslationclient LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml b/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml index 5b4784bbff..e90f9a5a80 100644 --- a/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp b/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp index 4ad9699cb9..068ab69d4c 100644 --- a/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp +++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only //QQmlDebugTest #include <debugutil_p.h> @@ -105,9 +105,7 @@ private: if (newCurrentOutputLine > m_currentOutputLine) { // lets wait a little bit more to not cut anything int triggeredCount = 0; - int debugCounter = 0; do { - debugCounter++; triggeredCount = m_process->output().size(); QTest::qWait(updateTimeOut); newCurrentOutputLine = m_process->output().size(); diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt index 7f971df399..5081f51557 100644 --- a/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qqmldebugtranslationservice Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldebugtranslationservice LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml b/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml index 1212fa100a..5d3957bffa 100644 --- a/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp index 0e0340b672..88d7bd39fd 100644 --- a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp +++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only //QQmlDebugTest #include <debugutil_p.h> diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt b/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt index 39435fe894..12c5b4ad1f 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlenginecontrol Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlenginecontrol LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp index 638389eb3c..bd01c69163 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp +++ b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include <QtQuickTestUtils/private/qmlutils_p.h> diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt index 8b4fc93594..bb36f678c3 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlenginedebuginspectorintegration Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlenginedebuginspectorintegration LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp index 890f4eeef6..7bbc8830b1 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../shared/debugutil_p.h" #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -24,7 +24,7 @@ public: tst_QQmlEngineDebugInspectorIntegration(); private: - ConnectResult init(bool restrictServices); + ConnectResult runAndConnect(bool restrictServices); QList<QQmlDebugClient *> createClients() override; QQmlEngineDebugObjectReference findRootObject(); @@ -68,7 +68,7 @@ tst_QQmlEngineDebugInspectorIntegration::tst_QQmlEngineDebugInspectorIntegration { } -QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::runAndConnect(bool restrictServices) { return QQmlDebugTest::connectTo( QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qml", @@ -96,15 +96,15 @@ void tst_QQmlEngineDebugInspectorIntegration::connect_data() void tst_QQmlEngineDebugInspectorIntegration::connect() { QFETCH(bool, restrictMode); - QCOMPARE(init(restrictMode), ConnectSuccess); + QCOMPARE(runAndConnect(restrictMode), ConnectSuccess); } void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() { - QCOMPARE(init(true), ConnectSuccess); + QCOMPARE(runAndConnect(true), ConnectSuccess); bool success = false; - QQmlEngineDebugObjectReference rootObject = findRootObject(); + const QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); int lineNumber = rootObject.source.lineNumber; @@ -114,7 +114,7 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))); - foreach (QQmlEngineDebugObjectReference child, rootObject.children) { + for (const QQmlEngineDebugObjectReference &child : rootObject.children) { success = false; lineNumber = child.source.lineNumber; columnNumber = child.source.columnNumber; @@ -127,12 +127,12 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() void tst_QQmlEngineDebugInspectorIntegration::select() { - QCOMPARE(init(true), ConnectSuccess); + QCOMPARE(runAndConnect(true), ConnectSuccess); - QQmlEngineDebugObjectReference rootObject = findRootObject(); + const QQmlEngineDebugObjectReference rootObject = findRootObject(); QList<int> childIds; int requestId = 0; - foreach (const QQmlEngineDebugObjectReference &child, rootObject.children) { + for (const QQmlEngineDebugObjectReference &child : rootObject.children) { requestId = m_inspectorClient->select(QList<int>() << child.debugId); QTRY_COMPARE(m_recipient->lastResponseId, requestId); QVERIFY(m_recipient->lastResult); @@ -145,7 +145,7 @@ void tst_QQmlEngineDebugInspectorIntegration::select() void tst_QQmlEngineDebugInspectorIntegration::createObject() { - QCOMPARE(init(true), ConnectSuccess); + QCOMPARE(runAndConnect(true), ConnectSuccess); QString qml = QLatin1String("Rectangle {\n" " id: xxxyxxx\n" @@ -172,7 +172,7 @@ void tst_QQmlEngineDebugInspectorIntegration::createObject() void tst_QQmlEngineDebugInspectorIntegration::moveObject() { - QCOMPARE(init(true), ConnectSuccess); + QCOMPARE(runAndConnect(true), ConnectSuccess); QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); QQmlEngineDebugObjectReference rootObject = findRootObject(); @@ -197,7 +197,7 @@ void tst_QQmlEngineDebugInspectorIntegration::moveObject() void tst_QQmlEngineDebugInspectorIntegration::destroyObject() { - QCOMPARE(init(true), ConnectSuccess); + QCOMPARE(runAndConnect(true), ConnectSuccess); QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); QQmlEngineDebugObjectReference rootObject = findRootObject(); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt index 4d4a939da5..d412368767 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlenginedebugservice Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlenginedebugservice LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml index 9e4546bdac..bda1673b18 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import Test 1.0 @@ -7,7 +7,7 @@ Item { id: root width: 10; height: 20; scale: blueRect.scale; Rectangle { id: blueRect; width: 500; height: 600; color: "blue"; } - Text { font.bold: true; color: blueRect.color; } + Text { id: blueText; font.bold: true; color: blueRect.color; } MouseArea { onEntered: { console.log('hello') } } diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml index a472988772..3203cc5bfc 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import Backend 1.0 CustomTypes { diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml index 0532b44726..994b2f82f1 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQuick.Controls 2.5 diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml index ebbf3f85e3..ded2b620ce 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 Item { diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml index ea0c3ff8c0..0a2e68f786 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml index 118b13f2d7..e107ed5259 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 Item { diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml index eb0c65b42f..27f6ffcc92 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import JsonTest 1.0 JsonTest { diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml index fee3f969b8..d8fe7f98c3 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 Rectangle { diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index e6e21cbb9e..a63c690cf0 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -22,6 +22,7 @@ #include <QtQml/qqmlproperty.h> #include <QtQml/qqmlincubator.h> #include <QtQml/qqmlapplicationengine.h> +#include <QtQml/private/qqmlsignalnames_p.h> #include <QtQuick/qquickitem.h> #include <QtNetwork/qhostaddress.h> @@ -244,7 +245,7 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o)); // signal properties are fake - they are generated from QQmlAbstractBoundSignal children - if (p.name.startsWith("on") && p.name.size() > 2 && p.name[2].isUpper()) { + if (QQmlSignalNames::isHandlerName(p.name)) { QString signal = p.value.toString(); QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name)); QVERIFY(expr && expr->expression() == signal); @@ -414,10 +415,10 @@ void tst_QQmlEngineDebugService::watch_property() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->addWatch(prop, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->addWatch(QQmlEngineDebugPropertyReference(), &success); QVERIFY(success); @@ -458,10 +459,10 @@ void tst_QQmlEngineDebugService::watch_object() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->addWatch(obj, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->addWatch(QQmlEngineDebugObjectReference(), &success); QVERIFY(success); @@ -525,10 +526,10 @@ void tst_QQmlEngineDebugService::watch_expression() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->addWatch(obj, expr, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->addWatch(QQmlEngineDebugObjectReference(), expr, &success); QVERIFY(success); @@ -600,10 +601,10 @@ void tst_QQmlEngineDebugService::queryAvailableEngines() { bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->queryAvailableEngines(&success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->queryAvailableEngines(&success); QVERIFY(success); @@ -628,10 +629,10 @@ void tst_QQmlEngineDebugService::queryRootContexts() QVERIFY(m_dbg->engines().size()); const QQmlEngineDebugEngineReference engine = m_dbg->engines()[0]; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->queryRootContexts(engine, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->queryRootContexts(engine, &success); QVERIFY(success); @@ -659,10 +660,10 @@ void tst_QQmlEngineDebugService::queryObject() QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); recursive ? m_dbg->queryObjectRecursive(rootObject, &success) : m_dbg->queryObject(rootObject, &success); QVERIFY(success); @@ -690,9 +691,9 @@ void tst_QQmlEngineDebugService::queryObject() QQmlEngineDebugObjectReference text; for (const QQmlEngineDebugObjectReference &child : obj.children) { QVERIFY(!child.className.isEmpty()); - if (child.className == "Rectangle") + if (child.idString == "blueRect") rect = child; - else if (child.className == "Text") + else if (child.idString == "blueText") text = child; } @@ -731,13 +732,13 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() int lineNumber = rootObject.source.lineNumber; int columnNumber = rootObject.source.columnNumber; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); recursive ? unconnected->queryObjectsForLocationRecursive(fileName, lineNumber, columnNumber, &success) : unconnected->queryObjectsForLocation(fileName, lineNumber, columnNumber, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); recursive ? m_dbg->queryObjectsForLocationRecursive(fileName, lineNumber, columnNumber, &success) @@ -769,9 +770,9 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QQmlEngineDebugObjectReference text; for (const QQmlEngineDebugObjectReference &child : obj.children) { QVERIFY(!child.className.isEmpty()); - if (child.className == "Rectangle") + if (child.idString == "blueRect") rect = child; - else if (child.className == "Text") + else if (child.idString == "blueText") text = child; } @@ -813,9 +814,9 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() "text: \"test\"\n" "}"); component.setData(content, rootObject.source.url); - QObject *object = component.create(context); + std::unique_ptr<QObject> object { component.create(context) }; QVERIFY(object); - int idNew = QQmlDebugService::idForObject(object); + int idNew = QQmlDebugService::idForObject(object.get()); QVERIFY(idNew >= 0); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); @@ -839,7 +840,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); } - delete object; + object.reset(); QObject *deleted = QQmlDebugService::objectForId(idNew); QVERIFY(!deleted); @@ -870,10 +871,10 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() QQmlEngineDebugObjectReference rootObject = findRootObject(4, true); QVERIFY(!rootObject.className.isEmpty()); - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->queryObject(rootObject, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->queryObject(rootObject, &success); QVERIFY(success); @@ -914,10 +915,10 @@ void tst_QQmlEngineDebugService::queryExpressionResult() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->queryExpressionResult(objectId, expr, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->queryExpressionResult(objectId, expr, &success); QVERIFY(success); @@ -967,10 +968,10 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr); unconnected->queryExpressionResultBC(objectId, expr, &success); QVERIFY(!success); - delete unconnected; + unconnected.reset(); m_dbg->queryExpressionResultBC(objectId, expr, &success); QVERIFY(success); diff --git a/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt b/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt index 7e6fb3559d..34a9b7a393 100644 --- a/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlinspector Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlinspector LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp index 897d8c8688..7f4e5eb73f 100644 --- a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp +++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../shared/debugutil_p.h" #include "../shared/qqmldebugprocess_p.h" diff --git a/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt b/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt index 705361c3be..f6d8988ec2 100644 --- a/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlnativeconnector Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlnativeconnector LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlnativeconnector SOURCES tst_qqmlnativeconnector.cpp diff --git a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp index 55a9aeb3f5..dd9789cdc5 100644 --- a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp +++ b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> diff --git a/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt b/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt index 3eaf14b35d..83d33a617f 100644 --- a/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlpreview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlpreview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "data/window.qml") list(APPEND test_data "data/qtquick2.qml") diff --git a/tests/auto/qml/debugger/qqmlpreview/data/broken.qml b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml index 75387c42aa..e8f82461a0 100644 --- a/tests/auto/qml/debugger/qqmlpreview/data/broken.qml +++ b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml index 4013dfc9ff..d49b94fc7e 100644 --- a/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml +++ b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window.qml b/tests/auto/qml/debugger/qqmlpreview/data/window.qml index 951b9a7f10..1141292a2c 100644 --- a/tests/auto/qml/debugger/qqmlpreview/data/window.qml +++ b/tests/auto/qml/debugger/qqmlpreview/data/window.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQuick.Window 2.0 diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window1.qml b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml index 4a24041dea..20c7ab04ea 100644 --- a/tests/auto/qml/debugger/qqmlpreview/data/window1.qml +++ b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQuick.Window 2.3 diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window2.qml b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml index 0de5dc36c8..d144cfb033 100644 --- a/tests/auto/qml/debugger/qqmlpreview/data/window2.qml +++ b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQuick.Window 2.3 diff --git a/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml index 86445a2fdc..18712a7e2d 100644 --- a/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml +++ b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp index 0c4fd568a9..5874100ebd 100644 --- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp +++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qqmldebugprocess_p.h> #include <debugutil_p.h> @@ -303,7 +303,7 @@ void tst_QQmlPreview::zoom() for (auto testZoomFactor : {2.0f, 1.5f, 0.5f}) { m_client->triggerZoom(testZoomFactor); - verifyZoomFactor(m_process, testZoomFactor); + verifyZoomFactor(m_process, testZoomFactor * baseZoomFactor); } m_client->triggerZoom(-1.0f); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt index 1e6c4c1753..d770f1fb27 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt +++ b/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlprofilerservice Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlprofilerservice LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml index 3a7c093fa5..6e16eaa7b1 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.0 diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml index 026911d1cc..788cd86cce 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 53971d3e8b..2088f958ae 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include "qqmldebugprocess_p.h" @@ -80,7 +80,7 @@ void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event) const QQmlProfilerEventType &type = types[typeIndex]; - QVERIFY(event.timestamp() >= lastTimestamp); + const qint64 oldTimestamp = lastTimestamp; lastTimestamp = event.timestamp(); switch (type.message()) { @@ -149,6 +149,8 @@ void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event) } break; } + + QCOMPARE_GE(lastTimestamp, oldTimestamp); } class tst_QQmlProfilerService : public QQmlDebugTest @@ -287,7 +289,7 @@ void tst_QQmlProfilerService::checkJsHeap() qint64 allocated = 0; qint64 used = 0; qint64 lastTimestamp = -1; - foreach (const QQmlProfilerEvent &message, m_client->jsHeapMessages) { + for (const QQmlProfilerEvent &message : std::as_const(m_client->jsHeapMessages)) { const auto amount = message.number<qint64>(0); const QQmlProfilerEventType &type = m_client->types.at(message.typeIndex()); switch (type.detailType()) { @@ -440,7 +442,7 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty return true; } while (++position < target->size() && target->at(position).timestamp() == timestamp); - foreach (const QString &message, warnings) + for (const QString &message : std::as_const(warnings)) qWarning() << message.toLocal8Bit().constData(); return false; @@ -580,7 +582,7 @@ void tst_QQmlProfilerService::scenegraphData() QCOMPARE(connectTo(true, "scenegraphTest.qml"), ConnectSuccess); while (!m_process->output().contains(QLatin1String("tick"))) - QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); + QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()), 30000)); m_client->client->setRecording(false); checkTraceReceived(); @@ -594,7 +596,7 @@ void tst_QQmlProfilerService::scenegraphData() // interleaved. Also, events could carry the same time stamps and be sorted in an unexpected way // if the clocks are acting up. qint64 renderFrameTime = -1; - foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + for (const QQmlProfilerEvent &msg : std::as_const(m_client->asynchronousMessages)) { const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); if (type.detailType() == SceneGraphRendererFrame) { renderFrameTime = msg.timestamp(); diff --git a/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt b/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt index 0b60a0092b..42f6cf931a 100644 --- a/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt +++ b/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qv4debugger Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4debugger LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -17,11 +23,12 @@ qt_add_library(testCppTypes STATIC) qt_autogen_tools_initial_setup(testCppTypes) target_link_libraries(testCppTypes PRIVATE Qt::Qml Qt::QmlPrivate Qt::Quick) +qt_policy(SET QTP0001 NEW) + qt6_add_qml_module(testCppTypes URI TestTypes SOURCES commontypes.h - AUTO_RESOURCE_PREFIX OUTPUT_DIRECTORY TestTypes ) qt_autogen_tools_initial_setup(testCppTypesplugin) diff --git a/tests/auto/qml/debugger/qv4debugger/commontypes.h b/tests/auto/qml/debugger/qv4debugger/commontypes.h index 01b2125ae3..b63059b0e6 100644 --- a/tests/auto/qml/debugger/qv4debugger/commontypes.h +++ b/tests/auto/qml/debugger/qv4debugger/commontypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef COMMONTYPES_H #define COMMONTYPES_H @@ -14,7 +14,7 @@ class MyType : public QQuickItem QML_ELEMENT public: MyType(QQuickItem *parent = nullptr) : QQuickItem(parent) {} - Q_INVOKABLE void name(QQmlV4Function*) const {} + Q_INVOKABLE void name(QQmlV4FunctionPtr) const {} }; #endif // COMMONTYPES_H diff --git a/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml b/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml new file mode 100644 index 0000000000..2582a23ec5 --- /dev/null +++ b/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml @@ -0,0 +1,4 @@ +import QtQml 2.15 +import "module1.js" as Module1 + +QtObject {} diff --git a/tests/auto/qml/debugger/qv4debugger/data/module1.js b/tests/auto/qml/debugger/qv4debugger/data/module1.js new file mode 100644 index 0000000000..9ce1f1e6b1 --- /dev/null +++ b/tests/auto/qml/debugger/qv4debugger/data/module1.js @@ -0,0 +1,5 @@ +.pragma library + +.import "module2.mjs" as Module2 + +Module2.crashMe(); diff --git a/tests/auto/qml/debugger/qv4debugger/data/module2.mjs b/tests/auto/qml/debugger/qv4debugger/data/module2.mjs new file mode 100644 index 0000000000..80f82af953 --- /dev/null +++ b/tests/auto/qml/debugger/qv4debugger/data/module2.mjs @@ -0,0 +1,7 @@ +import * as Module3 from "module3.mjs" +import * as Module4 from "module4.mjs" + +export function crashMe() +{ + console.log("Hello world!"); +} diff --git a/tests/auto/qml/debugger/qv4debugger/data/module3.mjs b/tests/auto/qml/debugger/qv4debugger/data/module3.mjs new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/debugger/qv4debugger/data/module3.mjs diff --git a/tests/auto/qml/debugger/qv4debugger/data/module4.mjs b/tests/auto/qml/debugger/qv4debugger/data/module4.mjs new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/debugger/qv4debugger/data/module4.mjs diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 6f147446f0..3da401b5bb 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtTest/QtTest> @@ -167,7 +168,7 @@ public slots: m_thrownValue = job.exceptionValue(); } - foreach (const TestBreakPoint &bp, m_breakPointsToAddWhenPaused) + for (const TestBreakPoint &bp : std::as_const(m_breakPointsToAddWhenPaused)) debugger->addBreakPoint(bp.fileName, bp.lineNumber); m_breakPointsToAddWhenPaused.clear(); @@ -216,10 +217,14 @@ public: QJsonArray scopes = frameObj.value(QLatin1String("scopes")).toArray(); int nscopes = scopes.size(); int s = 0; - for (s = 0; s < nscopes; ++s) { - QJsonObject o = scopes.at(s).toObject(); - if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext - break; + if (m_targetScope != -1) { + s = m_targetScope; + } else { + for (s = 0; s < nscopes; ++s) { + QJsonObject o = scopes.at(s).toObject(); + if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext + break; + } } if (s == nscopes) return; @@ -249,6 +254,7 @@ public: bool m_wasPaused; QV4Debugger::PauseReason m_pauseReason; bool m_captureContextInfo; + int m_targetScope = -1; QList<QV4Debugger::ExecutionState> m_statesWhenPaused; QList<TestBreakPoint> m_breakPointsToAddWhenPaused; QVector<QV4::StackFrame> m_stackTrace; @@ -274,9 +280,10 @@ public: void dumpStackTrace() const { qDebug() << "Stack depth:" << m_stackTrace.size(); - foreach (const QV4::StackFrame &frame, m_stackTrace) + for (const QV4::StackFrame &frame : m_stackTrace) { qDebug("\t%s (%s:%d:%d)", qPrintable(frame.function), qPrintable(frame.source), - frame.line, frame.column); + qAbs(frame.line), frame.column); + } } }; @@ -322,6 +329,9 @@ private slots: void readThis(); void signalParameters(); void debuggerNoCrash(); + + void breakPointInJSModule(); + private: QV4Debugger *debugger() const { @@ -329,27 +339,27 @@ private: } void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1) { - QMetaObject::invokeMethod(m_engine, "evaluate", Qt::QueuedConnection, + QMetaObject::invokeMethod(m_engine.get(), "evaluate", Qt::QueuedConnection, Q_ARG(QString, script), Q_ARG(QString, fileName), Q_ARG(int, lineNumber)); - waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0); + waitForSignal(m_engine.get(), SIGNAL(evaluateFinished()), /*timeout*/0); } - TestEngine *m_engine; + std::unique_ptr<TestEngine> m_engine; QV4::ExecutionEngine *m_v4; - TestAgent *m_debuggerAgent; - QThread *m_javaScriptThread; + std::unique_ptr<TestAgent> m_debuggerAgent; + std::unique_ptr<QThread> m_javaScriptThread; }; void tst_qv4debugger::init() { - m_javaScriptThread = new QThread; - m_engine = new TestEngine; + m_javaScriptThread = std::make_unique<QThread>(); + m_engine = std::make_unique<TestEngine>(); m_v4 = m_engine->v4Engine(); m_v4->setDebugger(new QV4Debugger(m_v4)); - m_engine->moveToThread(m_javaScriptThread); + m_engine->moveToThread(m_javaScriptThread.get()); m_javaScriptThread->start(); - m_debuggerAgent = new TestAgent(m_v4); + m_debuggerAgent = std::make_unique<TestAgent>(m_v4); m_debuggerAgent->addDebugger(debugger()); } @@ -357,11 +367,11 @@ void tst_qv4debugger::cleanup() { m_javaScriptThread->exit(); m_javaScriptThread->wait(); - delete m_engine; - delete m_javaScriptThread; + m_engine.reset(); + m_javaScriptThread.reset(); m_engine = nullptr; m_v4 = nullptr; - delete m_debuggerAgent; + m_debuggerAgent.reset(); m_debuggerAgent = nullptr; } @@ -453,7 +463,7 @@ void tst_qv4debugger::removeBreakPointForNextInstruction() "someCall();\n" "var i = 42;"; - QMetaObject::invokeMethod(m_engine, "injectFunction", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(m_engine.get(), "injectFunction", Qt::BlockingQueuedConnection, Q_ARG(QString, "someCall"), Q_ARG(InjectedFunction, someCall)); debugger()->addBreakPoint("removeBreakPointForNextInstruction", 2); @@ -967,6 +977,35 @@ void tst_qv4debugger::debuggerNoCrash() debugThread->wait(); } +void tst_qv4debugger::breakPointInJSModule() +{ + QQmlEngine engine; + QV4::ExecutionEngine *v4 = engine.handle(); + QPointer<QV4Debugger> v4Debugger = new QV4Debugger(v4); + v4->setDebugger(v4Debugger.data()); + + QScopedPointer<QThread> debugThread(new QThread); + debugThread->start(); + QScopedPointer<TestAgent> debuggerAgent(new TestAgent(v4)); + debuggerAgent->addDebugger(v4Debugger); + debuggerAgent->moveToThread(debugThread.data()); + + QQmlComponent component(&engine, testFileUrl("breakPointInJSModule.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + + debuggerAgent->m_captureContextInfo = true; + debuggerAgent->m_targetScope = 1; + v4Debugger->addBreakPoint("module2.mjs", 6); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QVERIFY(!debuggerAgent->m_capturedScope.isEmpty()); + + debugThread->quit(); + debugThread->wait(); +} + tst_qv4debugger::tst_qv4debugger() : QQmlDataTest(QT_QMLTEST_DATADIR) { } QTEST_MAIN(tst_qv4debugger) diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp index bce2c28378..28d40d768d 100644 --- a/tests/auto/qml/debugger/shared/debugutil.cpp +++ b/tests/auto/qml/debugger/shared/debugutil.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "debugutil_p.h" #include "qqmldebugprocess_p.h" diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h index 0303aa2da9..188cc14c32 100644 --- a/tests/auto/qml/debugger/shared/debugutil_p.h +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DEBUGUTIL_P_H #define DEBUGUTIL_P_H diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp index 4944ba2e15..97477370c8 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp +++ b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qqmldebugprocess_p.h" @@ -56,7 +56,7 @@ QString QQmlDebugProcess::stateString() const void QQmlDebugProcess::start(const QStringList &arguments) { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // make sure m_executable points to the actual binary even if it's inside an app bundle QFileInfo binFile(m_executable); if (!binFile.isExecutable()) { diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h index 56ed9b9830..94554928a5 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h +++ b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QQMLDEBUGPROCESS_P_H #define QQMLDEBUGPROCESS_P_H diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp index daafa8c26e..4884c9d400 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp +++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qqmldebugtestservice.h" #include <QThread> diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h index 150ded9b1b..a2a3ce6161 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h +++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QQMLDEBUGTESTSERVICE_H #define QQMLDEBUGTESTSERVICE_H diff --git a/tests/auto/qml/ecmascripttests/CMakeLists.txt b/tests/auto/qml/ecmascripttests/CMakeLists.txt index d3da3adb53..1ee70cb101 100644 --- a/tests/auto/qml/ecmascripttests/CMakeLists.txt +++ b/tests/auto/qml/ecmascripttests/CMakeLists.txt @@ -5,15 +5,22 @@ ## tst_ecmascripttests Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_ecmascripttests LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test262/*) list(APPEND test_data ${test_data_glob}) +list(FILTER test_data EXCLUDE REGEX ".git") qt_internal_add_test(tst_ecmascripttests SOURCES - qjstest/test262runner.cpp qjstest/test262runner.h + test262runner.cpp test262runner.h tst_ecmascripttests.cpp LIBRARIES Qt::QmlPrivate @@ -40,7 +47,3 @@ else() QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/test262" ) endif() - -if(NOT CMAKE_CROSSCOMPILING) - add_subdirectory(qjstest) -endif() diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 75fdd1cb0c..cc5eae456d 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -18,6 +18,12 @@ language/statements/labeled/let-identifier-with-newline.js sloppyFails language/statements/while/let-identifier-with-newline.js sloppyFails language/statements/with/let-identifier-with-newline.js sloppyFails +# These failures are a defect in the Yarr regexp engine we are using. +# They all amount to some variation of: /\udf06/u.exec('\ud834\udf06') +built-ins/RegExp/prototype/Symbol.match/builtin-infer-unicode.js +built-ins/RegExp/prototype/Symbol.search/u-lastindex-advance.js +built-ins/RegExp/prototype/exec/u-lastindex-adv.js + # The ES6/7 spec says that [[DefineOwnProperty]] on the module namespace exotic object # always returns false. This was changed in https://github.com/tc39/ecma262/pull/858 # but it's not in the published spec yet. @@ -92,7 +98,6 @@ built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js built-ins/Array/prototype/slice/length-exceeding-integer-limit.js fails built-ins/Array/prototype/some/15.4.4.17-3-28.js fails built-ins/Array/prototype/some/15.4.4.17-3-29.js fails -built-ins/Array/prototype/sort/comparefn-nonfunction-call-throws.js fails built-ins/Array/prototype/splice/S15.4.4.12_A3_T1.js fails built-ins/Array/prototype/splice/clamps-length-to-integer-limit.js fails built-ins/Array/prototype/splice/create-ctor-non-object.js fails @@ -211,7 +216,6 @@ built-ins/Promise/prototype/then/ctor-throws.js fails built-ins/Promise/race/ctx-ctor.js fails built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails -built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails @@ -249,8 +253,6 @@ built-ins/String/prototype/toLocaleLowerCase/special_casing_conditional.js fails built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js fails built-ins/String/prototype/toLowerCase/special_casing_conditional.js fails built-ins/TypedArray/prototype/constructor.js fails -built-ins/TypedArray/prototype/fill/fill-values-conversion-operations-consistent-nan.js fails -built-ins/TypedArray/prototype/slice/bit-precision.js fails built-ins/TypedArray/prototype/sort/arraylength-internal.js fails built-ins/TypedArray/prototype/sort/comparefn-call-throws.js fails built-ins/TypedArray/prototype/sort/comparefn-calls.js fails diff --git a/tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt b/tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt deleted file mode 100644 index 86ca5f97a3..0000000000 --- a/tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause - -# Generated from qjstest.pro. - -##################################################################### -## qjstest Tool: -##################################################################### - -qt_get_tool_target_name(target_name qjstest) -qt_internal_add_tool(${target_name} - TARGET_DESCRIPTION "Javascript test runner" - SOURCES - main.cpp - test262runner.cpp test262runner.h - DEFINES - QT_DEPRECATED_WARNINGS - INCLUDE_DIRECTORIES - . - LIBRARIES - Qt::Gui - Qt::QmlPrivate -) -qt_internal_return_unless_building_tools() - -#### Keys ignored in scope 1:.:.:qjstest.pro:<TRUE>: -# QMAKE_TARGET_DESCRIPTION = "Javascript" "test" "runner" -# TEMPLATE = "app" diff --git a/tests/auto/qml/ecmascripttests/qjstest/main.cpp b/tests/auto/qml/ecmascripttests/qjstest/main.cpp deleted file mode 100644 index 7bffedae81..0000000000 --- a/tests/auto/qml/ecmascripttests/qjstest/main.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <QJSEngine> -#include <QCoreApplication> -#include <QCommandLineParser> -#include <qdebug.h> -#include <stdlib.h> - -#include "test262runner.h" - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - - - QCommandLineParser parser; - parser.addHelpOption(); - parser.addVersionOption(); - QCommandLineOption verbose("verbose", "Verbose output"); - parser.addOption(verbose); - QCommandLineOption commandOption("command", "Javascript command line interpreter", "command"); - parser.addOption(commandOption); - QCommandLineOption testDir("tests", "path to the tests", "tests", "test262"); - parser.addOption(testDir); - QCommandLineOption cat("cat", "Print packaged test code that would be run"); - parser.addOption(cat); - QCommandLineOption parallel("parallel", "Run tests in parallel"); - parser.addOption(parallel); - QCommandLineOption jit("jit", "JIT all code"); - parser.addOption(jit); - QCommandLineOption bytecode("interpret", "Run using the bytecode interpreter"); - parser.addOption(bytecode); - QCommandLineOption withExpectations("with-test-expectations", "Parse TestExpectations to deal with known failures"); - parser.addOption(withExpectations); - QCommandLineOption updateExpectations("update-expectations", "Update TestExpectations to remove unexepected passes"); - parser.addOption(updateExpectations); - QCommandLineOption writeExpectations("write-expectations", "Generate a new TestExpectations file based on the results of the run"); - parser.addOption(writeExpectations); - parser.addPositionalArgument("[filter]", "Only run tests that contain filter in their name"); - - parser.process(app); - - Test262Runner testRunner(parser.value(commandOption), parser.value(testDir), QStringLiteral("TestExpectations")); - - QStringList otherArgs = parser.positionalArguments(); - if (otherArgs.size() > 1) { - qWarning() << "too many arguments"; - return 1; - } else if (otherArgs.size()) { - testRunner.setFilter(otherArgs.at(0)); - } - - if (parser.isSet(cat)) { - testRunner.cat(); - return 0; - } - - if (parser.isSet(updateExpectations) && parser.isSet(writeExpectations)) { - qWarning() << "Can only specify one of --update-expectations and --write-expectations."; - exit(1); - } - - if (parser.isSet(jit) && parser.isSet(bytecode)) { - qWarning() << "Can only specify one of --jit and --interpret."; - exit(1); - } - - int flags = 0; - if (parser.isSet(verbose)) - - flags |= Test262Runner::Verbose; - if (parser.isSet(parallel)) - flags |= Test262Runner::Parallel; - if (parser.isSet(jit)) - flags |= Test262Runner::ForceJIT; - if (parser.isSet(bytecode)) - flags |= Test262Runner::ForceBytecode; - if (parser.isSet(withExpectations)) - flags |= Test262Runner::WithTestExpectations; - if (parser.isSet(updateExpectations)) - flags |= Test262Runner::UpdateTestExpectations; - if (parser.isSet(writeExpectations)) - flags |= Test262Runner::WriteTestExpectations; - testRunner.setFlags(flags); - - if (testRunner.run()) - return EXIT_SUCCESS; - else - return EXIT_FAILURE; -} diff --git a/tests/auto/qml/ecmascripttests/test262.py b/tests/auto/qml/ecmascripttests/test262.py deleted file mode 100755 index 01c990950c..0000000000 --- a/tests/auto/qml/ecmascripttests/test262.py +++ /dev/null @@ -1,611 +0,0 @@ -#!/usr/bin/env python -# Copyright (C) 2017 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -# Copyright 2009 the Sputnik authors. All rights reserved. -# This code is governed by the BSD license found in the LICENSE file. - -# This is derived from sputnik.py, the Sputnik console test runner, -# with elements from packager.py, which is separately -# copyrighted. TODO: Refactor so there is less duplication between -# test262.py and packager.py. - -import sys -from os import path -rootDir = path.dirname(path.realpath(__file__)) -sys.path.insert(0, path.abspath(rootDir + "/test262/tools/packaging")) - -import logging -import optparse -import os -import platform -import re -import subprocess -import tempfile -import time -import xml.dom.minidom -import datetime -import shutil -import json -import stat -import multiprocessing -import signal - - -from parseTestRecord import parseTestRecord, stripHeader - -from packagerConfig import * - -# excluded features that are still experimental and not part of any official standard -# see also the features.txt file in test262/ -excludedFeatures = [ - "BigInt", - "class-fields-public", - "class-fields-private", - "Promise.prototype.finally", - "async-iteration", - "Symbol.asyncIterator", - "object-rest", - "object-spread", - "optional-catch-binding", - "regexp-dotall", - "regexp-lookbehind", - "regexp-named-groups", - "regexp-unicode-property-escapes", - "Atomics", - "SharedArrayBuffer", - "Array.prototype.flatten", - "Array.prototype.flatMap", - "string-trimming", - "String.prototype.trimEnd", - "String.prototype.trimStart", - "numeric-separator-literal", - - # optional features, not supported by us - "caller" -] - -# ############# Helpers needed for parallel multi-process test execution ############ - -def runTest(case, args): - return case.Run(args) - -def runTestVarArgs(args): - return runTest(*args) - -def initWorkerProcess(): - signal.signal(signal.SIGINT, signal.SIG_IGN) - -# ############# - -class Test262Error(Exception): - def __init__(self, message): - self.message = message - -def ReportError(s): - raise Test262Error(s) - - -class TestExpectations: - def __init__(self, enabled): - self.testsToSkip = [] - self.failingTests = [] - f = open(rootDir + "/TestExpectations") - if not enabled: - return - for line in f.read().splitlines(): - line = line.strip() - if len(line) == 0 or line[0] == "#": - continue - record = line.split() - if len(record) == 1: - self.failingTests.append(record[0]) - else: - test = record[0] - expectation = record[1] - if expectation == "skip": - self.testsToSkip.append(test) - f.close() - - def update(self, progress): - unexpectedPasses = [c.case.name for c in progress.failed_tests if c.case.IsNegative()] - - # If a test fails that we expected to fail, then it actually passed unexpectedly. - failures = [c.case.name for c in progress.failed_tests if not c.case.IsNegative()] - for failure in failures: - if failure in self.failingTests: - unexpectedPasses.append(failure) - - f = open(rootDir + "/TestExpectations") - lines = f.read().splitlines() - oldLen = len(lines) - for result in unexpectedPasses: - expectationLine = result - try: - lines.remove(expectationLine) - except ValueError: - pass - - f.close() - if len(lines) != oldLen: - f = open(rootDir + "/TestExpectations", "w") - f.write("\n".join(lines)) - f.close() - print "Changes to TestExpectations written!" - - -if not os.path.exists(EXCLUDED_FILENAME): - print "Cannot generate (JSON) test262 tests without a file," + \ - " %s, showing which tests have been disabled!" % EXCLUDED_FILENAME - sys.exit(1) -EXCLUDE_LIST = xml.dom.minidom.parse(EXCLUDED_FILENAME) -EXCLUDE_LIST = EXCLUDE_LIST.getElementsByTagName("test") -EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST] - - -def BuildOptions(): - result = optparse.OptionParser() - result.add_option("--command", default="qmljs", help="The command-line to run") - result.add_option("--tests", default=path.abspath(rootDir + '/test262'), - help="Path to the tests") - result.add_option("--cat", default=False, action="store_true", - help="Print packaged test code that would be run") - result.add_option("--summary", default=True, action="store_true", - help="Print summary after running tests") - result.add_option("--full-summary", default=False, action="store_true", - help="Print summary and test output after running tests") - result.add_option("--strict_only", default=False, action="store_true", - help="Test only strict mode") - result.add_option("--non_strict_only", default=False, action="store_true", - help="Test only non-strict mode") - result.add_option("--parallel", default=False, action="store_true", - help="Run tests in parallel") - result.add_option("--with-test-expectations", default=False, action="store_true", - help="Parse TestExpectations to deal with tests known to fail") - result.add_option("--update-expectations", default=False, action="store_true", - help="Update test expectations fail when a test passes that was expected to fail") - # TODO: Once enough tests are made strict compat, change the default - # to "both" - result.add_option("--unmarked_default", default="non_strict", - help="default mode for tests of unspecified strictness") - return result - - -def ValidateOptions(options): - if not options.command: - ReportError("A --command must be specified.") - if not path.exists(options.tests): - ReportError("Couldn't find test path '%s'" % options.tests) - - -placeHolderPattern = re.compile(r"\{\{(\w+)\}\}") - - -def IsWindows(): - p = platform.system() - return (p == 'Windows') or (p == 'Microsoft') - - -class TempFile(object): - - def __init__(self, suffix="", prefix="tmp", text=False): - self.suffix = suffix - self.prefix = prefix - self.text = text - self.fd = None - self.name = None - self.is_closed = False - self.Open() - - def Open(self): - (self.fd, self.name) = tempfile.mkstemp( - suffix = self.suffix, - prefix = self.prefix, - text = self.text) - - def Write(self, str): - os.write(self.fd, str) - - def Read(self): - f = file(self.name) - result = f.read() - f.close() - return result - - def Close(self): - if not self.is_closed: - self.is_closed = True - os.close(self.fd) - - def Dispose(self): - try: - self.Close() - os.unlink(self.name) - except OSError, e: - logging.error("Error disposing temp file: %s", str(e)) - - -class TestResult(object): - - def __init__(self, exit_code, stdout, stderr, case): - self.exit_code = exit_code - self.stdout = stdout - self.stderr = stderr - self.case = case - - def ReportOutcome(self, long_format): - name = self.case.GetName() - mode = self.case.GetMode() - if self.HasUnexpectedOutcome(): - if self.case.IsNegative(): - print "=== %s was expected to fail in %s, but didn't ===" % (name, mode) - else: - if long_format: - print "=== %s failed in %s ===" % (name, mode) - else: - print "%s in %s: " % (name, mode) - out = self.stdout.strip() - if len(out) > 0: - print "--- output ---" - print out - err = self.stderr.strip() - if len(err) > 0: - print "--- errors ---" - print err - if long_format: - print "===" - elif self.case.IsNegative(): - print "%s failed in %s as expected" % (name, mode) - else: - print "%s passed in %s" % (name, mode) - - def HasFailed(self): - return self.exit_code != 0 - - def HasUnexpectedOutcome(self): - if self.case.IsNegative(): - return not self.HasFailed() - else: - return self.HasFailed() - - -class TestCase(object): - - def __init__(self, suite, name, full_path, strict_mode): - self.suite = suite - self.name = name - self.full_path = full_path - self.strict_mode = strict_mode - f = open(self.full_path) - self.contents = f.read() - f.close() - testRecord = parseTestRecord(self.contents, name) - self.test = testRecord["test"] - if 'features' in testRecord: - self.features = testRecord["features"]; - else: - self.features = [] - del testRecord["test"] - del testRecord["header"] - self.testRecord = testRecord; - - - def GetName(self): - return self.name - - def GetMode(self): - if self.strict_mode: - return "strict mode" - else: - return "non-strict mode" - - def GetPath(self): - return self.name - - def NegateResult(self): - if self.IsNegative(): - del self.testRecord['negative'] - else: - self.testRecord['negative'] = "Some failure"; - - def IsNegative(self): - return 'negative' in self.testRecord - - def IsOnlyStrict(self): - return 'onlyStrict' in self.testRecord - - def IsNoStrict(self): - return 'noStrict' in self.testRecord - - def IsExperimental(self): - for f in self.features: - if excludedFeatures.count(f) >= 1: - return True; - return False - - def GetSource(self): - # "var testDescrip = " + str(self.testRecord) + ';\n\n' + \ - source = self.suite.GetInclude("assert.js") + \ - self.suite.GetInclude("sta.js") + \ - self.test + '\n' - if 'includes' in self.testRecord: - for inc in self.testRecord['includes']: - source += self.suite.GetInclude(inc); - - if self.strict_mode: - source = '"use strict";\nvar strict_mode = true;\n' + source - else: - source = "var strict_mode = false; \n" + source - return source - - def InstantiateTemplate(self, template, params): - def GetParameter(match): - key = match.group(1) - return params.get(key, match.group(0)) - return placeHolderPattern.sub(GetParameter, template) - - def Execute(self, command): - if IsWindows(): - args = '%s' % command - else: - args = command.split(" ") - stdout = TempFile(prefix="test262-out-") - stderr = TempFile(prefix="test262-err-") - try: - logging.info("exec: %s", str(args)) - process = subprocess.Popen( - args, - shell = IsWindows(), - stdout = stdout.fd, - stderr = stderr.fd - ) - code = process.wait() - out = stdout.Read() - err = stderr.Read() - finally: - stdout.Dispose() - stderr.Dispose() - return (code, out, err) - - def RunTestIn(self, command_template, tmp): - tmp.Write(self.GetSource()) - tmp.Close() - command = self.InstantiateTemplate(command_template, { - 'path': tmp.name - }) - (code, out, err) = self.Execute(command) - return TestResult(code, out, err, self) - - def Run(self, command_template): - tmp = TempFile(suffix=".js", prefix="test262-", text=True) - try: - result = self.RunTestIn(command_template, tmp) - finally: - tmp.Dispose() - return result - - def Print(self): - print self.GetSource() - - -class ProgressIndicator(object): - - def __init__(self, count): - self.count = count - self.succeeded = 0 - self.failed = 0 - self.failed_tests = [] - - def HasRun(self, result): - result.ReportOutcome(True) - if result.HasUnexpectedOutcome(): - self.failed += 1 - self.failed_tests.append(result) - else: - self.succeeded += 1 - - -def MakePlural(n): - if (n == 1): - return (n, "") - else: - return (n, "s") - - -class TestSuite(object): - - def __init__(self, root, strict_only, non_strict_only, unmarked_default, load_expectations): - # TODO: derive from packagerConfig.py - self.test_root = path.join(root, 'test') - self.lib_root = path.join(root, 'harness') - self.strict_only = strict_only - self.non_strict_only = non_strict_only - self.unmarked_default = unmarked_default - self.include_cache = { } - self.expectations = TestExpectations(load_expectations) - - def IsExcludedTest(self, path): - if path.startswith('annexB'): - return True; - if path.startswith('harness'): - return True; - if path.startswith('intl402'): - return True; - return False; - - def Validate(self): - if not path.exists(self.test_root): - ReportError("No test repository found") - if not path.exists(self.lib_root): - ReportError("No test library found") - - def IsHidden(self, path): - return path.startswith('.') or path == 'CVS' - - def IsTestCase(self, path): - return path.endswith('.js') - - def ShouldRun(self, rel_path, tests): - if len(tests) == 0: - return True - for test in tests: - if test in rel_path: - return True - return False - - def GetInclude(self, name): - if not name in self.include_cache: - static = path.join(self.lib_root, name) - if path.exists(static): - f = open(static) - contents = stripHeader(f.read()) - contents = re.sub(r'\r\n', '\n', contents) - self.include_cache[name] = contents + "\n" - f.close() - else: - ReportError("Can't find: " + static) - return self.include_cache[name] - - def EnumerateTests(self, tests): - logging.info("Listing tests in %s", self.test_root) - cases = [] - for root, dirs, files in os.walk(self.test_root): - for f in [x for x in dirs if self.IsHidden(x)]: - dirs.remove(f) - dirs.sort() - for f in sorted(files): - if self.IsTestCase(f): - full_path = path.join(root, f) - if full_path.startswith(self.test_root): - rel_path = full_path[len(self.test_root)+1:] - else: - logging.warning("Unexpected path %s", full_path) - rel_path = full_path - if self.ShouldRun(rel_path, tests) and not self.IsExcludedTest(rel_path): - basename = path.basename(full_path)[:-3] - name = rel_path.replace('.js', '') - if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(name) >= 1: - print 'Excluded: ' + rel_path - else: - if not self.non_strict_only: - strict_case = TestCase(self, name, full_path, True) - if self.expectations.failingTests.count(name) >= 1: - strict_case.NegateResult() - if not strict_case.IsNoStrict() and not strict_case.IsExperimental(): - if strict_case.IsOnlyStrict() or \ - self.unmarked_default in ['both', 'strict']: - cases.append(strict_case) - if not self.strict_only: - non_strict_case = TestCase(self, name, full_path, False) - if self.expectations.failingTests.count(name) >= 1: - non_strict_case.NegateResult() - if not non_strict_case.IsOnlyStrict() and not non_strict_case.IsExperimental(): - if non_strict_case.IsNoStrict() or \ - self.unmarked_default in ['both', 'non_strict']: - cases.append(non_strict_case) - logging.info("Done listing tests") - return cases - - def PrintSummary(self, progress): - print - print "=== Summary ===" - count = progress.count - succeeded = progress.succeeded - failed = progress.failed - print " - Ran %i test%s" % MakePlural(count) - if progress.failed == 0: - print " - All tests succeeded" - else: - percent = ((100.0 * succeeded) / count,) - print " - Passed %i test%s (%.1f%%)" % (MakePlural(succeeded) + percent) - percent = ((100.0 * failed) / count,) - print " - Failed %i test%s (%.1f%%)" % (MakePlural(failed) + percent) - positive = [c for c in progress.failed_tests if not c.case.IsNegative()] - negative = [c for c in progress.failed_tests if c.case.IsNegative()] - if len(positive) > 0: - print - print "Failed tests" - for result in positive: - print " %s in %s" % (result.case.GetName(), result.case.GetMode()) - if len(negative) > 0: - print - print "Expected to fail but passed ---" - for result in negative: - print " %s in %s" % (result.case.GetName(), result.case.GetMode()) - - def PrintFailureOutput(self, progress): - for result in progress.failed_tests: - print - result.ReportOutcome(False) - - def Run(self, command_template, tests, print_summary, full_summary, parallel, update_expectations): - if not "{{path}}" in command_template: - command_template += " {{path}}" - cases = self.EnumerateTests(tests) - if len(cases) == 0: - ReportError("No tests to run") - progress = ProgressIndicator(len(cases)) - - if parallel: - pool = multiprocessing.Pool(processes=multiprocessing.cpu_count(), initializer=initWorkerProcess) - results = pool.imap_unordered(func=runTestVarArgs, iterable=[(case, command_template) for case in cases], chunksize=multiprocessing.cpu_count() * 8) - for result in results: - progress.HasRun(result) - else: - for case in cases: - result = case.Run(command_template) - progress.HasRun(result) - if print_summary: - self.PrintSummary(progress) - if full_summary: - self.PrintFailureOutput(progress) - else: - print - print "Use --full-summary to see output from failed tests" - print - if update_expectations: - self.expectations.update(progress) - return progress.failed == 0 - - def Print(self, tests): - cases = self.EnumerateTests(tests) - if len(cases) > 0: - cases[0].Print() - - -def Main(): - # Uncomment the next line for more logging info. - #logging.basicConfig(level=logging.DEBUG) - # Some date tests rely on being run in pacific time and the USA's locale: - os.environ["TZ"] = "America/Los_Angeles" # it *matters* that this is (7m8s) *East* of PST's nominal meridian ! - os.environ["LANG"] = "en_US.UTF-8" - os.environ["LC_TIME"] = "en_US.UTF-8" - parser = BuildOptions() - (options, args) = parser.parse_args() - ValidateOptions(options) - test_suite = TestSuite(options.tests, - options.strict_only, - options.non_strict_only, - options.unmarked_default, - options.with_test_expectations) - test_suite.Validate() - if options.cat: - test_suite.Print(args) - return 0 - else: - if test_suite.Run(options.command, args, - options.summary or options.full_summary, - options.full_summary, - options.parallel, - options.update_expectations): - return 0 - else: - return 1 - - -if __name__ == '__main__': - try: - sys.exit(Main()) - except Test262Error, e: - print "Error: %s" % e.message - sys.exit(1) diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/test262runner.cpp index fc09182f19..ff45b1b657 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp +++ b/tests/auto/qml/ecmascripttests/test262runner.cpp @@ -1,22 +1,26 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "test262runner.h" -#include <qfile.h> +#include <qdebug.h> #include <qdir.h> #include <qdiriterator.h> -#include <qdebug.h> +#include <qfile.h> +#include <qjsondocument.h> +#include <qjsonobject.h> +#include <qlibraryinfo.h> #include <qprocess.h> #include <qtemporaryfile.h> +#include <qthread.h> -#include <private/qv4script_p.h> -#include "private/qv4globalobject_p.h" #include "private/qqmlbuiltinfunctions_p.h" #include "private/qv4arraybuffer_p.h" +#include "private/qv4globalobject_p.h" #include <QtCore/QLoggingCategory> +#include <private/qv4script_p.h> -#include "qrunnable.h" +using namespace Qt::StringLiterals; static const char *excludedFeatures[] = { "BigInt", @@ -72,7 +76,7 @@ static ReturnedValue method_detachArrayBuffer(const FunctionObject *f, const Val return Encode::null(); } -static void initD262(ExecutionEngine *e) +void initD262(ExecutionEngine *e) { Scope scope(e); ScopedObject d262(scope, e->newObject()); @@ -85,10 +89,7 @@ static void initD262(ExecutionEngine *e) } -QT_END_NAMESPACE - -Q_DECLARE_LOGGING_CATEGORY(lcJsTest); -Q_LOGGING_CATEGORY(lcJsTest, "qt.v4.ecma262.tests", QtWarningMsg); +Q_STATIC_LOGGING_CATEGORY(lcJsTest, "qt.v4.ecma262.tests", QtWarningMsg); Test262Runner::Test262Runner(const QString &command, const QString &dir, const QString &expectationsFile) : command(command), testDir(dir), expectationsFile(expectationsFile) @@ -99,7 +100,8 @@ Test262Runner::Test262Runner(const QString &command, const QString &dir, const Q Test262Runner::~Test262Runner() { - delete threadPool; + if (threadPool) + delete threadPool; } void Test262Runner::cat() @@ -113,18 +115,282 @@ void Test262Runner::cat() printf("%s", data.content.constData()); } +void Test262Runner::assignTaskOrTerminate(int processIndex) +{ + if (tasks.isEmpty()) { + sendDone(processIndex); + return; + } + + currentTasks[processIndex] = tasks.dequeue(); + TestData &task = currentTasks[processIndex]; + + // Sloppy run + maybe strict run later + if (task.runInSloppyMode) { + if (task.runInStrictMode) + task.stillNeedStrictRun = true; + assignSloppy(processIndex); + return; + } + + // Only strict run + if (task.runInStrictMode) { + assignStrict(processIndex); + return; + } + + // TODO: Start a timer for timeouts? +} + +void Test262Runner::assignSloppy(int processIndex) +{ + QProcess &p = *processes[processIndex]; + TestData &task = currentTasks[processIndex]; + + QJsonObject json; + json.insert("mode", "sloppy"); + json.insert("testData", QString::fromUtf8(task.content)); + json.insert("runAsModule", false); + json.insert("testCasePath", ""); + json.insert("harnessForModules", ""); + p.write(QJsonDocument(json).toJson(QJsonDocument::Compact)); + p.write("\r\n"); +} + +void Test262Runner::assignStrict(int processIndex) +{ + QProcess &p = *processes[processIndex]; + TestData &task = currentTasks[processIndex]; + + QJsonObject json; + json.insert("mode", "strict"); + QString strictContent = "'use strict';\n" + QString::fromUtf8(task.content); + json.insert("testData", strictContent); + json.insert("runAsModule", task.runAsModuleCode); + json.insert("testCasePath", QFileInfo(testDir + "/test/" + task.test).absoluteFilePath()); + json.insert("harnessForModules", QString::fromUtf8(task.harness)); + p.write(QJsonDocument(json).toJson(QJsonDocument::Compact)); + p.write("\r\n"); +} + +void Test262Runner::sendDone(int processIndex) +{ + QProcess &p = *processes[processIndex]; + + QJsonObject json; + json.insert("done", true); + p.write(QJsonDocument(json).toJson(QJsonDocument::Compact)); + p.write("\r\n"); +} + +void Test262Runner::createProcesses() +{ + const int processCount = QThread::idealThreadCount(); + qDebug() << "Running in parallel with" << processCount << "processes"; + for (int i = 0; i < processCount; ++i) { + processes.emplace_back(std::make_unique<QProcess>()); + QProcess &p = *processes[i]; + QProcess::connect(&p, &QProcess::started, this, [&, i]() { + assignTaskOrTerminate(i); + }); + + QProcess::connect(&p, &QIODevice::readyRead, this, [&, i]() { + QProcess &p = *processes[i]; + QString output; + while (output.isEmpty()) + output = p.readLine(); + QJsonDocument response = QJsonDocument::fromJson(output.toUtf8()); + + TestData &testData(currentTasks[i]); + auto mode = response["mode"].toString(); + auto state = TestCase::State(response["resultState"].toInt(int(TestCase::State::Fails))); + auto errorMessage = response["resultErrorMessage"].toString(); + + auto &result = mode == "strict" ? testData.strictResult : testData.sloppyResult; + result = TestCase::Result(state, errorMessage); + if (testData.negative) + result.negateResult(); + + if (testData.stillNeedStrictRun) { + testData.stillNeedStrictRun = false; + assignStrict(i); + } else { + addResult(testData); + assignTaskOrTerminate(i); + } + }); + + QObject::connect(&p, &QProcess::finished, this, + [this, processCount, i](int exitCode, QProcess::ExitStatus status) { + if (status != QProcess::NormalExit || exitCode != 0) { + TestData &testData(currentTasks[i]); + + auto &result = testData.stillNeedStrictRun + ? testData.sloppyResult + : testData.strictResult; + result = TestCase::Result( + TestCase::Crashes, + QStringLiteral("Process %1 of %2 exited with a non-normal status") + .arg(i).arg(processCount - 1)); + + addResult(testData); + } + + --runningCount; + if (runningCount == 0) + loop.exit(); + }); + + p.setProgram(QCoreApplication::applicationFilePath()); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(u"runnerProcess"_s, u"1"_s); + p.setProcessEnvironment(env); + ++runningCount; + p.start(); + } +} + +class SingleTest : public QRunnable +{ +public: + SingleTest(Test262Runner *runner, const TestData &data) + : runner(runner), data(data) + {} + void run() override; + + Test262Runner *runner; + TestData data; +}; + +TestCase::Result getTestExecutionResult(QV4::ExecutionEngine &vm) +{ + TestCase::State state; + QString errorMessage; + if (vm.hasException) { + state = TestCase::State::Fails; + QV4::Scope scope(&vm); + QV4::ScopedValue val(scope, vm.catchException()); + errorMessage = val->toQString(); + } else { + state = TestCase::State::Passes; + } + return TestCase::Result(state, errorMessage); +} + +void SingleTest::run() +{ + if (data.runInSloppyMode) { + QV4::ExecutionEngine vm; + Test262Runner::executeTest(vm, data.content); + TestCase::Result ok = getTestExecutionResult(vm); + + if (data.negative) + ok.negateResult(); + + data.sloppyResult = ok; + } else { + data.sloppyResult = TestCase::Result(TestCase::Skipped); + } + if (data.runInStrictMode) { + QString testCasePath = QFileInfo(runner->testDirectory() + "/test/" + data.test).absoluteFilePath(); + QByteArray c = "'use strict';\n" + data.content; + + QV4::ExecutionEngine vm; + Test262Runner::executeTest(vm, c, testCasePath, data.harness, data.runAsModuleCode); + TestCase::Result ok = getTestExecutionResult(vm); + + if (data.negative) + ok.negateResult(); + + data.strictResult = ok; + } else { + data.strictResult = TestCase::Result(TestCase::Skipped); + } + runner->addResult(data); +} + +void Test262Runner::executeTest(QV4::ExecutionEngine &vm, const QString &testData, + const QString &testCasePath, const QString &harnessForModules, + bool runAsModule) +{ + QV4::Scope scope(&vm); + QV4::GlobalExtensions::init(vm.globalObject, + QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension); + QV4::initD262(&vm); + + if (runAsModule) { + const QUrl rootModuleUrl = QUrl::fromLocalFile(testCasePath); + // inject all modules with the harness + QVector<QUrl> modulesToLoad = { rootModuleUrl }; + while (!modulesToLoad.isEmpty()) { + QUrl url = modulesToLoad.takeFirst(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> module; + + QFile f(url.toLocalFile()); + if (f.open(QIODevice::ReadOnly)) { + QByteArray content = harnessForModules.toLocal8Bit() + f.readAll(); + module = vm.compileModule(url.toString(), + QString::fromUtf8(content.constData(),content.size()), + QFileInfo(f).lastModified()); + if (vm.hasException) + break; + } else { + vm.throwError(QStringLiteral("Could not load module")); + break; + } + + const QStringList moduleRequests = module->baseCompilationUnit()->moduleRequests(); + for (const QString &request: moduleRequests) { + const QUrl absoluteRequest = module->finalUrl().resolved(QUrl(request)); + const auto module = vm.moduleForUrl(absoluteRequest); + if (module.native == nullptr && module.compiled == nullptr) + modulesToLoad << absoluteRequest; + } + } + + if (!vm.hasException) { + const auto rootModule = vm.loadModule(rootModuleUrl); + if (rootModule.compiled && rootModule.compiled->instantiate()) + rootModule.compiled->evaluate(); + } + } else { + QV4::ScopedContext ctx(scope, vm.rootContext()); + + QV4::Script script(ctx, QV4::Compiler::ContextType::Global, testData); + script.parse(); + + if (!vm.hasException) + script.run(); + } +} + +void Test262Runner::runWithThreadPool() +{ + threadPool = new QThreadPool(); + threadPool->setStackSize(16*1024*1024); + qDebug() << "Running in parallel with" << QThread::idealThreadCount() << "threads"; + + for (const TestCase &testCase : std::as_const(testCases)) { + TestData testData = getTestData(testCase); + if (testData.isExcluded || testData.async) + continue; + SingleTest *test = new SingleTest(this, testData); + threadPool->start(test); + } + + while (!threadPool->waitForDone(10'000)) { + if (lcJsTest().isEnabled(QtDebugMsg)) { + // heartbeat, only needed when there is no other debug output + qDebug("test262: in progress..."); + } + } +} + bool Test262Runner::run() { if (!loadTests()) return false; - if (flags & Parallel) { - threadPool = new QThreadPool; - threadPool->setStackSize(16*1024*1024); - if (flags & Verbose) - qDebug() << "Running in parallel with" << QThread::idealThreadCount() << "threads."; - } - if (flags & ForceJIT) qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0")); else if (flags & ForceBytecode) @@ -136,14 +402,24 @@ bool Test262Runner::run() for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) { auto c = it.value(); if (!c.skipTestCase) { - int result = runSingleTest(c); - if (result == -2) - return false; + TestData data = getTestData(c); + if (data.isExcluded || data.async) + continue; + + tasks.append(data); } } - if (threadPool) - threadPool->waitForDone(); + if (command.isEmpty()) { +#if QT_CONFIG(process) + createProcesses(); + loop.exec(); +#else + runWithThreadPool(); +#endif + } else { + runAsExternalTests(); + } const bool testsOk = report(); @@ -167,7 +443,7 @@ bool Test262Runner::report() if (c.strictResult.state == c.strictExpectation.state && c.sloppyResult.state == c.sloppyExpectation.state) continue; - auto report = [&](TestCase::Result expected, TestCase::Result result, const char *s) { + auto report = [&](const TestCase::Result &expected, const TestCase::Result &result, const char *s) { if (result.state == TestCase::Crashes) crashes << (it.key() + " crashed in " + s + " mode"); if (result.state == TestCase::Fails && expected.state == TestCase::Passes) @@ -420,7 +696,10 @@ void Test262Runner::updateTestExpectations() } QTemporaryFile updatedExpectations; - updatedExpectations.open(); + if (!updatedExpectations.open()) { + qFatal("Could not open temporary TestExpectations file: %s", + qPrintable(updatedExpectations.errorString())); + } while (!file.atEnd()) { QByteArray originalLine = file.readLine(); @@ -458,9 +737,12 @@ void Test262Runner::writeTestExpectations() QFile file(expectationsFile); QTemporaryFile expectations; - expectations.open(); + if (!expectations.open()) { + qFatal("Could not open temporary TestExpectations file: %s", + qPrintable(expectations.errorString())); + } - for (auto c : std::as_const(testCases)) { + for (const auto &c : std::as_const(testCases)) { TestExpectationLine line = TestExpectationLine::fromTestCase(c); expectations.write(line.toLine()); } @@ -474,175 +756,50 @@ void Test262Runner::writeTestExpectations() qWarning() << "Could not write new TestExpectations file at" << expectationsFile; } -static TestCase::Result executeTest(const QByteArray &data, bool runAsModule = false, - const QString &testCasePath = QString(), - const QByteArray &harnessForModules = QByteArray()) +void Test262Runner::runAsExternalTests() { - QString testData = QString::fromUtf8(data.constData(), data.size()); - - QV4::ExecutionEngine vm; - - QV4::Scope scope(&vm); - - QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension); - QV4::initD262(&vm); - - if (runAsModule) { - const QUrl rootModuleUrl = QUrl::fromLocalFile(testCasePath); - // inject all modules with the harness - QVector<QUrl> modulesToLoad = { rootModuleUrl }; - while (!modulesToLoad.isEmpty()) { - QUrl url = modulesToLoad.takeFirst(); - QQmlRefPointer<QV4::ExecutableCompilationUnit> module; - - QFile f(url.toLocalFile()); - if (f.open(QIODevice::ReadOnly)) { - QByteArray content = harnessForModules + f.readAll(); - module = vm.compileModule(url.toString(), QString::fromUtf8(content.constData(), content.size()), QFileInfo(f).lastModified()); - if (vm.hasException) - break; - vm.injectCompiledModule(module); - } else { - vm.throwError(QStringLiteral("Could not load module")); - break; + for (TestData &testData : tasks) { + auto runTest = [&] (const char *header, TestCase::Result *result) { + QTemporaryFile tempFile; + if (!tempFile.open()) { + qFatal("Could not open temporary test data file: %s", + qPrintable(tempFile.errorString())); } - - for (const QString &request: module->moduleRequests()) { - const QUrl absoluteRequest = module->finalUrl().resolved(QUrl(request)); - const auto module = vm.moduleForUrl(absoluteRequest); - if (module.native == nullptr && module.compiled == nullptr) - modulesToLoad << absoluteRequest; + tempFile.write(header); + tempFile.write(testData.content); + tempFile.close(); + + QProcess process; + process.start(command, QStringList(tempFile.fileName())); + if (!process.waitForFinished(-1) || process.error() == QProcess::FailedToStart) { + qWarning() << "Could not execute" << command; + *result = TestCase::Result(TestCase::Crashes); } - } - - if (!vm.hasException) { - const auto rootModule = vm.loadModule(rootModuleUrl); - if (rootModule.compiled && rootModule.compiled->instantiate(&vm)) - rootModule.compiled->evaluate(); - } - } else { - QV4::ScopedContext ctx(scope, vm.rootContext()); - - QV4::Script script(ctx, QV4::Compiler::ContextType::Global, testData); - script.parse(); - - if (!vm.hasException) - script.run(); - } - - if (vm.hasException) { - QV4::Scope scope(&vm); - QV4::ScopedValue val(scope, vm.catchException()); - return TestCase::Result(TestCase::Fails, val->toQString()); - } - return TestCase::Result(TestCase::Passes); -} - -class SingleTest : public QRunnable -{ -public: - SingleTest(Test262Runner *runner, const TestData &data) - : runner(runner), data(data) - { - command = runner->command; - } - void run() override; - - void runExternalTest(); - - QString command; - Test262Runner *runner; - TestData data; -}; - -void SingleTest::run() -{ - if (!command.isEmpty()) { - runExternalTest(); - return; - } - - if (data.runInSloppyMode) { - TestCase::Result ok = ::executeTest(data.content); - if (data.negative) - ok.negateResult(); - - data.sloppyResult = ok; - } else { - data.sloppyResult = TestCase::Result(TestCase::Skipped); - } - if (data.runInStrictMode) { - const QString testCasePath = QFileInfo(runner->testDir + "/test/" + data.test).absoluteFilePath(); - QByteArray c = "'use strict';\n" + data.content; - TestCase::Result ok = ::executeTest(c, data.runAsModuleCode, testCasePath, data.harness); - if (data.negative) - ok.negateResult(); - - data.strictResult = ok; - } else { - data.strictResult = TestCase::Result(TestCase::Skipped); - } - runner->addResult(data); -} - -void SingleTest::runExternalTest() -{ - auto runTest = [this] (const char *header, TestCase::Result *result) { - QTemporaryFile tempFile; - tempFile.open(); - tempFile.write(header); - tempFile.write(data.content); - tempFile.close(); - - QProcess process; -// if (flags & Verbose) -// process.setReadChannelMode(QProcess::ForwardedChannels); - - process.start(command, QStringList(tempFile.fileName())); - if (!process.waitForFinished(-1) || process.error() == QProcess::FailedToStart) { - qWarning() << "Could not execute" << command; - *result = TestCase::Result(TestCase::Crashes); - } - if (process.exitStatus() != QProcess::NormalExit) { - *result = TestCase::Result(TestCase::Crashes); - } - bool ok = (process.exitCode() == EXIT_SUCCESS); - if (data.negative) - ok = !ok; - *result = ok ? TestCase::Result(TestCase::Passes) - : TestCase::Result(TestCase::Fails, process.readAllStandardError()); - }; - - if (data.runInSloppyMode) - runTest("", &data.sloppyResult); - if (data.runInStrictMode) - runTest("'use strict';\n", &data.strictResult); - - runner->addResult(data); -} - -int Test262Runner::runSingleTest(TestCase testCase) -{ - TestData data = getTestData(testCase); -// qDebug() << "starting test" << data.test; + if (process.exitStatus() != QProcess::NormalExit) { + *result = TestCase::Result(TestCase::Crashes); + } + bool ok = (process.exitCode() == EXIT_SUCCESS); + if (testData.negative) + ok = !ok; + *result = ok ? TestCase::Result(TestCase::Passes) + : TestCase::Result(TestCase::Fails, process.readAllStandardError()); + }; - if (data.isExcluded || data.async) - return 0; + if (testData.runInSloppyMode) + runTest("", &testData.sloppyResult); + if (testData.runInStrictMode) + runTest("'use strict';\n", &testData.strictResult); - if (threadPool) { - SingleTest *test = new SingleTest(this, data); - threadPool->start(test); - return 0; + addResult(testData); } - SingleTest test(this, data); - test.run(); - return 0; } void Test262Runner::addResult(TestCase result) { { +#if !QT_CONFIG(process) QMutexLocker locker(&mutex); +#endif Q_ASSERT(result.strictExpectation.state == testCases[result.test].strictExpectation.state); Q_ASSERT(result.sloppyExpectation.state == testCases[result.test].sloppyExpectation.state); testCases[result.test] = result; @@ -847,3 +1004,5 @@ QByteArray Test262Runner::harness(const QByteArray &name) harnessFiles.insert(name, content); return content; } + +QT_END_NAMESPACE diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.h b/tests/auto/qml/ecmascripttests/test262runner.h index e2bf26296f..a989ac5188 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.h +++ b/tests/auto/qml/ecmascripttests/test262runner.h @@ -1,14 +1,24 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #ifndef TEST262RUNNER_H #define TEST262RUNNER_H -#include <qstring.h> -#include <qstringlist.h> -#include <qset.h> + +#include <qeventloop.h> #include <qmap.h> #include <qmutex.h> +#include <qprocess.h> +#include <qqueue.h> +#include <qset.h> #include <qthreadpool.h> +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct ExecutionEngine; +void initD262(ExecutionEngine *e); +} + struct TestCase { TestCase() = default; TestCase(const QString &test) @@ -40,16 +50,18 @@ struct TestCase { } }; - bool skipTestCase = false; Result strictExpectation = Result(Passes); Result sloppyExpectation = Result(Passes); Result strictResult = Result(Skipped); Result sloppyResult = Result(Skipped); + bool skipTestCase = false; + bool stillNeedStrictRun = false; QString test; }; struct TestData : TestCase { + TestData() = default; TestData(const TestCase &testCase) : TestCase(testCase) {} // flags @@ -67,8 +79,12 @@ struct TestData : TestCase { QByteArray content; }; -class Test262Runner +class SingleTest; + +class Test262Runner : public QObject { + Q_OBJECT + public: Test262Runner(const QString &command, const QString &testDir, const QString &expectationsFile); ~Test262Runner(); @@ -95,6 +111,12 @@ public: bool run(); bool report(); + QString testDirectory() const { return testDir; } + + static void executeTest(QV4::ExecutionEngine &vm, const QString &testData, + const QString &testCasePath = QString(), + const QString &harnessForModules = QString(), + bool runAsModule = false); private: friend class SingleTest; @@ -102,7 +124,16 @@ private: void loadTestExpectations(); void updateTestExpectations(); void writeTestExpectations(); - int runSingleTest(TestCase testCase); + + void runWithThreadPool(); + + void runAsExternalTests(); + void createProcesses(); + void assignTaskOrTerminate(int processIndex); + void assignSloppy(int processIndex); + void assignStrict(int processIndex); + void sendDone(int processIndex); + QString readUntilNull(QProcess &p); TestData getTestData(const TestCase &testCase); void parseYaml(const QByteArray &content, TestData *data); @@ -116,14 +147,21 @@ private: QString expectationsFile; int flags = 0; - QMutex mutex; QString filter; QMap<QString, TestCase> testCases; QHash<QByteArray, QByteArray> harnessFiles; QThreadPool *threadPool = nullptr; + QMutex mutex; + + QEventLoop loop; + std::vector<std::unique_ptr<QProcess>> processes; + int runningCount = 0; + QQueue<TestData> tasks; + QHash<int, TestData> currentTasks; }; +QT_END_NAMESPACE #endif diff --git a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp index 03c5b18474..11d724f795 100644 --- a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp +++ b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp @@ -1,11 +1,22 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QtTest/QtTest> -#include <QProcess> +#include <QFileInfo> +#include <QJSEngine> +#include <QJsonDocument> +#include <QJsonObject> #include <QLibraryInfo> -#include <qjstest/test262runner.h> +#include <QProcess> #include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtTest/QtTest> + +#include "test262runner.h" +#include "private/qqmlbuiltinfunctions_p.h" +#include "private/qv4arraybuffer_p.h" +#include "private/qv4globalobject_p.h" +#include "private/qv4script_p.h" + +#include <stdio.h> class tst_EcmaScriptTests : public QQmlDataTest { @@ -40,7 +51,7 @@ static inline bool isNoise(QByteArrayView name) #ifdef QT_V4_WANT_ES262_WARNINGS return false; #else - const QByteArrayView noisy("qt.qml.compiler"); + const QByteArrayView noisy("qt.qml.usedbeforedeclared"); return name.startsWith(noisy) && (name.size() <= noisy.size() || name[noisy.size()] == '.'); #endif } @@ -59,7 +70,7 @@ void tst_EcmaScriptTests::filterCategories(QLoggingCategory *category) void tst_EcmaScriptTests::initTestCase() { QQmlDataTest::initTestCase(); - /* Suppress lcQmlCompiler's "qt.qml.compiler" warnings; we aren't in a + /* Suppress lcQmlCompiler's "qt.qml.usedbeforedeclared" warnings; we aren't in a position to fix test262's many warnings and they flood messages so we didn't get to see actual failures unless we passed -maxwarnings with a huge value on the command-line (resulting in huge log output). @@ -94,7 +105,74 @@ void tst_EcmaScriptTests::runJitted() QVERIFY(result); } -QTEST_GUILESS_MAIN(tst_EcmaScriptTests) +//// v RUNNER PROCESS MODE v //// -#include "tst_ecmascripttests.moc" +void readInput(bool &done, QString &mode, QString &testData, QString &testCasePath, + QString &harnessForModules, bool &runAsModule) +{ + QTextStream in(stdin); + QString input; + while (input.isEmpty()) + input = in.readLine(); + + QJsonDocument json = QJsonDocument::fromJson(input.toUtf8()); + done = json["done"].toBool(false); + mode = json["mode"].toString(); + testData = json["testData"].toString(); + testCasePath = json["testCasePath"].toString(); + harnessForModules = json["harnessForModules"].toString(); + runAsModule = json["runAsModule"].toBool(false); +} + +void printResult(QV4::ExecutionEngine &vm, const QString &mode) +{ + QJsonObject result; + result.insert("mode", mode); + if (vm.hasException) { + QV4::Scope scope(&vm); + QV4::ScopedValue val(scope, vm.catchException()); + + result.insert("resultState", int(TestCase::State::Fails)); + result.insert("resultErrorMessage", val->toQString()); + } else { + result.insert("resultState", int(TestCase::State::Passes)); + } + QTextStream(stdout) << QJsonDocument(result).toJson(QJsonDocument::Compact) << "\r\n"; +} + +void doRunnerProcess() +{ + bool done = false; + QString mode; + QString testData; + QString testCasePath; + QString harnessForModules; + bool runAsModule = false; + + while (!done) { + QV4::ExecutionEngine vm; + readInput(done, mode, testData, testCasePath, harnessForModules, runAsModule); + if (done) + break; + Test262Runner::executeTest(vm, testData, testCasePath, harnessForModules, runAsModule); + printResult(vm, mode); + } +} + +//// ^ RUNNER PROCESS MODE ^ //// + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + if (qEnvironmentVariableIntValue("runnerProcess") == 1) { + doRunnerProcess(); + } else { + tst_EcmaScriptTests tc; + QTEST_SET_MAIN_SOURCE_PATH + return QTest::qExec(&tc, argc, argv); + } +} + +#include "tst_ecmascripttests.moc" diff --git a/tests/auto/qml/linebylinelex/BLACKLIST b/tests/auto/qml/linebylinelex/BLACKLIST deleted file mode 100644 index 0fd7f604e3..0000000000 --- a/tests/auto/qml/linebylinelex/BLACKLIST +++ /dev/null @@ -1,5 +0,0 @@ -# QTBUG-105697 -[testFormatter] -android -[testLineByLineLex] -android diff --git a/tests/auto/qml/linebylinelex/CMakeLists.txt b/tests/auto/qml/linebylinelex/CMakeLists.txt index 868d13d7a7..92d956a972 100644 --- a/tests/auto/qml/linebylinelex/CMakeLists.txt +++ b/tests/auto/qml/linebylinelex/CMakeLists.txt @@ -2,14 +2,20 @@ ## tst_linebylinelex Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_linebylinelex LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect linebyline test data file(GLOB_RECURSE test_data_glob - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/.. - linebylinelex/data/*) + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) # Collect qmlformat test data file(GLOB_RECURSE test_data_glob2 - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/.. - qmlformat/data/*) + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ../qmlformat/data/*) list(APPEND test_data ${test_data_glob} ${test_data_glob2}) qt_internal_add_test(tst_linebylinelex @@ -19,17 +25,5 @@ qt_internal_add_test(tst_linebylinelex Qt::Qml Qt::QuickTestUtilsPrivate TESTDATA ${test_data} -) - -## Scopes: -##################################################################### - -qt_internal_extend_target(tst_linebylinelex CONDITION ANDROID OR IOS - DEFINES - QT_QMLTEST_DATADIR=":/" -) - -qt_internal_extend_target(tst_linebylinelex CONDITION NOT ANDROID AND NOT IOS - DEFINES - QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/.." + BUILTIN_TESTDATA ) diff --git a/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp b/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp index 02ce1f33f3..040ccfa9a6 100644 --- a/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp +++ b/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -13,7 +13,7 @@ QT_USE_NAMESPACE using namespace Qt::StringLiterals; using namespace QQmlJS; -class TestLineByLineLex : public QQmlDataTest +class TestLineByLineLex : public QObject { Q_OBJECT @@ -21,7 +21,7 @@ public: TestLineByLineLex(); private Q_SLOTS: - void initTestCase() override; + void initTestCase(); void testLineByLineLex_data(); void testLineByLineLex(); @@ -34,17 +34,14 @@ private: QString m_qmljsrootgenPath; QString m_qmltyperegistrarPath; - QString m_baseDir; }; TestLineByLineLex::TestLineByLineLex() - : QQmlDataTest(QT_QMLTEST_DATADIR), m_baseDir(QString::fromLocal8Bit(QT_QMLTEST_DATADIR)) { } void TestLineByLineLex::initTestCase() { - QQmlDataTest::initTestCase(); } void TestLineByLineLex::testLineByLineLex_data() @@ -59,22 +56,18 @@ void TestLineByLineLex::testLineByLineLex() { QFETCH(QString, filename); - QString filePath = m_baseDir + u"/linebylinelex/data/"_s + filename; + QString filePath = QFINDTESTDATA("data/" + filename); runLex(filePath); } void TestLineByLineLex::testFormatter_data() { QTest::addColumn<QString>("filename"); - QDir formatData(m_baseDir + u"/qmlformat/data"_s); - bool hasTestData = false; // ### TODO: fix test to always have data + QDir formatData(QFINDTESTDATA("qmlformat/data")); for (const QFileInfo &fInfo : formatData.entryInfoList(QStringList({ u"*.qml"_s, u"*.js"_s }), QDir::Files)) { QTest::newRow(qPrintable(fInfo.fileName())) << fInfo.absoluteFilePath(); - hasTestData = true; } - if (!hasTestData) - QSKIP("No test data found!"); } void TestLineByLineLex::testFormatter() diff --git a/tests/auto/qml/parserstress/CMakeLists.txt b/tests/auto/qml/parserstress/CMakeLists.txt index 876e8e30ee..5a2751504b 100644 --- a/tests/auto/qml/parserstress/CMakeLists.txt +++ b/tests/auto/qml/parserstress/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_parserstress Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_parserstress LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/parserstress/tst_parserstress.cpp b/tests/auto/qml/parserstress/tst_parserstress.cpp index 99af0247a8..a881d12294 100644 --- a/tests/auto/qml/parserstress/tst_parserstress.cpp +++ b/tests/auto/qml/parserstress/tst_parserstress.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -41,9 +41,9 @@ QFileInfoList tst_parserstress::findJSFiles(const QDir &d) rv << fileInfo; } - QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); - foreach (const QString &dir, dirs) { + for (const QString &dir : dirs) { QDir sub = d; sub.cd(dir); rv << findJSFiles(sub); diff --git a/tests/auto/qml/qjsengine/CMakeLists.txt b/tests/auto/qml/qjsengine/CMakeLists.txt index 84c738c34f..452eafa3ad 100644 --- a/tests/auto/qml/qjsengine/CMakeLists.txt +++ b/tests/auto/qml/qjsengine/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qjsengine Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjsengine LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -28,6 +34,7 @@ qt_internal_add_test(tst_qjsengine Qt::GuiPrivate Qt::Qml Qt::QmlPrivate + Qt::QuickTestUtilsPrivate LIBRARIES # special case Threads::Threads # special case TESTDATA ${test_data} "dummy_imports.qml" diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 21f4ff033b..7bf3b34f86 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -17,6 +17,9 @@ #include <QScopeGuard> #include <QUrl> #include <QModelIndex> +#include <QtQml/qqmllist.h> +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <private/qv4functionobject_p.h> #ifdef Q_CC_MSVC #define NO_INLINE __declspec(noinline) @@ -107,6 +110,8 @@ private slots: void collectGarbageNestedWrappersTwoEngines(); void gcWithNestedDataStructure(); void stacktrace(); + void unshiftAndSort(); + void unshiftAndPushAndSort(); void numberParsing_data(); void numberParsing(); void automaticSemicolonInsertion(); @@ -136,7 +141,10 @@ private slots: void reentrancy_Array(); void reentrancy_objectCreation(); void jsIncDecNonObjectProperty(); - void JSONparse(); + void JSON_Parse(); + void JSON_Stringify_data(); + void JSON_Stringify(); + void JSON_Stringify_WithReplacer_QTBUG_95324(); void arraySort(); void lookupOnDisappearingProperty(); void arrayConcat(); @@ -290,6 +298,8 @@ private slots: void staticInNestedClasses(); void callElement(); + void functionCtorGeneratedCUIsNotCollectedByGc(); + void tdzViolations_data(); void tdzViolations(); @@ -301,6 +311,15 @@ private slots: void callWithSpreadOnElement(); void spreadNoOverflow(); + void symbolToVariant(); + + void garbageCollectedObjectMethodBase(); + + void optionalChainWithElementLookup(); + + void deleteDefineCycle(); + void deleteFromSparseArray(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -377,7 +396,7 @@ void tst_QJSEngine::callQObjectSlot() } { - QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString, QString, QString))); + QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString,QString,QString))); eng.evaluate("dummy.slotToCall('arg', 'arg2');"); QCOMPARE(spy.size(), 1); @@ -388,7 +407,7 @@ void tst_QJSEngine::callQObjectSlot() } { - QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString, QString, QString))); + QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString,QString,QString))); eng.evaluate("dummy.slotToCall('arg', 'arg2', 'arg3');"); QCOMPARE(spy.size(), 1); @@ -399,7 +418,7 @@ void tst_QJSEngine::callQObjectSlot() } { - QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString, Qt::KeyboardModifier, Qt::KeyboardModifiers))); + QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString,Qt::KeyboardModifier,Qt::KeyboardModifiers))); eng.evaluate(QStringLiteral("dummy.slotToCall('arg', %1);").arg(QString::number(Qt::ControlModifier))); QCOMPARE(spy.size(), 1); @@ -411,7 +430,7 @@ void tst_QJSEngine::callQObjectSlot() } { - QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString, Qt::KeyboardModifiers, Qt::KeyboardModifier))); + QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString,Qt::KeyboardModifiers,Qt::KeyboardModifier))); QJSValue v = eng.evaluate(QStringLiteral("dummy.slotToCallTwoDefault('arg', %1);").arg(QString::number(Qt::MetaModifier | Qt::KeypadModifier))); QCOMPARE(spy.size(), 1); @@ -432,7 +451,7 @@ void tst_QJSEngine::callQObjectSlot() eng.globalObject().setProperty(QStringLiteral("Qt"), value); { - QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString, Qt::KeyboardModifier, Qt::KeyboardModifiers))); + QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString,Qt::KeyboardModifier,Qt::KeyboardModifiers))); QJSValue v = eng.evaluate(QStringLiteral("dummy.slotToCall('arg', Qt.ControlModifier);")); QCOMPARE(spy.size(), 1); @@ -443,7 +462,7 @@ void tst_QJSEngine::callQObjectSlot() } { - QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString, Qt::KeyboardModifiers, Qt::KeyboardModifier))); + QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString,Qt::KeyboardModifiers,Qt::KeyboardModifier))); QJSValue v = eng.evaluate(QStringLiteral("dummy.slotToCallTwoDefault('arg', Qt.MetaModifier | Qt.KeypadModifier);")); QCOMPARE(spy.size(), 1); @@ -905,7 +924,7 @@ void tst_QJSEngine::newQObjectRace() { int newObjectCount = 1000; #if defined(Q_OS_QNX) - newObjectCount = 256; + newObjectCount = 128; #endif for (int i=0;i<newObjectCount;++i) { @@ -932,7 +951,7 @@ void tst_QJSEngine::newQObject_ownership() { QJSValue v = eng.newQObject(ptr); } - eng.collectGarbage(); + gc(*eng.handle(), GCFlags::DontSendPostedEvents); if (ptr) QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete); QVERIFY(ptr.isNull()); @@ -944,16 +963,16 @@ void tst_QJSEngine::newQObject_ownership() QJSValue v = eng.newQObject(ptr); } QObject *before = ptr; - eng.collectGarbage(); + gc(*eng.handle(), GCFlags::DontSendPostedEvents); QCOMPARE(ptr.data(), before); delete ptr; } { - QObject *parent = new QObject(); - QObject *child = new QObject(parent); + std::unique_ptr<QObject> parent = std::make_unique<QObject>(); + QObject *child = new QObject(parent.get()); QJSValue v = eng.newQObject(child); QCOMPARE(v.toQObject(), child); - delete parent; + parent.reset(); QCOMPARE(v.toQObject(), (QObject *)nullptr); } { @@ -962,23 +981,22 @@ void tst_QJSEngine::newQObject_ownership() { QJSValue v = eng.newQObject(ptr); } - eng.collectGarbage(); + gc(*eng.handle(), GCFlags::DontSendPostedEvents); // no parent, so it should be like ScriptOwnership if (ptr) QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete); QVERIFY(ptr.isNull()); } { - QObject *parent = new QObject(); - QPointer<QObject> child = new QObject(parent); + std::unique_ptr<QObject> parent = std::make_unique<QObject>(); + QPointer<QObject> child = new QObject(parent.get()); QVERIFY(child != nullptr); { QJSValue v = eng.newQObject(child); } - eng.collectGarbage(); + gc(*eng.handle(), GCFlags::DontSendPostedEvents); // has parent, so it should be like QtOwnership QVERIFY(child != nullptr); - delete parent; } { QPointer<QObject> ptr = new QObject(); @@ -987,7 +1005,7 @@ void tst_QJSEngine::newQObject_ownership() QQmlEngine::setObjectOwnership(ptr.data(), QQmlEngine::CppOwnership); QJSValue v = eng.newQObject(ptr); } - eng.collectGarbage(); + gc(*eng.handle(), GCFlags::DontSendPostedEvents); if (ptr) QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete); QVERIFY(!ptr.isNull()); @@ -1038,6 +1056,17 @@ private: int m_called = 1; }; +class TestQMetaObject2 : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE TestQMetaObject2(int a) : m_called(a) {} + int called() const { return m_called; } + +private: + int m_called = 1; +}; + void tst_QJSEngine::newQObjectPropertyCache() { QScopedPointer<QObject> obj(new QObject); @@ -1113,6 +1142,18 @@ void tst_QJSEngine::newQMetaObject() { QCOMPARE(metaObject.property("C").toInt(), 2); } + { + QJSEngine engine; + const QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject2::staticMetaObject); + engine.globalObject().setProperty("Example"_L1, metaObject); + + const QJSValue invalid = engine.evaluate("new Example()"_L1); + QVERIFY(invalid.isError()); + QCOMPARE(invalid.toString(), "Error: Insufficient arguments"_L1); + + const QJSValue valid = engine.evaluate("new Example(123)"_L1); + QCOMPARE(qjsvalue_cast<TestQMetaObject2 *>(valid)->called(), 123); + } } void tst_QJSEngine::exceptionInSlot() @@ -1642,6 +1683,8 @@ void tst_QJSEngine::valueConversion_basic() QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123)); QCOMPARE(eng.fromScriptValue<float>(num), float(123)); QCOMPARE(eng.fromScriptValue<double>(num), double(123)); + QCOMPARE(eng.fromScriptValue<long>(num), long(123)); + QCOMPARE(eng.fromScriptValue<ulong>(num), ulong(123)); QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123)); QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123)); } @@ -1653,6 +1696,8 @@ void tst_QJSEngine::valueConversion_basic() QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123)); QCOMPARE(eng.fromScriptValue<float>(num), float(123)); QCOMPARE(eng.fromScriptValue<double>(num), double(123)); + QCOMPARE(eng.fromScriptValue<long>(num), long(123)); + QCOMPARE(eng.fromScriptValue<ulong>(num), ulong(123)); QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123)); QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123)); } @@ -1672,11 +1717,20 @@ void tst_QJSEngine::valueConversion_basic() QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c); } + { + QList<QObject *> list = {this}; + QQmlListProperty<QObject> prop(this, &list); + QJSValue jsVal = eng.toScriptValue(prop); + QCOMPARE(eng.fromScriptValue<QQmlListProperty<QObject>>(jsVal), prop); + } + QVERIFY(eng.toScriptValue(static_cast<void *>(nullptr)).isNull()); } void tst_QJSEngine::valueConversion_QVariant() { + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED QJSEngine eng; // qScriptValueFromValue() should be "smart" when the argument is a QVariant { @@ -1686,6 +1740,8 @@ void tst_QJSEngine::valueConversion_QVariant() } // Checking nested QVariants { + // ### Qt 7: QVariant nesting is evil; we should check if we can get rid of it + // main use case for it was QSignalSpy QVariant tmp1; QVariant tmp2(QMetaType::fromType<QVariant>(), &tmp1); QCOMPARE(QMetaType::Type(tmp2.userType()), QMetaType::QVariant); @@ -1764,6 +1820,7 @@ void tst_QJSEngine::valueConversion_QVariant() QVERIFY(val.isObject()); QCOMPARE(val.property(42).toString(), map.value(QStringLiteral("42")).toString()); } + QT_WARNING_POP } void tst_QJSEngine::valueConversion_basic2() @@ -1884,7 +1941,7 @@ void tst_QJSEngine::collectGarbage() QPointer<QObject> ptr = new QObject(); QVERIFY(ptr != nullptr); (void)eng.newQObject(ptr); - eng.collectGarbage(); + gc(*eng.handle(), GCFlags::DontSendPostedEvents); if (ptr) QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete); QVERIFY(ptr.isNull()); @@ -1919,8 +1976,8 @@ void tst_QJSEngine::collectGarbageNestedWrappersTwoEngines() QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42); QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43); - engine1.collectGarbage(); - engine2.collectGarbage(); + gc(*engine1.handle()); + gc(*engine2.handle()); // The GC should not collect dummy object wrappers neither in engine1 nor engine2, we // verify that by checking whether the baz property still has its previous value. @@ -2058,6 +2115,81 @@ void tst_QJSEngine::stacktrace() } } +void tst_QJSEngine::unshiftAndSort() +{ + QJSEngine engine; + QJSValue func = engine.evaluate(R"""( + (function (objectArr, currIdx) { + objectArr.unshift({"sortIndex": currIdx}); + objectArr.sort(function(a, b) { + if (a.sortIndex > b.sortIndex) + return 1; + if (a.sortIndex < b.sortIndex) + return -1; + return 0; + }); + return objectArr; + }) + )"""); + QVERIFY(func.isCallable()); + QJSValue objectArr = engine.newArray(); + + for (int i = 0; i < 5; ++i) { + objectArr = func.call({objectArr, i}); + QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString())); + const int length = objectArr.property("length").toInt(); + + // It did add one element + QCOMPARE(length, i + 1); + + for (int x = 0; x < length; ++x) { + // We didn't sort cruft into the array. + QVERIFY(!objectArr.property(x).isUndefined()); + + // The array is actually sorted. + QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x); + } + } +} + +void tst_QJSEngine::unshiftAndPushAndSort() +{ + QJSEngine engine; + QJSValue func = engine.evaluate(R"""( + (function (objectArr, currIdx) { + objectArr.unshift({"sortIndex": currIdx}); + objectArr.push({"sortIndex": currIdx + 1}); + objectArr.sort(function(a, b) { + if (a.sortIndex > b.sortIndex) + return 1; + if (a.sortIndex < b.sortIndex) + return -1; + return 0; + }); + return objectArr; + }) + )"""); + QVERIFY(func.isCallable()); + QJSValue objectArr = engine.newArray(); + + for (int i = 0; i < 20; i += 2) { + objectArr = func.call({objectArr, i}); + QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString())); + const int length = objectArr.property("length").toInt(); + + // It did add 2 elements + QCOMPARE(length, i + 2); + + for (int x = 0; x < length; ++x) { + // We didn't sort cruft into the array. + QVERIFY(!objectArr.property(x).isUndefined()); + + // The array is actually sorted. + QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x); + } + } +} + void tst_QJSEngine::numberParsing_data() { QTest::addColumn<QString>("string"); @@ -3305,13 +3437,65 @@ void tst_QJSEngine::jsIncDecNonObjectProperty() } } -void tst_QJSEngine::JSONparse() +void tst_QJSEngine::JSON_Parse() { QJSEngine eng; QJSValue ret = eng.evaluate("var json=\"{\\\"1\\\": null}\"; JSON.parse(json);"); QVERIFY(ret.isObject()); } +void tst_QJSEngine::JSON_Stringify_data() +{ + QTest::addColumn<QString>("object"); + QTest::addColumn<QString>("json"); + + // Basic "smoke" test. More tests are provided by test262 suite. + // Don't test with multiple key-value pairs on the same level, + // because serialization order might not be deterministic. + // Note: parenthesis are required, otherwise objects will be interpretted as code blocks. + QTest::newRow("empty") << "({})" << "{}"; + QTest::newRow("string") << "({a: 'b'})" << "{\"a\":\"b\"}"; + QTest::newRow("number") << "({c: 42})" << "{\"c\":42}"; + QTest::newRow("boolean") << "({d: true})" << "{\"d\":true}"; + QTest::newRow("key is array") << "({[[12, 34]]: 56})" << "{\"12,34\":56}"; + QTest::newRow("value is date") << "({d: new Date('2000-01-20T12:00:00.000Z')})" << "{\"d\":\"2000-01-20T12:00:00.000Z\"}"; +} + +void tst_QJSEngine::JSON_Stringify() +{ + QFETCH(QString, object); + QFETCH(QString, json); + + QJSEngine eng; + + QJSValue obj = eng.evaluate(object); + QVERIFY(obj.isObject()); + + QJSValue func = eng.evaluate("(function(obj) { return JSON.stringify(obj); })"); + QVERIFY(func.isCallable()); + + QJSValue ret = func.call(QJSValueList{obj}); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), json); +} + +void tst_QJSEngine::JSON_Stringify_WithReplacer_QTBUG_95324() +{ + QJSEngine eng; + QJSValue json = eng.evaluate(R"( + function replacer(k, v) { + if (this[k] instanceof Date) { + return Math.floor(this[k].getTime() / 1000.0); + } + return v; + } + const obj = {d: new Date('2000-01-20T12:00:00.000Z')}; + JSON.stringify(obj, replacer); + )"); + QVERIFY(json.isString()); + QCOMPARE(json.toString(), QString::fromLatin1("{\"d\":948369600}")); +} + void tst_QJSEngine::arraySort() { // tests that calling Array.sort with a bad sort function doesn't cause issues @@ -3480,9 +3664,6 @@ void tst_QJSEngine::qRegularExpressionExport() // effect at a given date (QTBUG-9770). void tst_QJSEngine::dateRoundtripJSQtJS() { -#ifdef Q_OS_WIN - QSKIP("This test fails on Windows due to a bug in QDateTime."); -#endif qint64 secs = QDate(2009, 1, 1).startOfDay(QTimeZone::UTC).toSecsSinceEpoch(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { @@ -3497,9 +3678,6 @@ void tst_QJSEngine::dateRoundtripJSQtJS() void tst_QJSEngine::dateRoundtripQtJSQt() { -#ifdef Q_OS_WIN - QSKIP("This test fails on Windows due to a bug in QDateTime."); -#endif QDateTime qtDate = QDate(2009, 1, 1).startOfDay(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { @@ -3513,9 +3691,6 @@ void tst_QJSEngine::dateRoundtripQtJSQt() void tst_QJSEngine::dateConversionJSQt() { -#ifdef Q_OS_WIN - QSKIP("This test fails on Windows due to a bug in QDateTime."); -#endif qint64 secs = QDate(2009, 1, 1).startOfDay(QTimeZone::UTC).toSecsSinceEpoch(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { @@ -3740,7 +3915,7 @@ void tst_QJSEngine::prototypeChainGc() QJSValue factory = engine.evaluate("(function() { return Object.create(Object.create({})); })"); QVERIFY(factory.isCallable()); QJSValue obj = factory.call(); - engine.collectGarbage(); + gc(*engine.handle()); QJSValue proto = getProto.call(QJSValueList() << obj); proto = getProto.call(QJSValueList() << proto); @@ -3759,7 +3934,7 @@ void tst_QJSEngine::prototypeChainGc_QTBUG38299() "delete mapping.prop1\n" "\n"); // Don't hang! - engine.collectGarbage(); + gc(*engine.handle()); } void tst_QJSEngine::dynamicProperties() @@ -4403,6 +4578,12 @@ void tst_QJSEngine::tracing() QTest::ignoreMessage(QtDebugMsg, "a (:1)\nb (:1)\nc (:1)\n%entry (:1)"); engine.evaluate("function a() { console.trace(); } function b() { a(); } function c() { b(); }"); engine.evaluate("c()"); + + QQmlTestMessageHandler messageHandler; + messageHandler.setIncludeCategoriesEnabled(true); + engine.evaluate("c()"); + QCOMPARE(messageHandler.messageString(), + QLatin1String("js: a (:1)\nb (:1)\nc (:1)\n%entry (:1)")); } void tst_QJSEngine::asserts() @@ -4497,7 +4678,6 @@ void tst_QJSEngine::privateMethods() } QVERIFY(privateMethods.contains("myPrivateMethod")); - QVERIFY(privateMethods.contains("_q_reregisterTimers")); privateMethods << QStringLiteral("deleteLater") << QStringLiteral("destroyed"); QJSValueIterator it(jsWrapper); @@ -4571,7 +4751,7 @@ public: bool called = false; - Q_INVOKABLE void callMe(QQmlV4Function *) { + Q_INVOKABLE void callMe(QQmlV4FunctionPtr) { called = true; } }; @@ -5749,6 +5929,38 @@ void tst_QJSEngine::callElement() QCOMPARE(engine.evaluate(program).toString(), u"a"_s); } +void tst_QJSEngine::functionCtorGeneratedCUIsNotCollectedByGc() +{ + QJSEngine engine; + auto v4 = engine.handle(); + QVERIFY(!v4->isGCOngoing); + + // run gc until roots are collected + // we run the gc steps manually, so use "Forever" as the dealine to avoid interference + v4->memoryManager->gcStateMachine->deadline = QDeadlineTimer(QDeadlineTimer::Forever); + auto sm = v4->memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::InitMarkPersistentValues) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + + const QString program = "new Function('a', 'b', 'let x = \"Hello\"; return a + b');"; + auto sumFunc = engine.evaluate(program); + QVERIFY(sumFunc.isCallable()); + auto *function = QJSValuePrivate::asManagedType<QV4::JavaScriptFunctionObject>(&sumFunc); + auto *cu = function->d()->function->executableCompilationUnit(); + QVERIFY(cu->runtimeStrings); // should exist for "Hello" + QVERIFY(cu->runtimeStrings[0]->isMarked()); + while (sm->state != QV4::GCState::Invalid) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + + auto sum = sumFunc.call({QJSValue(12), QJSValue(13)}); + QCOMPARE(sum.toInt(), 25); +} + void tst_QJSEngine::tdzViolations_data() { QTest::addColumn<QString>("type"); @@ -6028,6 +6240,184 @@ void tst_QJSEngine::spreadNoOverflow() QCOMPARE(result.errorType(), QJSValue::RangeError); } +void tst_QJSEngine::symbolToVariant() +{ + QJSEngine engine; + const QJSValue val = engine.newSymbol("asymbol"); + QCOMPARE(val.toVariant(), QStringLiteral("Symbol(asymbol)")); + + const QVariant retained = val.toVariant(QJSValue::RetainJSObjects); + QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>()); + QVERIFY(retained.value<QJSValue>().strictlyEquals(val)); + + QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)")); +} + +class PACHelper : public QObject { + Q_OBJECT +public: + Q_INVOKABLE bool shExpMatch(const QString &, const QString &) { return false; } + Q_INVOKABLE QString dnsResolve(const QString &) { return QString{}; } +}; + +class ProxyAutoConf { +public: + void exposeQObjectMethodsAsGlobal(QJSEngine *engine, QObject *object) + { + QJSValue helper = engine->newQObject(object); + QJSValue g = engine->globalObject(); + QJSValueIterator it(helper); + while (it.hasNext()) { + it.next(); + if (!it.value().isCallable()) + continue; + g.setProperty(it.name(), it.value()); + } + } + + bool parse(const QString & pacBytes) + { + jsFindProxyForURL = QJSValue(); + engine = std::make_unique<QJSEngine>(); + exposeQObjectMethodsAsGlobal(engine.get(), new PACHelper); + engine->evaluate(pacBytes); + jsFindProxyForURL = engine->globalObject().property(QStringLiteral("FindProxyForURL")); + return true; + } + + QString findProxyForUrl(const QString &url, const QString &host) + { + QJSValueList args; + args << url << host; + gc(*engine->handle(), GCFlags::DontSendPostedEvents); + QJSValue callResult = jsFindProxyForURL.call(args); + return callResult.toString().trimmed(); + } + +private: + std::unique_ptr<QJSEngine> engine; + QJSValue jsFindProxyForURL; +}; + +QString const pacstring = R"js( +function FindProxyForURL(host) { + list_split_all = Array( + "oneoneoneoneo.oneo.oneo.oneoneo.one", + "twotwotwotwotw.otwo.twot.wotwotw.otw", + "threethreethr.eeth.reet.hreethr.eet", + "fourfourfourfo.urfo.urfo.urfourf.our", + "fivefivefivef.ivef.ivef.ivefive.fiv", + "sixsixsixsixsi.xsix.sixs.ixsixsi.xsi", + "sevensevenseve.nsev.ense.venseve.nse", + "eight.eighteigh.tei", + "*.nin.eninen.ine" + ) + list_myip_direct = + "10.254.0.0/255.255.0.0" + for (i = 0; i < list_split_all.length; ++i) + for (j = 0; j < list_myip_direct.length; ++j) + shExpMatch(host, list_split_all) + shExpMatch() + dnsResolve()} +)js"; + +void tst_QJSEngine::garbageCollectedObjectMethodBase() +{ + ProxyAutoConf proxyConf; + bool pac_read = false; + + const auto processUrl = [&](QString const &url, QString const &host) + { + if (!pac_read) { + proxyConf.parse(pacstring); + pac_read = true; + } + return proxyConf.findProxyForUrl(url, host); + }; + + const QString url = QStringLiteral("https://servername.domain.test"); + const QString host = QStringLiteral("servername.domain.test"); + + for (size_t i = 0; i < 5; ++i) { + auto future = std::async(processUrl, url, host); + QCOMPARE(future.get(), QLatin1String("Error: Insufficient arguments")); + } +} + +void tst_QJSEngine::optionalChainWithElementLookup() +{ + QJSEngine engine; + + const QString program = R"js( + (function(xxx) { return xxx?.title["en"] ?? "A" }) + )js"; + + QJSManagedValue func = QJSManagedValue(engine.evaluate(program), &engine); + QVERIFY(func.isFunction()); + + QCOMPARE(func.call({QJSValue::NullValue}).toString(), "A"); + QCOMPARE(func.call({QJSValue::UndefinedValue}).toString(), "A"); + + const QJSValue nice + = engine.toScriptValue(QVariantMap { {"title", QVariantMap { {"en", "B"} } } }); + QCOMPARE(func.call({nice}).toString(), "B"); + + const QJSValue naughty1 + = engine.toScriptValue(QVariantMap { {"title", QVariantMap { {"fr", "B"} } } }); + QCOMPARE(func.call({naughty1}).toString(), "A"); + + const QJSValue naughty2 + = engine.toScriptValue(QVariantMap { {"foos", QVariantMap { {"en", "B"} } } }); + QVERIFY(func.call({naughty2}).isUndefined()); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), "TypeError: Cannot read property 'en' of undefined"); + QVERIFY(!engine.hasError()); + + QVERIFY(func.call({ QJSValue(4) }).isUndefined()); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), "TypeError: Cannot read property 'en' of undefined"); + QVERIFY(!engine.hasError()); +} + +void tst_QJSEngine::deleteDefineCycle() +{ + QJSEngine engine; + QStringList stackTrace; + + QJSValue result = engine.evaluate(QString::fromLatin1(R"( + let global = ({}) + + for (let j = 0; j < 1000; j++) { + for (let i = 0; i < 2; i++) { + const name = "test" + i + delete global[name] + Object.defineProperty(global, name, { get() { return 0 }, configurable: true }) + } + } + )"), {}, 1, &stackTrace); + QVERIFY(stackTrace.isEmpty()); +} + +void tst_QJSEngine::deleteFromSparseArray() +{ + QJSEngine engine; + + // Should not crash + const QJSValue result = engine.evaluate(QLatin1String(R"((function() { + let o = []; + o[10000] = 10; + o[20000] = 20; + for (let k in o) + delete o[k]; + return o; + })())")); + + QVERIFY(result.isArray()); + QCOMPARE(result.property("length").toNumber(), 20001); + QVERIFY(result.property(10000).isUndefined()); + QVERIFY(result.property(20000).isUndefined()); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt index 7400bc5f1f..3bc5c39bf9 100644 --- a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt +++ b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qjsmanagedvalue Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjsmanagedvalue LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qjsmanagedvalue SOURCES tst_qjsmanagedvalue.cpp tst_qjsmanagedvalue.h diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp index f11ae73a59..35f5bbadc4 100644 --- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp +++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_qjsmanagedvalue.h" @@ -542,7 +542,7 @@ void tst_QJSManagedValue::toVariant() } - // array + // variant list { auto handler = qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &) { if (type == QtMsgType::QtWarningMsg) @@ -553,12 +553,10 @@ void tst_QJSManagedValue::toVariant() QVariantList listIn; listIn << 123 << QStringLiteral("hello"); QJSManagedValue array(eng.toManagedValue(listIn)); - QVERIFY(array.isArray()); QCOMPARE(array.property(QStringLiteral("length")).toInt(), 2); QVariant retained = array.toVariant(); - QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>()); - QVERIFY(QJSManagedValue(retained.value<QJSValue>(), &eng).strictlyEquals(array)); + QCOMPARE(retained.metaType(), QMetaType::fromType<QVariantList>()); QVariantList listOut = retained.toList(); QCOMPARE(listOut.size(), listIn.size()); @@ -566,7 +564,6 @@ void tst_QJSManagedValue::toVariant() QCOMPARE(listOut.at(i), listIn.at(i)); // round-trip conversion QJSManagedValue array2(eng.toManagedValue(retained)); - QVERIFY(array2.isArray()); QCOMPARE(array2.property(QStringLiteral("length")).toInt(), array.property(QStringLiteral("length")).toInt()); for (int i = 0; i < array.property(QStringLiteral("length")).toInt(); ++i) QVERIFY(array2.property(i).strictlyEquals(array.property(i))); @@ -1525,8 +1522,6 @@ void tst_QJSManagedValue::strictlyEquals() { QJSManagedValue var1(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a")))); QJSManagedValue var2(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a")))); - QVERIFY(var1.isArray()); - QVERIFY(var2.isArray()); QVERIFY(!var1.strictlyEquals(var2)); } { @@ -1582,22 +1577,22 @@ void tst_QJSManagedValue::castToPointer() void tst_QJSManagedValue::engineDeleted() { - QJSEngine *eng = new QJSEngine; + std::unique_ptr<QJSEngine> eng = std::make_unique<QJSEngine>(); QObject *temp = new QObject(); // Owned by JS engine, as newQObject() sets JS ownership explicitly QJSManagedValue v1(eng->toManagedValue(123)); QCOMPARE(v1.type(), QJSManagedValue::Number); QJSManagedValue v2(eng->toManagedValue(QStringLiteral("ciao"))); QCOMPARE(v2.type(), QJSManagedValue::String); - QJSManagedValue v3(eng->newObject(), eng); + QJSManagedValue v3(eng->newObject(), eng.get()); QCOMPARE(v3.type(), QJSManagedValue::Object); QVERIFY(!v3.isNull()); - QJSManagedValue v4(eng->newQObject(temp), eng); + QJSManagedValue v4(eng->newQObject(temp), eng.get()); QCOMPARE(v4.type(), QJSManagedValue::Object); QVERIFY(!v4.isNull()); - QJSManagedValue v5(QStringLiteral("Hello"), eng); + QJSManagedValue v5(QStringLiteral("Hello"), eng.get()); QCOMPARE(v2.type(), QJSManagedValue::String); - delete eng; + eng.reset(); // You can still check the type, but anything involving the engine is obviously prohibited. QCOMPARE(v1.type(), QJSManagedValue::Undefined); @@ -1742,7 +1737,7 @@ void tst_QJSManagedValue::stringByIndex() const QString testString = QStringLiteral("foobar"); QJSManagedValue str(testString, &engine); - for (uint i = 0; i < testString.size(); ++i) { + for (qsizetype i = 0; i < testString.size(); ++i) { QVERIFY(str.hasOwnProperty(i)); QVERIFY(str.hasProperty(i)); diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h index 6f6add433e..bb5a6be3ca 100644 --- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h +++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TST_QJSMANAGEDVALUE_H #define TST_QJSMANAGEDVALUE_H diff --git a/tests/auto/qml/qjsonbinding/CMakeLists.txt b/tests/auto/qml/qjsonbinding/CMakeLists.txt index 66f3e94e4c..fa80e764ef 100644 --- a/tests/auto/qml/qjsonbinding/CMakeLists.txt +++ b/tests/auto/qml/qjsonbinding/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qjsonbinding Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjsonbinding LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp index 6235ff376d..450560833f 100644 --- a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp +++ b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQml/QtQml> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -67,7 +67,8 @@ private: QByteArray tst_qjsonbinding::readAsUtf8(const QString &fileName) { QFile file(testFile(fileName)); - file.open(QIODevice::ReadOnly); + if (!file.open(QIODevice::ReadOnly)) + qFatal("Cannot open file %s", qPrintable(fileName)); QTextStream stream(&file); return stream.readAll().trimmed().toUtf8(); } @@ -144,7 +145,11 @@ void tst_qjsonbinding::cppJsConversion() { QJSValue jsValue = eng.toScriptValue(jsonValue); +#if QT_DEPRECATED_SINCE(6, 9) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QVERIFY(!jsValue.isVariant()); + QT_WARNING_POP +#endif switch (jsonValue.type()) { case QJsonValue::Null: QVERIFY(jsValue.isNull()); @@ -193,7 +198,6 @@ void tst_qjsonbinding::cppJsConversion() if (jsonValue.isObject()) { QJsonObject jsonObject = jsonValue.toObject(); QJSValue jsObject = eng.toScriptValue(jsonObject); - QVERIFY(!jsObject.isVariant()); QVERIFY(jsObject.isObject()); QJSValue stringified = stringify.call(QJSValueList() << jsObject); @@ -205,7 +209,6 @@ void tst_qjsonbinding::cppJsConversion() } else if (jsonValue.isArray()) { QJsonArray jsonArray = jsonValue.toArray(); QJSValue jsArray = eng.toScriptValue(jsonArray); - QVERIFY(!jsArray.isVariant()); QVERIFY(jsArray.isArray()); QJSValue stringified = stringify.call(QJSValueList() << jsArray); diff --git a/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt b/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt index 18b3ee938c..c709a5aa56 100644 --- a/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt +++ b/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qjsprimitivevalue Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjsprimitivevalue LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qjsprimitivevalue SOURCES tst_qjsprimitivevalue.cpp diff --git a/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp b/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp index 7609842f17..232200e633 100644 --- a/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp +++ b/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qobject.h> #include <QtQml/qjsengine.h> @@ -19,6 +19,7 @@ private slots: void unaryOperators(); void toFromVariant(); + void coercion(); void ctor_invalid(); void ctor_undefinedWithEngine(); @@ -313,25 +314,37 @@ void tst_QJSPrimitiveValue::toFromVariant() switch (operand.type()) { case QJSPrimitiveValue::Undefined: QVERIFY(!var.isValid()); + QCOMPARE(operand.metaType(), QMetaType()); + QCOMPARE(operand.data(), nullptr); break; case QJSPrimitiveValue::Null: QCOMPARE(var.typeId(), QMetaType::Nullptr); + QCOMPARE(operand.metaType(), QMetaType::fromType<std::nullptr_t>()); + QCOMPARE(operand.data(), nullptr); break; case QJSPrimitiveValue::Boolean: QCOMPARE(var.typeId(), QMetaType::Bool); QCOMPARE(var.toBool(), operand.toBoolean()); + QCOMPARE(operand.metaType(), QMetaType::fromType<bool>()); + QCOMPARE(*static_cast<const bool *>(operand.data()), operand.toBoolean()); break; case QJSPrimitiveValue::Integer: QCOMPARE(var.typeId(), QMetaType::Int); QCOMPARE(var.toInt(), operand.toInteger()); + QCOMPARE(operand.metaType(), QMetaType::fromType<int>()); + QCOMPARE(*static_cast<const int *>(operand.data()), operand.toInteger()); break; case QJSPrimitiveValue::Double: QCOMPARE(var.typeId(), QMetaType::Double); QCOMPARE(var.toDouble(), operand.toDouble()); + QCOMPARE(operand.metaType(), QMetaType::fromType<double>()); + QCOMPARE(*static_cast<const double *>(operand.data()), operand.toDouble()); break; case QJSPrimitiveValue::String: QCOMPARE(var.typeId(), QMetaType::QString); QCOMPARE(var.toString(), operand.toString()); + QCOMPARE(operand.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(*static_cast<const QString *>(operand.data()), operand.toString()); break; } @@ -344,6 +357,25 @@ void tst_QJSPrimitiveValue::toFromVariant() } } +void tst_QJSPrimitiveValue::coercion() +{ + for (const QJSPrimitiveValue &operand : operands) { + QCOMPARE(operand.to<QJSPrimitiveValue::Undefined>(), QJSPrimitiveUndefined()); + QCOMPARE(operand.to<QJSPrimitiveValue::Null>(), QJSPrimitiveNull()); + QCOMPARE(operand.to<QJSPrimitiveValue::Boolean>(), operand.toBoolean()); + QCOMPARE(operand.to<QJSPrimitiveValue::Integer>(), operand.toInteger()); + QCOMPARE(operand.to<QJSPrimitiveValue::String>(), operand.toString()); + + const QJSPrimitiveValue lhs = operand.to<QJSPrimitiveValue::Double>(); + QCOMPARE(lhs.type(), QJSPrimitiveValue::Double); + const double rhs = operand.toDouble(); + if (std::isnan(rhs)) + QVERIFY(std::isnan(lhs.toDouble())); + else + QCOMPARE(lhs.toDouble(), rhs); + } +} + void tst_QJSPrimitiveValue::ctor_invalid() { QJSPrimitiveValue v; diff --git a/tests/auto/qml/qjsvalue/CMakeLists.txt b/tests/auto/qml/qjsvalue/CMakeLists.txt index fcd51fff44..212109c878 100644 --- a/tests/auto/qml/qjsvalue/CMakeLists.txt +++ b/tests/auto/qml/qjsvalue/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qjsvalue Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjsvalue LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # special case begin # TODO: Prepare for removal, once Platform brings in Threads. if(NOT TARGET Threads::Threads) diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 516abed9e8..6a10b5910d 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_qjsvalue.h" @@ -19,11 +19,6 @@ tst_QJSValue::tst_QJSValue() { } -tst_QJSValue::~tst_QJSValue() -{ - delete engine; -} - void tst_QJSValue::ctor_invalid() { QJSEngine eng; @@ -369,10 +364,11 @@ void tst_QJSValue::toString() // variant should use internal valueOf(), then fall back to QVariant::toString(), // then fall back to "QVariant(typename)" QJSValue variant = eng.toScriptValue(QPoint(10, 20)); + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QVERIFY(!variant.isVariant()); + QT_WARNING_POP QCOMPARE(variant.toString(), QString::fromLatin1("QPoint(10, 20)")); variant = eng.toScriptValue(QUrl()); - QVERIFY(!variant.isVariant()); QVERIFY(variant.isUrl()); QVERIFY(variant.toString().isEmpty()); @@ -1085,13 +1081,8 @@ void tst_QJSValue::toVariant() QVariantList listIn; listIn << 123 << "hello"; QJSValue array = eng.toScriptValue(listIn); - QVERIFY(array.isArray()); QCOMPARE(array.property("length").toInt(), 2); - QVariant retained = array.toVariant(QJSValue::RetainJSObjects); - QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>()); - QVERIFY(retained.value<QJSValue>().strictlyEquals(array)); - QVariant ret = array.toVariant(); QCOMPARE(ret.typeId(), QMetaType::QVariantList); QVariantList listOut = ret.toList(); @@ -1100,7 +1091,6 @@ void tst_QJSValue::toVariant() QCOMPARE(listOut.at(i), listIn.at(i)); // round-trip conversion QJSValue array2 = eng.toScriptValue(ret); - QVERIFY(array2.isArray()); QCOMPARE(array2.property("length").toInt(), array.property("length").toInt()); for (int i = 0; i < array.property("length").toInt(); ++i) QVERIFY(array2.property(i).strictlyEquals(array.property(i))); @@ -1124,6 +1114,25 @@ void tst_QJSValue::toVariant() QCOMPARE(func.toVariant().metaType(), QMetaType::fromType<QJSValue>()); } + + // object with custom prototype + { + QJSValue object = eng.evaluate(R"js( + (function(){ + function Person(firstName, lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + return new Person("John", "Doe"); + })(); + )js"); + QVERIFY(object.isObject()); + auto asVariant = object.toVariant(); + QCOMPARE(asVariant.metaType(), QMetaType::fromType<QVariantMap>()); + auto variantMap = asVariant.value<QVariantMap>(); + QVERIFY(variantMap.contains("firstName")); + QCOMPARE(variantMap["firstName"].toString(), "John"); + } } void tst_QJSValue::toPrimitive_data() @@ -2345,8 +2354,6 @@ void tst_QJSValue::strictlyEquals() { QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a")); QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a")); - QVERIFY(var1.isArray()); - QVERIFY(var2.isArray()); QVERIFY(!var1.strictlyEquals(var2)); } { @@ -2586,7 +2593,7 @@ void tst_QJSValue::prettyPrinter() void tst_QJSValue::engineDeleted() { - QJSEngine *eng = new QJSEngine; + std::unique_ptr<QJSEngine> eng = std::make_unique<QJSEngine>(); QObject *temp = new QObject(); // Owned by JS engine, as newQObject() sets JS ownership explicitly QJSValue v1 = eng->toScriptValue(123); QVERIFY(v1.isNumber()); @@ -2599,7 +2606,7 @@ void tst_QJSValue::engineDeleted() QJSValue v5 = "Hello"; QVERIFY(v2.isString()); - delete eng; + eng.reset(); QVERIFY(!v1.isUndefined()); // Primitive value is stored inline QVERIFY(v2.isUndefined()); @@ -2810,6 +2817,7 @@ void tst_QJSValue::deleteFromDifferentThread() thread->start(); condition.wait(&mutex); QTRY_VERIFY(thread->isFinished()); + storage.clearFreePageHint(); QTRY_COMPARE(storage.firstPage, nullptr); #endif } diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h index 5b77bc2e15..459c0535c0 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TST_QJSVALUE_H #define TST_QJSVALUE_H @@ -16,7 +16,6 @@ class tst_QJSValue : public QObject public: tst_QJSValue(); - virtual ~tst_QJSValue(); private slots: void ctor_invalid(); @@ -129,11 +128,9 @@ private slots: private: void newEngine() { - if (engine) - delete engine; - engine = new QJSEngine(); + engine = std::make_unique<QJSEngine>(); } - QJSEngine *engine; + std::unique_ptr<QJSEngine> engine; }; #endif diff --git a/tests/auto/qml/qjsvalueiterator/CMakeLists.txt b/tests/auto/qml/qjsvalueiterator/CMakeLists.txt index 85ac6c7a58..961674e50d 100644 --- a/tests/auto/qml/qjsvalueiterator/CMakeLists.txt +++ b/tests/auto/qml/qjsvalueiterator/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qjsvalueiterator Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjsvalueiterator LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qjsvalueiterator SOURCES tst_qjsvalueiterator.cpp diff --git a/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp b/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp index e329cf948f..a671bf42e2 100644 --- a/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp +++ b/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -447,7 +447,7 @@ void tst_QJSValueIterator::iterateNonObject() void tst_QJSValueIterator::iterateOverObjectFromDeletedEngine() { - QJSEngine *engine = new QJSEngine; + std::unique_ptr<QJSEngine> engine = std::make_unique<QJSEngine>(); QJSValue objet = engine->newObject(); // populate object with properties @@ -465,7 +465,7 @@ void tst_QJSValueIterator::iterateOverObjectFromDeletedEngine() it.next(); QVERIFY(properties.contains(it.name())); - delete engine; + engine.reset(); QVERIFY(objet.isUndefined()); QVERIFY(it.name().isEmpty()); diff --git a/tests/auto/qml/qml/CMakeLists.txt b/tests/auto/qml/qml/CMakeLists.txt index a696d80c4d..39056384de 100644 --- a/tests/auto/qml/qml/CMakeLists.txt +++ b/tests/auto/qml/qml/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qml LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -15,4 +21,6 @@ qt_internal_add_test(tst_qml QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" LIBRARIES Qt::QuickTestUtilsPrivate + Qt::Gui + Qt::GuiPrivate ) diff --git a/tests/auto/qml/qml/data/resizeItem.qml b/tests/auto/qml/qml/data/resizeItem.qml new file mode 100644 index 0000000000..c416451ccc --- /dev/null +++ b/tests/auto/qml/qml/data/resizeItem.qml @@ -0,0 +1,28 @@ +import QtQuick + +Rectangle { + id: rect + color: "green" + + Timer { + id: exitTimer + running: false + onTriggered: Qt.quit() + } + + Timer { + id: resizeTimer + running: false + onTriggered: { + rect.width = 100 + rect.height = 50 + exitTimer.start() + } + } + + Window.onHeightChanged: { + if (rect.Window.width > 0) + console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height) + resizeTimer.start() + } +} diff --git a/tests/auto/qml/qml/data/sizedItem.qml b/tests/auto/qml/qml/data/sizedItem.qml new file mode 100644 index 0000000000..edcb0e8629 --- /dev/null +++ b/tests/auto/qml/qml/data/sizedItem.qml @@ -0,0 +1,19 @@ +import QtQuick + +Rectangle { + id: rect + color: "blue" + width: 200; height: 150 + + Timer { + id: exitTimer + running: false + onTriggered: Qt.quit() + } + + Window.onHeightChanged: { + if (rect.Window.width > 0) + console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height) + exitTimer.restart() + } +} diff --git a/tests/auto/qml/qml/data/unsizedItem.qml b/tests/auto/qml/qml/data/unsizedItem.qml new file mode 100644 index 0000000000..e32784762f --- /dev/null +++ b/tests/auto/qml/qml/data/unsizedItem.qml @@ -0,0 +1,18 @@ +import QtQuick + +Rectangle { + id: rect + color: "green" + + Timer { + id: exitTimer + running: false + onTriggered: Qt.quit() + } + + Window.onHeightChanged: { + if (rect.Window.width > 0) + console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height) + exitTimer.restart() + } +} diff --git a/tests/auto/qml/qml/tst_qml.cpp b/tests/auto/qml/qml/tst_qml.cpp index de884d8257..befc68d443 100644 --- a/tests/auto/qml/qml/tst_qml.cpp +++ b/tests/auto/qml/qml/tst_qml.cpp @@ -1,11 +1,16 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/qtest.h> #include <QtCore/qlibraryinfo.h> #include <QtCore/qprocess.h> +#include <QtCore/qloggingcategory.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> #include <QtQuickTestUtils/private/qmlutils_p.h> +Q_LOGGING_CATEGORY(lcQml, "qt.qml.tests"); + class tst_qml : public QQmlDataTest { Q_OBJECT @@ -15,6 +20,8 @@ public: private slots: void initTestCase() override; void nonWindow(); + void itemAndWindowGeometry_data(); + void itemAndWindowGeometry(); private: QString qmlPath; @@ -42,6 +49,124 @@ void tst_qml::nonWindow() QCOMPARE(qml.exitCode(), 0); // Should not exit with code 2 } +void tst_qml::itemAndWindowGeometry_data() +{ + QTest::addColumn<QString>("config"); + QTest::addColumn<QString>("geometry"); + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<QSize>("expectedWindowSize"); + QTest::addColumn<QSize>("expectedContentSize"); + + const QString none; // empty string + + auto sizeOrInvalid = [](int w, int h) { + static const bool wm = QGuiApplicationPrivate::platformIntegration()-> + hasCapability(QPlatformIntegration::WindowManagement); + return wm ? QSize(w, h) : QSize(); + }; + + QTest::newRow("default: unsized") + << none << none << "unsizedItem.qml" + << QSize() << QSize(); // default size depends on window system + QTest::newRow("default: unsized with geometry") + << none << "100x100+50+50" << "unsizedItem.qml" + << sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100); + QTest::newRow("resizeToItem: unsized") + << "resizeToItem" << none << "unsizedItem.qml" + << QSize() << QSize(0, 0); + QTest::newRow("resizeToItem: unsized with geometry") + << "resizeToItem" << "100x100+50+50" << "unsizedItem.qml" + << sizeOrInvalid(100, 100) << QSize(0, 0); + + QTest::newRow("default: sized") + << none << none << "sizedItem.qml" + << QSize() << QSize(); + QTest::newRow("default: sized with geometry") + << none << "100x100+50+50" << "sizedItem.qml" + << sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100); + QTest::newRow("resizeToItem: sized") + << "resizeToItem" << none << "sizedItem.qml" + << sizeOrInvalid(200, 150) << sizeOrInvalid(200, 150); + QTest::newRow("resizeToItem: sized with geometry") + << "resizeToItem" << "320x240+50+50" << "sizedItem.qml" + << sizeOrInvalid(320, 240) << QSize(200, 150); + + QTest::newRow("default: resizing") + << none << none << "resizeItem.qml" + << QSize() << QSize(); + QTest::newRow("default: resizing with geometry") + << none << "100x100+50+50" << "resizeItem.qml" + << sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100); + QTest::newRow("resizeToItem: resizing") + << "resizeToItem" << none << "resizeItem.qml" + << sizeOrInvalid(100, 50) << sizeOrInvalid(100, 50); + QTest::newRow("resizeToItem: resizing with geometry") + << "resizeToItem" << "320x240+50+50" << "resizeItem.qml" + << sizeOrInvalid(100, 50) << sizeOrInvalid(100, 50); +} + +/* + - A root Item will get put into a Window depending on config (implementations in + tools/qml/ResizeItemToWindow.qml and ResizeWindowToItem.qml). + - The window system will enforce a minimum size. + - In the default configuration, the root Item should then get resized to fit + (QTBUG-114068 / QTBUG-116753). + - In resizeToItem configuration, if the item width/height are not set, the window would + try to be 0x0, but the window system won't allow it. + - This also tests the `--qwindowgeometry` argument: with the default config, the + item should be resized to fit, but not with `-c resizeToItem`. +*/ +void tst_qml::itemAndWindowGeometry() +{ +#ifdef Q_OS_WIN + QSKIP("console.info does not go to stderr on Windows."); +#endif + + QFETCH(QString, config); + QFETCH(QString, geometry); + QFETCH(QString, qmlfile); + QFETCH(QSize, expectedWindowSize); + QFETCH(QSize, expectedContentSize); + + QStringList args; + if (!config.isEmpty()) + args << "-c" << config; + if (!geometry.isEmpty()) + args << "--qwindowgeometry" << geometry; + args << testFile(qmlfile); + QProcess qml; + qml.start(qmlPath, args); + QVERIFY(qml.waitForFinished()); + QCOMPARE(qml.exitStatus(), QProcess::NormalExit); + const QByteArray output = qml.readAllStandardError(); + const auto sizeLineIndex = output.lastIndexOf("window"); + QCOMPARE_GE(sizeLineIndex, 0); + const auto newlineIndex = output.indexOf('\n', sizeLineIndex); + QCOMPARE_GT(newlineIndex, sizeLineIndex); + // expect a line like "window 120 120 content 120 120" + const auto sizes = output.sliced(sizeLineIndex, newlineIndex - sizeLineIndex).split(' '); + QCOMPARE_GE(sizes.size(), 6); + QCOMPARE(sizes[0], "window"); + QCOMPARE(sizes[3], "content"); + const QSize windowSize(sizes[1].toInt(), sizes[2].toInt()); + const QSize contentSize(sizes[4].toInt(), sizes[5].toInt()); + qCDebug(lcQml) << sizes + << "window" << windowSize << "expect" << expectedWindowSize + << "content" << contentSize << "expect" << expectedContentSize; + QVERIFY(!windowSize.isEmpty()); + if (config != "resizeToItem") { + // default config: + // ResizeItemToWindow.qml should have resized the item to its window + QCOMPARE(contentSize, windowSize); + } + // windowSize can be off-by-one on hidpi (e.g. QT_SCALE_FACTOR=2 on xcb); + // perhaps that's a bug somewhere, but so far we aren't testing hidpi on CI + if (expectedWindowSize.isValid()) + QCOMPARE(windowSize, expectedWindowSize); + if (expectedContentSize.isValid()) + QCOMPARE(contentSize, expectedContentSize); +} + QTEST_MAIN(tst_qml) #include <tst_qml.moc> diff --git a/tests/auto/qml/qmlbasicapp/CMakeLists.txt b/tests/auto/qml/qmlbasicapp/CMakeLists.txt index 8328a6b3f4..add4c560a8 100644 --- a/tests/auto/qml/qmlbasicapp/CMakeLists.txt +++ b/tests/auto/qml/qmlbasicapp/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlbasicapp LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmlbasicapp SOURCES tst_qmlbasicapp.cpp diff --git a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp index 7f2736a76b..f3846569cb 100644 --- a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp +++ b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "timemodel.h" diff --git a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h index b42542be31..2cc8f2e756 100644 --- a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h +++ b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TIMEMODEL_H #define TIMEMODEL_H diff --git a/tests/auto/qml/qmlbasicapp/main.qml b/tests/auto/qml/qmlbasicapp/main.qml index 37598a5235..8c10b0881f 100644 --- a/tests/auto/qml/qmlbasicapp/main.qml +++ b/tests/auto/qml/qmlbasicapp/main.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import TimeExample2 // import types from the plugin import BasicExtension diff --git a/tests/auto/qml/qmlbasicapp/manual_imports.cpp b/tests/auto/qml/qmlbasicapp/manual_imports.cpp index 61df826af4..57adb572dd 100644 --- a/tests/auto/qml/qmlbasicapp/manual_imports.cpp +++ b/tests/auto/qml/qmlbasicapp/manual_imports.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlextensionplugin.h> diff --git a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp index fd72a0910f..3b9028154c 100644 --- a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp +++ b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QQmlEngine> #include <QQmlComponent> diff --git a/tests/auto/qml/qmlcachegen/CMakeLists.txt b/tests/auto/qml/qmlcachegen/CMakeLists.txt index d92d5e6166..d88de460e9 100644 --- a/tests/auto/qml/qmlcachegen/CMakeLists.txt +++ b/tests/auto/qml/qmlcachegen/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmlcachegen Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlcachegen LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -22,6 +28,7 @@ qt_internal_add_test(tst_qmlcachegen Qt::Gui Qt::QmlPrivate Qt::QuickTestUtilsPrivate + Qt::QmlCompilerPrivate TESTDATA ${test_data} ) diff --git a/tests/auto/qml/qmlcachegen/data/aotstats/AotstatsClean.qml b/tests/auto/qml/qmlcachegen/data/aotstats/AotstatsClean.qml new file mode 100644 index 0000000000..b48004dc87 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/aotstats/AotstatsClean.qml @@ -0,0 +1,11 @@ +import QtQml + +QtObject { + property int i: 100 + property int j: i * 2 + + function s() : string { + let s = "abc" + return s + "def " + } +} diff --git a/tests/auto/qml/qmlcachegen/data/aotstats/AotstatsMixed.qml b/tests/auto/qml/qmlcachegen/data/aotstats/AotstatsMixed.qml new file mode 100644 index 0000000000..3d96760e96 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/aotstats/AotstatsMixed.qml @@ -0,0 +1,7 @@ +import QtQml + +QtObject { + property int i: Math.max(1, 2) // OK + function f() { return 1 } // Error: No specified return type + property string s: g() // Error: g? +} diff --git a/tests/auto/qml/qmlcachegen/data/aotstats/cachegentest.qrc b/tests/auto/qml/qmlcachegen/data/aotstats/cachegentest.qrc new file mode 100644 index 0000000000..4f156ad2ef --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/aotstats/cachegentest.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/cachegentest"> + <file alias="qmldir">qmldir</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/data/aotstats/qmldir b/tests/auto/qml/qmlcachegen/data/aotstats/qmldir new file mode 100644 index 0000000000..72047cc99d --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/aotstats/qmldir @@ -0,0 +1,3 @@ +module cachegentest +AotstatsClean 254.0 AotstatsClean.qml +AotstatsMixed 254.0 AotstatsMixed.qml diff --git a/tests/auto/qml/qmlcachegen/data/truncateTest.qml b/tests/auto/qml/qmlcachegen/data/truncateTest.qml new file mode 100644 index 0000000000..fee768cf77 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/truncateTest.qml @@ -0,0 +1,764 @@ +import QtQuick 2.15 +import QtQuick.Layouts 2.15 + +Item { + width: 400 + height: 400 + + component Button : Item { + property string text + property Item background + } + + ColumnLayout { + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + Button { + text: "A Special Button" + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + color: "red" + border.color: "#26282a" + border.width: 1 + radius: 4 + } + } + + } +} diff --git a/tests/auto/qml/qmlcachegen/scriptstringprops.h b/tests/auto/qml/qmlcachegen/scriptstringprops.h index 521455c098..96c86f1c59 100644 --- a/tests/auto/qml/qmlcachegen/scriptstringprops.h +++ b/tests/auto/qml/qmlcachegen/scriptstringprops.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SCRIPT_STRING_PROPS_H #define SCRIPT_STRING_PROPS_H diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 60d4a5df32..34ad3bbc98 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -1,8 +1,9 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> +#include <QJsonDocument> #include <QQmlComponent> #include <QQmlEngine> #include <QProcess> @@ -11,6 +12,7 @@ #include <QSysInfo> #include <QLoggingCategory> #include <private/qqmlcomponent_p.h> +#include <private/qqmljscompilerstats_p.h> #include <private/qqmlscriptdata_p.h> #include <private/qv4compileddata_p.h> #include <qtranslator.h> @@ -63,8 +65,14 @@ private slots: void inlineComponent(); void posthocRequired(); + void gracefullyHandleTruncatedCacheFile(); + void scriptStringCachegenInteraction(); void saveableUnitPointer(); + + void aotstatsSerialization(); + void aotstatsGeneration_data(); + void aotstatsGeneration(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -349,7 +357,7 @@ void tst_qmlcachegen::signalHandlerParameters() }; QVERIFY(isStringIndexInStringTable(compilationUnit->objectAt(0)->signalAt(0)->parameterAt(0)->nameIndex)); - QVERIFY(!compilationUnit->dynamicStrings.isEmpty()); + QVERIFY(!compilationUnit->baseCompilationUnit()->dynamicStrings.isEmpty()); } } @@ -714,7 +722,8 @@ void tst_qmlcachegen::moduleScriptImport() { auto componentPrivate = QQmlComponentPrivate::get(&component); QVERIFY(componentPrivate); - auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit(); + auto compilationUnit = componentPrivate->compilationUnit->dependentScriptsPtr() + ->first()->compilationUnit(); QVERIFY(compilationUnit); auto unitData = compilationUnit->unitData(); QVERIFY(unitData); @@ -839,6 +848,23 @@ void tst_qmlcachegen::posthocRequired() qPrintable(component.errorString())); } +void tst_qmlcachegen::gracefullyHandleTruncatedCacheFile() +{ +#if defined(QTEST_CROSS_COMPILED) + QSKIP("Cannot call qmlcachegen on cross-compiled target."); +#endif + + bool ok = generateCache(testFile("truncateTest.qml")); + QVERIFY(ok); + const QString qmlcFile = testFile("truncateTest.qmlc"); + QVERIFY(QFile::exists(qmlcFile)); + QFile::resize(qmlcFile, QFileInfo(qmlcFile).size() / 2); + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, testFileUrl("truncateTest.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); +} + void tst_qmlcachegen::scriptStringCachegenInteraction() { #if defined(QTEST_CROSS_COMPILED) @@ -875,6 +901,133 @@ void tst_qmlcachegen::saveableUnitPointer() QCOMPARE(unit.flags, flags); } +void tst_qmlcachegen::aotstatsSerialization() +{ + const auto createEntry = [](const auto &d, const auto &n, const auto &e, const auto &l, + const auto &c, const auto &s) -> QQmlJS::AotStatsEntry { + QQmlJS::AotStatsEntry entry; + entry.codegenDuration = d; + entry.functionName = n; + entry.errorMessage = e; + entry.line = l; + entry.column = c; + entry.codegenSuccessful = s; + return entry; + }; + + const auto equal = [](const auto &e1, const auto &e2) -> bool { + return e1.codegenDuration == e2.codegenDuration && e1.functionName == e2.functionName + && e1.errorMessage == e2.errorMessage && e1.line == e2.line + && e1.column == e2.column && e1.codegenSuccessful == e2.codegenSuccessful; + }; + + // AotStats + // +-ModuleA + // | +-File1 + // | | +-e1 + // | | +-e2 + // | +-File2 + // | | +-e3 + // +-ModuleB + // | +-File3 + // | | +-e4 + + QQmlJS::AotStats original; + QQmlJS::AotStatsEntry e1 = createEntry(std::chrono::microseconds(500), "f1", "", 1, 1, true); + QQmlJS::AotStatsEntry e2 = createEntry(std::chrono::microseconds(200), "f2", "err1", 5, 4, false); + QQmlJS::AotStatsEntry e3 = createEntry(std::chrono::microseconds(750), "f3", "", 20, 4, true); + QQmlJS::AotStatsEntry e4 = createEntry(std::chrono::microseconds(300), "f4", "err2", 5, 8, false); + original.addEntry("ModuleA", "File1", e1); + original.addEntry("ModuleA", "File1", e2); + original.addEntry("ModuleA", "File2", e3); + original.addEntry("ModuleB", "File3", e4); + + const auto parsed = QQmlJS::AotStats::fromJsonDocument(original.toJsonDocument()); + QCOMPARE(parsed.entries().size(), original.entries().size()); + + const auto &parsedA = parsed.entries()["ModuleA"]; + const auto &originalA = original.entries()["ModuleA"]; + QCOMPARE(parsedA.size(), originalA.size()); + QCOMPARE(parsedA["File1"].size(), originalA["File1"].size()); + QVERIFY(equal(parsedA["File1"][0], originalA["File1"][0])); + QVERIFY(equal(parsedA["File1"][1], originalA["File1"][1])); + QCOMPARE(parsedA["File2"].size(), originalA["File2"].size()); + QVERIFY(equal(parsedA["File2"][0], originalA["File2"][0])); + + const auto &parsedB = parsed.entries()["ModuleB"]; + const auto &originalB = original.entries()["ModuleB"]; + QCOMPARE(parsedB.size(), originalB.size()); + QCOMPARE(parsedB["File3"].size(), originalB["File3"].size()); + QVERIFY(equal(parsedB["File3"][0], originalB["File3"][0])); +} + +struct FunctionEntry +{ + QString name; + QString errorMessage; + bool codegenSuccessful; +}; + +void tst_qmlcachegen::aotstatsGeneration_data() +{ + QTest::addColumn<QString>("qmlFile"); + QTest::addColumn<QList<FunctionEntry>>("entries"); + + QTest::addRow("clean") << "AotstatsClean.qml" + << QList<FunctionEntry>{ { "j", "", true }, { "s", "", true } }; + + const QString fError = "function without return type annotation returns int. This may prevent " + "proper compilation to Cpp."; + const QString sError = "method g cannot be resolved."; + QTest::addRow("mixed") << "AotstatsMixed.qml" + << QList<FunctionEntry>{ { "i", "", true }, + { "f", fError, false }, + { "s", sError, false } }; +} + +void tst_qmlcachegen::aotstatsGeneration() +{ +#if defined(QTEST_CROSS_COMPILED) + QSKIP("Cannot call qmlcachegen on cross-compiled target."); +#endif + QFETCH(QString, qmlFile); + QFETCH(QList<FunctionEntry>, entries); + + QTemporaryDir dir; + QProcess proc; + proc.setProgram(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + "/qmlcachegen"_L1); + const QString cppOutput = dir.filePath(qmlFile + ".cpp"); + const QString aotstatsOutput = cppOutput + ".aotstats"; + proc.setArguments({ "--bare", + "--resource-path", "/cachegentest/data/aotstats/" + qmlFile, + "-i", testFile("aotstats/qmldir"), + "--resource", testFile("aotstats/cachegentest.qrc"), + "--dump-aot-stats", + "--module-id=Aotstats", + "-o", cppOutput, + testFile("aotstats/" + qmlFile) }); + proc.start(); + QVERIFY(proc.waitForFinished() && proc.exitStatus() == QProcess::NormalExit); + + QVERIFY(QFileInfo::exists(aotstatsOutput)); + QFile aotstatsFile(aotstatsOutput); + QVERIFY(aotstatsFile.open(QIODevice::Text | QIODevice::ReadOnly)); + const auto document = QJsonDocument::fromJson(aotstatsFile.readAll()); + const auto aotstats = QQmlJS::AotStats::fromJsonDocument(document); + QVERIFY(aotstats.entries().size() == 1); // One module + const auto &moduleEntries = aotstats.entries()["Aotstats"]; + QVERIFY(moduleEntries.size() == 1); // Only one qml file was compiled + const auto &fileEntries = moduleEntries[moduleEntries.keys().first()]; + + for (const auto &entry : entries) { + const auto it = std::find_if(fileEntries.cbegin(), fileEntries.cend(), + [&](const auto &e) { return e.functionName == entry.name; }); + QVERIFY(it != fileEntries.cend()); + QVERIFY(it->codegenSuccessful == entry.codegenSuccessful); + QVERIFY(it->errorMessage == entry.errorMessage); + } +} + const QQmlScriptString &ScriptStringProps::undef() const { return m_undef; diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt index 8644e00cde..715ad6162a 100644 --- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlcppcodegen LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + add_subdirectory(data) qt_internal_add_test(tst_qmlcppcodegen @@ -8,9 +14,13 @@ qt_internal_add_test(tst_qmlcppcodegen tst_qmlcppcodegen.cpp LIBRARIES Qt::QmlPrivate - Qt::Gui + Qt::GuiPrivate codegen_test_module codegen_test_moduleplugin + codegen_test_hidden + codegen_test_hiddenplugin + codegen_test_stringbuilder + codegen_test_stringbuilderplugin ) qt_internal_add_test(tst_qmlcppcodegen_interpreted @@ -18,9 +28,13 @@ qt_internal_add_test(tst_qmlcppcodegen_interpreted tst_qmlcppcodegen.cpp LIBRARIES Qt::QmlPrivate - Qt::Gui + Qt::GuiPrivate codegen_test_module codegen_test_moduleplugin + codegen_test_hidden + codegen_test_hiddenplugin + codegen_test_stringbuilder + codegen_test_stringbuilderplugin DEFINES QT_TEST_FORCE_INTERPRETER ) diff --git a/tests/auto/qml/qmlcppcodegen/data/Action.qml b/tests/auto/qml/qmlcppcodegen/data/Action.qml new file mode 100644 index 0000000000..99b86fb31c --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/Action.qml @@ -0,0 +1,7 @@ +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Templates as T + +QQC2.Action { + property bool visible: true +} diff --git a/tests/auto/qml/qmlcppcodegen/data/B.qml b/tests/auto/qml/qmlcppcodegen/data/B.qml new file mode 100644 index 0000000000..97e895ecad --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/B.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property rect r +} diff --git a/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml b/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml new file mode 100644 index 0000000000..2615778f6a --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml @@ -0,0 +1,9 @@ +pragma Strict +import QtQml + +QtObject { + property int satisfaction: Satisfaction.NONE + property QtObject output + + function inputsKnown(mark: int) : bool { return true } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 0737d77005..7f2e6ad967 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -4,41 +4,61 @@ set(cpp_sources ambiguous.h birthdayparty.cpp birthdayparty.h + convertQJSPrimitiveValueToIntegral.h cppbaseclass.h druggeljug.h + dummyobjekt.h dynamicmeta.h enumproblems.h enumProperty.h + getOptionalLookup.h gadgetwithenum.h invisible.h + listprovider.h multiforeign.h objectwithmethod.h person.cpp person.h + resettable.h + sequenceToIterable.h sequencetypeexample.cpp sequencetypeexample.h state.h theme.cpp theme.h timelinetheme.cpp timelinetheme.h + variantMapLookup.h + variantreturn.h + weathermoduleurl.h wrapwithvariant.h withlength.h ) set(qml_files AccessModelMethodsFromOutside.qml + Action.qml ArraySequenceLengthInterop.qml + B.qml BadType.qml + BaseConstraint.qml BaseMember.qml BindingExpression.qml + CxxTypeFromDir.qml + CxxTypeFromImplicit.qml Cycle1.qml Cycle2.qml Cycle3.qml - CxxTypeFromDir.qml - CxxTypeFromImplicit.qml + CppMethodListReturnType.qml Dummy.qml + Dummy2.qml + EditConstraint.qml Enums.qml Foozle.qml + GetOptionalLookupOnQJSValueNonStrict.qml + GetOptionalLookupShadowed.qml Loopy.qml + NotificationItem.qml + NotificationsUtils.js OkType.qml Panel.qml + Planner.qml ProgressBar/Keyframe.qml ProgressBar/KeyframeGroup.qml ProgressBar/ProgressBar.ui.qml @@ -46,98 +66,145 @@ set(qml_files ProgressBar/Timeline.qml ProgressBar/TimelineAnimation.qml RootWithoutId.qml + Satisfaction.qml SelectionRectangle.qml + ShadowedObjectName.qml + ShadowedObjectNameDerived.qml + StoreMetaEnum.qml Test.qml TestCase.qml + Variable.qml WindowDerived.qml aliasLookup.qml ambiguous1/Ambiguous.qml ambiguous2/Ambiguous.qml + ambiguousAs.qml ambiguousSignals.qml anchorsFill.qml argumentConversion.qml array.qml + arrayCtor.qml asCast.qml attachedBaseEnum.qml badSequence.qml + basicBlocksWithBackJump.qml + basicBlocksWithBackJump_infinite.qml + basicDTZ.qml bindToValueType.qml blockComments.qml + boolCoercions.qml + boolPointerMerge.qml boundComponents.qml callContextPropertyLookupResult.qml callWithSpread.qml childobject.qml colorAsVariant.qml colorString.qml - consoleObject.qml + compareOriginals.qml + comparisonTypes.qml componentReturnType.qml compositeTypeMethod.qml compositesingleton.qml + consoleObject.qml + consoleTrace.qml construct.qml contextParam.qml conversionDecrement.qml + conversionInDeadCode.qml conversions.qml conversions2.qml + convertPrimitiveToVar.qml + convertQJSPrimitiveValueToIntegral.qml + convertToOriginalReadAcumulatorForUnaryOperators.qml curlygrouped.qml cycleHead.qml + dateConstruction.qml dateConversions.qml deadShoeSize.qml deadStoreLoop.qml dialog.qml + dialogButtonBox.qml dynamicscene.qml + enforceSignature.qml enumConversion.qml + enumFromBadSingleton.qml enumInvalid.qml enumLookup.qml + enumMarkedAsFlag.qml enumProblems.qml enumScope.qml enumsInOtherObject.qml enumsUser.qml equalityQObjects.qml + equalityQUrl.qml + equalityTestsWithNullOrUndefined.qml equalityVarAndNonStorable.qml equalsUndefined.qml + exceptionFromInner.qml excessiveParameters.qml extendedTypes.qml + extra/extra.qml failures.qml fallbacklookups.qml + fallbackresettable.qml fileDialog.qml + flagEnum.qml fromBoolValue.qml - functionLookup.qml funcWithParams.qml + functionLookup.qml functionReturningVoid.qml functionTakingVar.qml + getOptionalLookup.qml globals.qml - hidden/Main.qml - hidden/Style.qml idAccess.qml + ignoredFunctionReturn.qml immediateQuit.qml imports/QmlBench/Globals.qml importsFromImportPath.qml + indirectlyShadowable.qml infinities.qml infinitiesToInt.qml - invisibleBase.qml - invisibleTypes.qml - invisibleListElementType.qml intEnumCompare.qml intOverflow.qml + intToEnum.qml interactive.qml interceptor.qml + internalConversion.qml + invisibleBase.qml + invisibleListElementType.qml + invisibleTypes.qml isnan.qml + iteration.qml javaScriptArgument.qml + jsArrayMethods.qml + jsArrayMethodsUntyped.qml + jsArrayMethodsWithParams.qml + jsArrayMethodsWithParamsUntyped.qml jsMathObject.qml jsimport.qml jsmoduleimport.qml layouts.qml - library.js letAndConst.qml + library.js listAsArgument.qml + listConversion.qml listIndices.qml + listOfInvisible.qml listPropertyAsModel.qml + listToString.qml listlength.qml math.qml + mathMinMax.qml mathOperations.qml + mathStaticProperties.qml + mergedObjectRead.qml + mergedObjectWrite.qml + methodOnListLookup.qml methods.qml modulePrefix.qml moveRegVoid.qml multiforeign.qml + multipleCtors.qml namespaceWithEnum.qml noBindingLoop.qml noQQmlData.qml @@ -146,9 +213,14 @@ set(qml_files notEqualsInt.qml notNotString.qml nullAccess.qml + nullAccessInsideSignalHandler.qml nullComparison.qml + nullishCoalescing.qml numbersInJsPrimitive.qml objectInVar.qml + objectLookupOnListElement.qml + objectWithStringListMethod.qml + optionalComparison.qml outOfBounds.qml overriddenMember.qml ownProperty.qml @@ -157,48 +229,76 @@ set(qml_files popContextAfterRet.qml prefixedMetaType.qml pressAndHoldButton.qml - registerelimination.qml + qtbug113150.qml + reduceWithNullThis.qml + readEnumFromInstance.qml + readonlyListProperty.qml registerPropagation.qml + registerelimination.qml + renameAdjust.qml + resettable.qml + returnAfterReject.qml revisions.qml + scopeIdLookup.qml scopeVsObject.qml + scopedEnum.qml script.js script.mjs + sequenceToIterable.qml + setLookupConversion.qml + setLookupOriginalScope.qml + shadowedAsCasts.qml + shadowedMethod.qml + shadowedPrimitiveCmpEqNull.qml shared/Slider.qml shifts.qml signal.qml signalHandler.qml signalIndexMismatch.qml + signalsWithLists.qml signatureIgnored.qml specificParent.qml storeElementSideEffects.qml stringArg.qml stringLength.qml stringToByteArray.qml + structuredValueType.qml testlogger.js text.qml themerbad.qml themergood.qml + thisObject.qml throwObjectName.qml toString.qml + topLevelComponent.qml translation.qml + trigraphs.qml trivialSignalHandler.qml typePropagationLoop.qml typePropertyClash.qml typedArray.qml undefinedResets.qml + undefinedToDouble.qml unknownAttached.qml unknownParameter.qml unstoredUndefined.qml unusedAttached.qml urlString.qml usingCxxTypesFromFileImports.qml + valueTypeCast.qml valueTypeCopy.qml + valueTypeDefault.qml valueTypeLists.qml valueTypeProperty.qml valueTypeReference.qml + variantMap.qml + variantMapLookup.qml + variantReturn.qml variantlist.qml versionmismatch.qml + voidConversion.qml voidfunction.qml + writeback.qml dummy_imports.qml ) @@ -214,21 +314,88 @@ set_source_files_properties("shared/Slider.qml" set_source_files_properties("hidden/Style.qml" PROPERTIES QT_QML_SINGLETON_TYPE TRUE) +qt_policy(SET QTP0001 NEW) + +# Add the module for the hidden files before setting QTP0004, so that we don't add a qmldir in +# "hidden". That would defeat the purpose. + +qt_add_library(codegen_test_hidden STATIC) +qt_autogen_tools_initial_setup(codegen_test_hidden) + +set_target_properties(codegen_test_hidden PROPERTIES + # We really want qmlcachegen here, even if qmlsc is available + QT_QMLCACHEGEN_EXECUTABLE qmlcachegen + QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks +) + +target_compile_definitions(codegen_test_hidden PUBLIC + -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache" +) + +qt_policy(SET QTP0004 OLD) +qt6_add_qml_module(codegen_test_hidden + URI HiddenTestTypes + QML_FILES + hidden/Main.qml + hidden/Style.qml + OUTPUT_DIRECTORY HiddenTestTypes + __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE +) + +add_dependencies(codegen_test_hidden Qt::Quick) + +qt_autogen_tools_initial_setup(codegen_test_hiddenplugin) + +qt_policy(SET QTP0004 NEW) + +qt_add_library(codegen_test_stringbuilder STATIC) +qt_autogen_tools_initial_setup(codegen_test_stringbuilder) + +set_target_properties(codegen_test_stringbuilder PROPERTIES + # We really want qmlcachegen here, even if qmlsc is available + QT_QMLCACHEGEN_EXECUTABLE qmlcachegen + QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks +) + +target_compile_definitions(codegen_test_stringbuilder PRIVATE + -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache" + QT_USE_QSTRINGBUILDER +) + +qt6_add_qml_module(codegen_test_stringbuilder + URI StringBuilderTestTypes + SOURCES + writableVariantMap.h + QML_FILES + writeVariantMap.qml + OUTPUT_DIRECTORY stringbuilderTestTypes + __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE +) + +qt_autogen_tools_initial_setup(codegen_test_stringbuilderplugin) + qt_add_library(codegen_test_module STATIC) qt_autogen_tools_initial_setup(codegen_test_module) set_target_properties(codegen_test_module PROPERTIES # We really want qmlcachegen here, even if qmlsc is available QT_QMLCACHEGEN_EXECUTABLE qmlcachegen + QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks +) + + + +target_compile_definitions(codegen_test_module PUBLIC + -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache" ) qt6_add_qml_module(codegen_test_module VERSION 1.5 URI TestTypes IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/" - AUTO_RESOURCE_PREFIX DEPENDENCIES QtQuick + QtQuick.Controls QtQuick.Templates QtQuick.Shapes SOURCES @@ -238,8 +405,96 @@ qt6_add_qml_module(codegen_test_module RESOURCES ${resource_files} OUTPUT_DIRECTORY TestTypes # Make sure tst_qmlcachegen doesn't see our output + __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE ) +if(${CMAKE_VERSION} GREATER_EQUAL "3.19.0") + qt_target_qml_sources(codegen_test_module + QML_FILES extra2/extra.qml + ) +else() + target_compile_definitions(codegen_test_module PUBLIC + -DVERY_OLD_CMAKE=1 + ) +endif() + add_dependencies(codegen_test_module Qt::Quick Qt::QuickTemplates2 Qt::QuickShapesPrivate) qt_autogen_tools_initial_setup(codegen_test_moduleplugin) + + +qt_add_library(codegen_test_module_verify STATIC) +qt_autogen_tools_initial_setup(codegen_test_module_verify) + +set_target_properties(codegen_test_module_verify PROPERTIES + # We really want qmlcachegen here, even if qmlsc is available + QT_QMLCACHEGEN_EXECUTABLE qmlcachegen + QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks +) + + +qt6_add_qml_module(codegen_test_module_verify + VERSION 1.5 + URI TestTypes + IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/" + DEPENDENCIES + QtQuick + QtQuick.Controls + QtQuick.Templates + QtQuick.Shapes + SOURCES + ${cpp_sources} + QML_FILES + ${qml_files} + RESOURCES + ${resource_files} + OUTPUT_DIRECTORY verify/TestTypes # Make sure tst_qmlcachegen doesn't see our output + TARGET_PATH verify/TestTypes # Different target path to avoid resource file name clashes + __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE +) + +add_dependencies(codegen_test_module_verify Qt::Quick Qt::QuickTemplates2 Qt::QuickShapesPrivate) + +qt_autogen_tools_initial_setup(codegen_test_module_verifyplugin) + + +qt_internal_add_test(tst_qmlcppcodegen_verify + SOURCES + tst_qmlcppcodegen_verify.cpp +) + +add_dependencies(tst_qmlcppcodegen_verify codegen_test_module codegen_test_module_verify) + +set(a_files "") +set(b_files "") + +foreach(qml_file IN LISTS qml_files) + string(REGEX REPLACE "\\.(js|mjs|qml)$" "_\\1" compiled_file ${qml_file}) + string(REGEX REPLACE "[$#?]+" "_" compiled_file ${compiled_file}) + + list(APPEND + a_files + "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/codegen_test_module_${compiled_file}.cpp") + + list(APPEND + b_files + "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/codegen_test_module_verify_${compiled_file}.cpp") +endforeach() + +qt_add_resources(tst_qmlcppcodegen_verify "a" + PREFIX + "/a" + FILES + ${a_files} + BASE + "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/" +) + +qt_add_resources(tst_qmlcppcodegen_verify "b" + PREFIX + "/b" + FILES + ${b_files} + BASE + "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/" +) diff --git a/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml new file mode 100644 index 0000000000..9c3ce4e877 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml @@ -0,0 +1,12 @@ +pragma Strict + +import QtQuick +import TestTypes + +Item { + ListProvider { + id: listProvider + } + + property var list: listProvider.intList() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml b/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml new file mode 100644 index 0000000000..a3bbef1888 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2020 The Qt Company Ltd. + +import QtQml + +QtObject { + property int value + property Dummy2 child + property int dummyEnum + + signal triggered() + signal signalWithArg(int one, bool two) + property real onValue + property real offValue + + function someFunction(a: int, b: bool, c: Dummy2, d: real, e: int) : int { return 42 } + property string strProp + function concat(a: string, b: string) : string { return a + b } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml b/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml new file mode 100644 index 0000000000..ce278fbdf9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +QtObject { + property Variable myOutput +} diff --git a/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml new file mode 100644 index 0000000000..5a89e996b4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml @@ -0,0 +1,7 @@ +import QtQml +import TestTypes + +QtObject { + property Action action: Action { } + property bool b: action?.visible +} diff --git a/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml new file mode 100644 index 0000000000..eacefc3017 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml @@ -0,0 +1,19 @@ +pragma Strict + +import QtQml +import QtQuick + +QtObject { + id: root + + component Base : QtObject { + property int i: 1 + } + + component Derived : Base { + property string i: "a" + } + + property Base base: Derived { } + property var res: root.base?.i +} diff --git a/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml b/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml new file mode 100644 index 0000000000..fba4df6453 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml @@ -0,0 +1,7 @@ +import QtQml +import TestTypes as MobileShell + +QtObject { + id: notificationItem + objectName: MobileShell.NotificationsUtils.determineNotificationHeadingText(notificationItem) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js b/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js new file mode 100644 index 0000000000..079270e1b9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js @@ -0,0 +1,3 @@ +function determineNotificationHeadingText(notificationItem) { + return "heading"; +} diff --git a/tests/auto/qml/qmlcppcodegen/data/Panel.qml b/tests/auto/qml/qmlcppcodegen/data/Panel.qml index 84e926b8d2..7f589d23e8 100644 --- a/tests/auto/qml/qmlcppcodegen/data/Panel.qml +++ b/tests/auto/qml/qmlcppcodegen/data/Panel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qmlcppcodegen/data/Planner.qml b/tests/auto/qml/qmlcppcodegen/data/Planner.qml new file mode 100644 index 0000000000..ddb2fff053 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/Planner.qml @@ -0,0 +1,50 @@ +pragma Strict +import QtQml + +QtObject { + id: planner + property Variable last: Variable { value: 10 } + + function newMark() : int { + return 5; + } + + function addPropagate(i: int) : bool { + return false; + } + + function typeErasedRemoveOne(v: QtObject) { removeOne(v as Variable) } + + // Work with various shadowable members and return values. + function removeOne(v: Variable) { + let vDeterminedBy = v.determinedBy; + for (let i = 0, length = v.length(); i < length; ++i) { + let next = v.constraint(i) as BaseConstraint; + if (next.satisfaction === Satisfaction.NONE) + objectName += "n" + else if (next !== vDeterminedBy) + objectName += "d" + else + objectName += "x" + } + } + + function typeErasedRun(c: QtObject) { run(c as BaseConstraint) } + + function run(initial: BaseConstraint) { + let mark = planner.newMark(); + let c = initial; + + let output = c.output as Variable; + if (output.mark !== mark && c.inputsKnown(mark)) { + output.mark = mark; + } + } + + function verify(i: int) { + if (last.value !== i) + console.error("failed", last.value, i); + else + console.log("success") + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml index 21a3832366..a44af04a50 100644 --- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml +++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.9 import QtQuick.Window 2.3 diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml index 707f0d9be9..43ff1b62b1 100644 --- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml +++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.9 diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml index b97e8956bf..75ad2245f2 100644 --- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml +++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml @@ -2,5 +2,5 @@ import QtQuick NumberAnimation { property bool pingPong - signal finished() + signal finishedEvil() } diff --git a/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml b/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml new file mode 100644 index 0000000000..74968c65ea --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml @@ -0,0 +1,10 @@ +pragma Strict +import QtQml + +QtObject { + enum Value { + NONE = 0, + FORWARD = 1, + BACKWARD = 2 + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml new file mode 100644 index 0000000000..f079f4a94e --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +QtObject { + property int objectName: 12 +} diff --git a/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml new file mode 100644 index 0000000000..f988cd6bc9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +ShadowedObjectName { + property double objectName: 17.4 +} diff --git a/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml b/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml new file mode 100644 index 0000000000..eb3da15a3a --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml @@ -0,0 +1,12 @@ +import QtQml + +QtObject { + enum Foo { + Bar, + Baz + } + + property var eF: StoreMetaEnum.Foo + property int bar: eF.Bar + property int baz: eF.Baz +} diff --git a/tests/auto/qml/qmlcppcodegen/data/Variable.qml b/tests/auto/qml/qmlcppcodegen/data/Variable.qml new file mode 100644 index 0000000000..f5af97757f --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/Variable.qml @@ -0,0 +1,22 @@ +pragma Strict +import QtQml + +QtObject { + id: variable + property int value: 0 + property int mark: 0 + property BaseConstraint determinedBy: null + property list<BaseConstraint> constraints: [ + BaseConstraint { + satisfaction: variable.value == 0 ? Satisfaction.NONE : Satisfaction.FORWARD + } + ] + + function length(): int { + return constraints.length + } + + function constraint(i: int) : BaseConstraint { + return constraints[i]; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/ambiguous.h b/tests/auto/qml/qmlcppcodegen/data/ambiguous.h index 5ea20dbcd1..8f964d593c 100644 --- a/tests/auto/qml/qmlcppcodegen/data/ambiguous.h +++ b/tests/auto/qml/qmlcppcodegen/data/ambiguous.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef AMBIGUOUS_H #define AMBIGUOUS_H diff --git a/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml b/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml new file mode 100644 index 0000000000..2b7cbd593d --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml @@ -0,0 +1,15 @@ +pragma Strict +import QtQml + +QtObject { + id: self + property bool useSelf: true + property QtObject other: { + var a; + if (useSelf) + a = self + else + a = 15 + return a as QtObject + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml b/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml new file mode 100644 index 0000000000..9886a14cb1 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml @@ -0,0 +1,11 @@ +pragma Strict +import QML + +QtObject { + property list<int> defaultCtor: new Array() + property list<int> oneArgCtor: new Array(5) + property list<int> multiArgCtor: new Array(2, 3, 3, 4) + property list<bool> arrayTrue: new Array(true) + property list<bool> arrayFalse: new Array(false) + property list<real> arrayNegative: new Array(-14) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/asCast.qml b/tests/auto/qml/qmlcppcodegen/data/asCast.qml index 1befc08d0a..cb8155ca6c 100644 --- a/tests/auto/qml/qmlcppcodegen/data/asCast.qml +++ b/tests/auto/qml/qmlcppcodegen/data/asCast.qml @@ -28,4 +28,14 @@ Item { property QtObject dummyAsItem: dummy as Item property QtObject dummyAsRectangle: dummy as Rectangle property QtObject dummyAsDummy: dummy as Dummy + + property QtObject nullAsObject: null as QtObject + property QtObject nullAsItem: null as Item + property QtObject nullAsRectangle: null as Rectangle + property QtObject nullAsDummy: null as Dummy + + property QtObject undefinedAsObject: undefined as QtObject + property QtObject undefinedAsItem: undefined as Item + property QtObject undefinedAsRectangle: undefined as Rectangle + property QtObject undefinedAsDummy: undefined as Dummy } diff --git a/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml new file mode 100644 index 0000000000..5b254fe494 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml @@ -0,0 +1,33 @@ +pragma Strict +import QtQml + +QtObject { + function t1() { + let i = 0 + let foo = false + if (true) { + for (i = 0; i < 42 ; ++i) {} + } else { + console.log(foo) + } + } + + function t2() { + let foo = false + if (false) { + while(Math.random() < 0.5) {} + } else { + console.log(foo) + } + } + + function t3() { + let foo = false + if (Math.random() < 0.5) { + console.log(foo) + } else { + while(Math.random() < 0.5) {} + console.log(foo) + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml new file mode 100644 index 0000000000..997ff68736 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml @@ -0,0 +1,13 @@ +pragma Strict +import QtQml + +QtObject { + function infinite() { + let foo = false + if (true) { + while (true) {} + } else { + console.log(foo) + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml b/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml new file mode 100644 index 0000000000..bc9506a533 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml @@ -0,0 +1,40 @@ +pragma Strict +import QtQml + +QtObject { + id: win + property real width: 640 + property real height: 480 + property string title: "none" + + function t1(): void { + const w = win.width + const h = win.height + + if (w > 0 && h > 0) { + const wByH = h / 3.0 * 4.0 + } + } + + function t2(): void { + let i = 42 + win.title = "Foo " + i + + for (let j = 0; j < 10; j++) { + win.title = "Bar " + j + } + + for (let k = 0; k < i; k++) { + win.title = "Baz " + k + } + } + + function t3(): void { + let v1 = 1 + let v2 = 2 + + if (true) { + v1 = v2 + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp index 048459f03a..10a2c90b38 100644 --- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp +++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "birthdayparty.h" @@ -36,6 +36,23 @@ Person *BirthdayParty::guest(int index) const return m_guests.at(index); } +QStringList BirthdayParty::guestNames() const +{ + QStringList names; + for (Person *guest: m_guests) + names.append(guest->name()); + return names; +} + +QVariantList BirthdayParty::stuffs() const +{ + return QVariantList({ + QVariant::fromValue(objectName()), + QVariant::fromValue(m_guests.size()), + QVariant::fromValue(m_host) + }); +} + void BirthdayParty::invite(const QString &name) { auto *person = new Person(this); diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h index a6871c39b1..8dd640c67f 100644 --- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h +++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BIRTHDAYPARTY_H #define BIRTHDAYPARTY_H @@ -52,7 +52,6 @@ private: }; struct Foozle { - Q_GADGET int foo = 1; }; @@ -61,6 +60,8 @@ class BirthdayParty : public QObject Q_OBJECT Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL) Q_PROPERTY(QQmlListProperty<Person> guests READ guests) + Q_PROPERTY(QStringList guestNames READ guestNames FINAL) + Q_PROPERTY(QVariantList stuffs READ stuffs FINAL) QML_ELEMENT QML_ATTACHED(BirthdayPartyAttached) QML_EXTENDED(BirthDayPartyExtended) @@ -74,6 +75,9 @@ public: int guestCount() const; Person *guest(int) const; + QStringList guestNames() const; + QVariantList stuffs() const; + Q_INVOKABLE void invite(const QString &name); static BirthdayPartyAttached *qmlAttachedProperties(QObject *object); diff --git a/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml b/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml new file mode 100644 index 0000000000..6b8ebb3f38 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml @@ -0,0 +1,45 @@ +pragma Strict +import QtQml + +QtObject { + property rect a + property bool t1: a + + property int c: 1 + property bool t2: c + + property url e: "qrc:/ab/c.txt" + property bool t3: e + + property string f: "a" + property bool t4: f + + property var j: 1 + property bool t5: j + + id: k + property bool t6: k + + property date l + property bool t7: l + + property url m + property bool t8: m + + + + property int b: 0 + property bool f1: b + + property QtObject d: null + property bool f2: d + + property string g + property bool f3: g + + property var h: undefined + property bool f4: h + + property var i: null + property bool f5: i +} diff --git a/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml b/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml new file mode 100644 index 0000000000..b2d4d7c5d0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml @@ -0,0 +1,15 @@ +pragma Strict +import TestTypes +import QtQuick + +Loader { + id: self + source: "BaseMember.qml" + property int ppp: -99 + + onItemChanged: { + var base = item as BaseMember; + if (base) + base.ppp = ppp + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/childobject.qml b/tests/auto/qml/qmlcppcodegen/data/childobject.qml index 3775ee16bc..76ad8fbbb2 100644 --- a/tests/auto/qml/qmlcppcodegen/data/childobject.qml +++ b/tests/auto/qml/qmlcppcodegen/data/childobject.qml @@ -4,6 +4,19 @@ import TestTypes QtObject { property ObjectWithMethod child: ObjectWithMethod { objectName: "kraut" + + function doString() { overloaded("string"); } + function doNumber() { overloaded(5.2); } + function doArray() { overloaded({a: 2, b: 3, c: 3}); } + + function doString2() { overloaded2("string"); } + function doNumber2() { overloaded2(5.2); } + + // Artificially pass an extra argument to avoid choosing the "string" overload. + // Unfortunately this is still order-dependent on the metaobject level. + function doArray2() { overloaded2({a: 2, b: 3, c: 3}, 1); } + + function doFoo() { foo(this); } } objectName: child.objectName property int doneThing: child.doThing() diff --git a/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml b/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml new file mode 100644 index 0000000000..3d40ffee62 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml @@ -0,0 +1,39 @@ +pragma Strict +import QtQml + +QtObject { + component Variable: QtObject { + property int value: 4 + } + + property Variable first: Variable {} + property Variable last: Variable { + id: last + } + + property int compareOriginals: { + var matches = 0; + for (var i = 0; i < 6; i++) { + first.value = i; // do a shadowed assignment + if (last.value != i) + ++matches + } + return matches; + } + + property bool optionalThis: { + var a + if (2 == 2) + a = this + else + a = undefined + + var b + if (2 == 2) + b = this + else + b = undefined + + return a === b + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml b/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml new file mode 100644 index 0000000000..423cd55ed4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml @@ -0,0 +1,54 @@ +import QtQml + +QtObject { + component Variable: QtObject { + property int value: 4 + } + + component VariableShadow: Variable { + property string value: "1" + } + + property Variable last: VariableShadow {} + + function find(n: int) : int { + var found = 0 + for (var i = 0; i < n; i++) { + if (last.value == i) + ++found + } + return found; + } + + function findStrict(n: int) : int { + var found = 0 + for (var i = 0; i < n; i++) { + if (last.value === i) + ++found + } + return found; + } + + function findNot(n: int) : int { + var found = 0 + for (var i = 0; i < n; i++) { + if (last.value != i) + ++found + } + return found; + } + + function findNotStrict(n: int) : int { + var found = 0 + for (var i = 0; i < n; i++) { + if (last.value !== i) + ++found + } + return found; + } + + property int found: find(3) + property int foundStrict: findStrict(10) + property int foundNot: findNot(3) + property int foundNotStrict: findNotStrict(10) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml new file mode 100644 index 0000000000..a80af89ddd --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + function a() { b() } + function b() { c() } + function c() { console.trace() } + Component.onCompleted: a() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml b/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml new file mode 100644 index 0000000000..b2e7b40c00 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml @@ -0,0 +1,32 @@ +pragma Strict +import QtQml + +QtObject { + // This does not look like dead code, but each access to 'result' generates a + // DeadTemoralZoneCheck instruction that we ignore when compiling to C++ + // after checking statically that 'result' is alive throughout the function. + // Therefore, this function is a torture test for the dead code elimination. + function calc(a: int, b: int) : int { + let result = a; + if (b < 0) { + if (b < -1) + result -= b; + if (b < -2) + result /= b; + } else { + if (b > 1) + result *= b; + if (b > 2) + result += b; + } + return result; + } + + property int a: calc(10, -3); + property int b: calc(10, -2); + property int c: calc(10, -1); + property int d: calc(10, 0); + property int e: calc(10, 1); + property int f: calc(10, 2); + property int g: calc(10, 3); +} diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml index c3a9414ae2..d0d1fc9b8f 100644 --- a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml +++ b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml @@ -94,6 +94,8 @@ Item { } function qtest_signalHandlerName(sn) { + // Warning: to not test for signal handlers like this in actual code. + // Use the helper methods in QQmlSignalNames instead. if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase()) return sn return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1) diff --git a/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml b/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml new file mode 100644 index 0000000000..f7c2cc4058 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml @@ -0,0 +1,18 @@ +pragma Strict +import QtQml + +QtObject { + id: foo + + property int offsetValue + + function send(data : variant) { + } + + Component.onCompleted: () => { + let deltaOffset = 42 + deltaOffset -= 1 + foo.offsetValue = deltaOffset + foo.send({offset: deltaOffset}) + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h new file mode 100644 index 0000000000..459dd62374 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H +#define CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class Moo485 : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(int uid READ uid CONSTANT FINAL) + +public: + explicit Moo485(QObject *parent = nullptr) : QObject(parent) { } + int uid() const { return 4711; } +}; + +class Foo485 : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(quint16 uid MEMBER m_uid FINAL) + +public: + explicit Foo485(QObject *parent = nullptr) : QObject(parent) { } + quint16 m_uid = 0; +}; + +#endif // CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H diff --git a/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml new file mode 100644 index 0000000000..6a15d6f775 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml @@ -0,0 +1,13 @@ +import QtQuick +import TestTypes + + +Item { + id: root + + property Moo485 moo + + readonly property Foo485 foo: Foo485 { + uid: root.moo.uid ?? 0xFFFF + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml b/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml new file mode 100644 index 0000000000..6eb14bba57 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml @@ -0,0 +1,13 @@ +pragma Strict +import QtQml + +QtObject { + id: self + property int i: 0 + property Planner planner: null + + function satisfy(mark: int) { + planner.addPropagate(mark); + i = +mark; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h index eecc3d968e..812415ae24 100644 --- a/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h +++ b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef CPPBASECLASS_H #define CPPBASECLASS_H diff --git a/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml b/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml new file mode 100644 index 0000000000..fc8a34da71 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml @@ -0,0 +1,20 @@ +pragma Strict +import QtQml + +QtObject { + property date now: new Date() + property date now2: new Date(now) + property date fromString: new Date("1995-12-17T03:24:00") + property date fromNumber: new Date(777) + property date fromPrimitive: new Date(objectName.length === 0 ? 57 : "1997-02-13T13:04:12") + property date from2: new Date(1996, 1) + property date from3: new Date(1996, 2, 3) + property date from4: new Date(1996, 3, 4, 5) + property date from5: new Date(1996, 4, 5, 6, 7) + property date from6: new Date(1996, 5, 6, 7, 8, 9) + property date from7: new Date(1996, 6, 7, 8, 9, 10, 11) + property date from8: new Date(1996, 7, 8, 9, 10, 11, 12, 13) + + property date withUnderflow: new Date(-4, -5, -6, -7, -8, -9, -10) + property date invalid: new Date("foo", "bar") +} diff --git a/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml b/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml index 38a34f7487..5c0a426466 100644 --- a/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml +++ b/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml @@ -9,12 +9,17 @@ QtObject { property string dateString: date property string timeString: time + property real dateNumber: date + property real timeNumber: time + function shuffle() { Druggeljug.myDate = date; Druggeljug.myTime = time; dateString = Druggeljug.myDate; timeString = Druggeljug.myTime; + dateNumber = Druggeljug.myDate; + timeNumber = Druggeljug.myTime; } function fool() { @@ -22,4 +27,9 @@ QtObject { Druggeljug.myTime = Druggeljug.myDate; Druggeljug.myDate = tmp; } + + function invalidate() { + date = new Date("foo", "bar"); + time = new Date("bar", "foo"); + } } diff --git a/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml b/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml new file mode 100644 index 0000000000..b30e0124c8 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml @@ -0,0 +1,8 @@ +pragma Strict +import QtQuick.Controls.Basic + +ApplicationWindow { + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h b/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h new file mode 100644 index 0000000000..ace319f91f --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DUMMYOBJEKT_H +#define DUMMYOBJEKT_H + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +#if QT_DEPRECATED_SINCE(6, 4) +class DummyObjekt : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + +public: + enum Test { + TestA = 1, + TestB + }; + Q_ENUM(Test) + + // Deliberately not default constructible + explicit DummyObjekt(QObject *parent) : QObject(parent) {} +}; +#endif + +#endif // DUMMYOBJEKT_H diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h index 64c2850bda..d8358b6a9c 100644 --- a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h +++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h @@ -1,38 +1,48 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DYNAMICMETA_H #define DYNAMICMETA_H #include <private/qobject_p.h> +#include <private/qmetaobjectbuilder_p.h> #include <QtQmlIntegration/qqmlintegration.h> -struct FreeDeleter { - void operator()(QMetaObject *meta) { free(meta); } -}; - template<typename T> class MetaObjectData : public QDynamicMetaObjectData { Q_DISABLE_COPY_MOVE(MetaObjectData) public: - MetaObjectData() = default; - ~MetaObjectData() = default; + MetaObjectData() + { + QMetaObjectBuilder builder; + builder.setSuperClass(&T::staticMetaObject); + builder.setFlags(builder.flags() | DynamicMetaObject); + metaObject = builder.toMetaObject(); + }; + + ~MetaObjectData() { + free(metaObject); + }; QMetaObject *toDynamicMetaObject(QObject *) override { - return const_cast<QMetaObject *>(&T::staticMetaObject); + return metaObject; } int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override { return o->qt_metacall(call, idx, argv); } + + QMetaObject *metaObject = nullptr; }; class DynamicMeta : public QObject { Q_OBJECT Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL) + Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal shadowable READ shadowable CONSTANT) QML_ELEMENT public: @@ -54,11 +64,26 @@ public: Q_INVOKABLE int bar(int baz) { return baz + 12; } + qreal value() const { return m_value; } + qreal shadowable() const { return 25; } + +public slots: + void resetValue() { setValue(0); } + void setValue(qreal value) + { + if (m_value == value) + return; + m_value = value; + emit valueChanged(); + } + Q_SIGNALS: void fooChanged(); + void valueChanged(); private: int m_foo = 0; + qreal m_value = 0; }; class DynamicMetaSingleton : public DynamicMeta diff --git a/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml new file mode 100644 index 0000000000..571a000199 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml @@ -0,0 +1,11 @@ +import QtQml + +QtObject { + id: mainItem + + function arg(item: Binding) : QtObject { return item } + function ret(item: QtObject) : Binding { return item } + + property QtObject a: arg(mainItem); + property QtObject b: ret(mainItem); +} diff --git a/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml b/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml index fee2c50e15..61ddd2162d 100644 --- a/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml +++ b/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml @@ -1,7 +1,12 @@ +pragma Strict import TestTypes MyType { id: root + + property alias status: root.a + property int test: myEnumType.type property bool test_1: myEnumType.type + objectName: root.status + "m" } diff --git a/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml b/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml new file mode 100644 index 0000000000..3176fde315 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml @@ -0,0 +1,6 @@ +import QtQml +import TestTypes + +QtObject { + objectName: "Dummy: " + DummyObjekt.TestA +} diff --git a/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml b/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml new file mode 100644 index 0000000000..2ef37cbdf0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml @@ -0,0 +1,6 @@ +import QML +import TestTypes + +QtObject { + property int flagValue: ControlFlags.Both +} diff --git a/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml b/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml index f9a4eb144b..6a57b0e64a 100644 --- a/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml +++ b/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml @@ -11,4 +11,9 @@ QtObject { readonly property FooThing fighter: root.f.get(Foo.Fighter) readonly property FooThing bar: root.f.get(Foo.Component) } + + property int a: FooFactory.B + property int b: f.t8 + property int c: FooFactory.D + property int d: f.t16 } diff --git a/tests/auto/qml/qmlcppcodegen/data/enumProperty.h b/tests/auto/qml/qmlcppcodegen/data/enumProperty.h index 8c13e860a3..8d6405a059 100644 --- a/tests/auto/qml/qmlcppcodegen/data/enumProperty.h +++ b/tests/auto/qml/qmlcppcodegen/data/enumProperty.h @@ -15,20 +15,87 @@ public: Tri = 0x04, }; Q_ENUM(MyEnum) - Q_PROPERTY(MyEnum type READ type) + Q_PROPERTY(MyEnum type READ type CONSTANT) MyEnum type() const { return MyEnum::Tri; } }; class MyType : public QObject { Q_OBJECT - Q_PROPERTY(MyEnumType myEnumType READ myEnumType) + Q_PROPERTY(MyEnumType myEnumType READ myEnumType CONSTANT) + Q_PROPERTY(A a READ a WRITE setA NOTIFY aChanged FINAL) QML_ELEMENT public: + enum A { B, C, D }; + Q_ENUM(A) + MyEnumType myEnumType() const { return m_type; } + A a() const { return m_a; } + void setA(A newA) + { + if (m_a == newA) + return; + m_a = newA; + emit aChanged(); + } + + Q_INVOKABLE int method(quint16, const QString &) { return 24; } + Q_INVOKABLE int method(quint16, MyType::A a) { return int(a); } + +Q_SIGNALS: + void aChanged(); + private: MyEnumType m_type; + A m_a = B; +}; + +class CommunicationPermission +{ + Q_GADGET +public: + enum CommunicationMode : quint8 { + Access = 0x01, + Advertise = 0x02, + Default = Access | Advertise, + }; + Q_DECLARE_FLAGS(CommunicationModes, CommunicationMode) + Q_FLAG(CommunicationModes) + + void setCommunicationModes(CommunicationModes modes) { m_modes = modes; } + CommunicationModes communicationModes() const { return m_modes; } + +private: + CommunicationModes m_modes; +}; + +struct QQmlCommunicationPermission : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(CommunicationPermission) + QML_EXTENDED_NAMESPACE(CommunicationPermission) + Q_PROPERTY(CommunicationPermission::CommunicationModes communicationModes READ communicationModes WRITE setCommunicationmodes NOTIFY communicationModesChanged) + +public: + CommunicationPermission::CommunicationModes communicationModes() const + { + return m_permission.communicationModes(); + } + + void setCommunicationmodes(const CommunicationPermission::CommunicationModes &newCommunicationModes) + { + if (communicationModes() == newCommunicationModes) + return; + m_permission.setCommunicationModes(newCommunicationModes); + emit communicationModesChanged(); + } + +signals: + void communicationModesChanged(); + +private: + CommunicationPermission m_permission; }; #endif // ENUMPROPERTY_H diff --git a/tests/auto/qml/qmlcppcodegen/data/enumproblems.h b/tests/auto/qml/qmlcppcodegen/data/enumproblems.h index 08a00acf7e..36f97bec5a 100644 --- a/tests/auto/qml/qmlcppcodegen/data/enumproblems.h +++ b/tests/auto/qml/qmlcppcodegen/data/enumproblems.h @@ -1,10 +1,11 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef ENUMPROBLEMS_H #define ENUMPROBLEMS_H #include <QObject> +#include <QtCore/qflags.h> #include <QtQml/qqml.h> #include <QtQml/qqmlregistration.h> @@ -45,9 +46,74 @@ class FooThingWrapper { class FooFactory : public QObject { Q_OBJECT QML_ELEMENT + Q_PROPERTY(T8 t8 READ t8 CONSTANT FINAL) + Q_PROPERTY(T16 t16 READ t16 CONSTANT FINAL) public: + enum T8: qint8 { + A, B, C + }; + Q_ENUM(T8) + + enum T16: qint16 { + D = 500, E, F + }; + Q_ENUM(T16) + + T8 t8() const { return C; } + T16 t16() const { return E; } + Q_INVOKABLE Foo* get(Foo::Type type) const { return new Foo(type); } }; +class ControlFlags : public QObject { + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("Flag Container Class") +public: + + enum Option { + ControlA = 0x1, + ControlB = 0x2, + Both = ControlA | ControlB + }; + + Q_DECLARE_FLAGS(Options, Option) + Q_FLAG(Option) +}; + +class ScopedEnum : public QObject { + Q_OBJECT + QML_NAMED_ELEMENT(Data) + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +public: + enum class DType { + A = 27, B + }; + Q_ENUM(DType) + + enum EType { + C = 7, D + }; + Q_ENUM(EType) +}; + +class UnscopedEnum : public QObject { + Q_OBJECT + QML_NAMED_ELEMENT(Data2) + Q_CLASSINFO("RegisterEnumClassesUnscoped", "true") + +public: + enum class DType { + A = 26, B + }; + Q_ENUM(DType) + + enum EType { + C = 6, D + }; + Q_ENUM(EType) +}; + #endif // ENUMPROBLEMS_H diff --git a/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml b/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml new file mode 100644 index 0000000000..55ac68592c --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml @@ -0,0 +1,16 @@ +pragma Strict +import QtQuick + +Item { + property url emptyUrl: "" + property url sourceUrl: "some/path/file.png" + + property bool emptyUrlStrict: emptyUrl === Qt.resolvedUrl("") + property bool emptyUrlWeak: emptyUrl == Qt.resolvedUrl("") + + property bool sourceUrlStrict: sourceUrl === Qt.url("some/path/file.png"); + property bool sourceUrlWeak: sourceUrl == Qt.url("some/path/file.png"); + + property bool sourceIsNotEmptyStrict: sourceUrl !== emptyUrl + property bool sourceIsNotEmptyWeak: sourceUrl != emptyUrl +} diff --git a/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml b/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml new file mode 100644 index 0000000000..cd0c433ea9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml @@ -0,0 +1,14 @@ +pragma Strict + +import QtQml +import QtQuick + +Window { + property var foo + Component.onCompleted: { + console.log(foo !== null) + console.log(foo === null) + console.log(foo !== undefined) + console.log(foo === undefined) + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml b/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml new file mode 100644 index 0000000000..13855356f2 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml @@ -0,0 +1,10 @@ +pragma Strict +import QtQml + +QtObject { + property QtObject theNull: null + + function doFail() : string { return theNull.objectName } + function delegateFail() : string { doFail() } + function disbelieveFail() : string { delegateFail() } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml b/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml new file mode 100644 index 0000000000..e8f51984b8 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +B { + r: ({x: 4, y: 6, width: 8, height: 10}) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml b/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml new file mode 100644 index 0000000000..e8f51984b8 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +B { + r: ({x: 4, y: 6, width: 8, height: 10}) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml index f90fb44fe1..3b0e4908ab 100644 --- a/tests/auto/qml/qmlcppcodegen/data/failures.qml +++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml @@ -9,6 +9,7 @@ QtObject { property string attachedForNasty: Nasty.objectName property Nasty nasty: Nasty { + id: theNasty objectName: Component.objectName } @@ -30,18 +31,10 @@ QtObject { Component.onCompleted: doesNotExist() - property string aString: self + "a" - property BirthdayParty party: BirthdayParty { onPartyStarted: (foozle) => { objectName = foozle } } - signal foo() - signal bar() - - // Cannot assign potential undefined - onFoo: objectName = self.bar() - property int enumFromGadget1: GadgetWithEnum.CONNECTED + 1 property int enumFromGadget2: TT2.GadgetWithEnum.CONNECTED + 1 @@ -62,4 +55,55 @@ QtObject { let a; return a; } + + function getText(myArr: list<string>): string { + myArr.shiftss() + } + + function readTracks(metadataList : list<badType>): int { + return metadataList.length + } + + function dtzFail() : int { + for (var a = 10; a < 20; ++a) { + switch (a) { + case 11: + let b = 5; + break; + case 10: + console.log(b); + break; + } + } + return a; + } + + // TODO: Drop these once we can manipulate QVariant-wrapped lists. + property list<withLength> withLengths + property int l: withLengths.length + property withLength w: withLengths[10] + + property unconstructibleWithLength uwl: 12 + 1 + + // Cannot generate code for getters + property rect r3: ({ get x() { return 42; }, y: 4 }) + + property int nonIterable: { + var result = 1; + for (var a in Component) + ++result; + return result; + } + + property alias selfself: self + property alias nastyBad: theNasty.bad + function writeToUnknown() : int { + self.selfself.nastyBad = undefined; + return 5; + } + + readonly property int someNumber: 10 + function writeToReadonly() { someNumber = 20 } + + property var silly: [,0] } diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml new file mode 100644 index 0000000000..44b55e245a --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml @@ -0,0 +1,23 @@ +pragma Strict +import QtQml +import TestTypes + +DynamicMeta { + id: self + value: 999 + + property double notResettable: 10 + property double notResettable2: { return undefined } + + property DynamicMeta shadowing: DynamicMeta { + property var shadowable: undefined + } + + function doReset() { self.value = undefined } + function doReset2() { self.value = shadowing.shadowable } + function doNotReset() { self.notResettable = undefined } + + signal aaa() + signal bbb() + onAaa: objectName = self.bbb() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml index b8bd466717..6634982de2 100644 --- a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml +++ b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls diff --git a/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml b/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml new file mode 100644 index 0000000000..3ea5cf98db --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml @@ -0,0 +1,6 @@ +pragma Strict +import TestTypes + +CommunicationPermission { + communicationModes: CommunicationPermission.Access +} diff --git a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h index 839e026b77..3c81cd2e7f 100644 --- a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h +++ b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef GADGETWITHENUM_H #define GADGETWITHENUM_H @@ -26,4 +26,40 @@ namespace GadgetWithEnumWrapper { QML_NAMED_ELEMENT(NamespaceWithEnum) }; +struct Gadget +{ + Q_GADGET + QML_VALUE_TYPE(gadget) + +public: + enum class Prop1 { High, low, VeryHigh, VeryLow }; + Q_ENUM(Prop1) + + enum class Prop2 { VeryHigh, High, low, VeryLow }; + Q_ENUM(Prop2) +}; + +class Backend : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + Q_PROPERTY(prop priority READ priority FINAL CONSTANT) + Q_PROPERTY(Gadget gadget READ gadget FINAL CONSTANT) + Q_CLASSINFO("RegisterEnumsFromRelatedTypes", "false") + +public: + enum prop { High, low, VeryHigh, VeryLow }; + Q_ENUM(prop) + + explicit Backend(QObject *parent = nullptr) : QObject(parent) {} + + prop priority() const { return m_priority; } + Gadget gadget() const { return m_gadget; } + +private: + prop m_priority = low; + Gadget m_gadget; +}; + #endif // GADGETWITHENUM_H diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h new file mode 100644 index 0000000000..e8a24cd707 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h @@ -0,0 +1,42 @@ +#ifndef GETOPTIONALLOOKUP_H +#define GETOPTIONALLOOKUP_H + +#include <QObject> +#include <QQmlEngine> + +class GOL_Object : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(int i READ i CONSTANT FINAL) + Q_PROPERTY(QString s READ s CONSTANT FINAL) + Q_PROPERTY(GOL_Object *childA READ childA WRITE setChildA NOTIFY childAChanged FINAL) + Q_PROPERTY(Enum e READ e CONSTANT FINAL) + +public: + GOL_Object(QObject *parent = nullptr) : QObject(parent) { } + + int i() const { return m_i; } + void setI(int i) { m_i = i; } + + QString s() const { return m_s; } + void setS(QString s) { m_s = s; } + + GOL_Object *childA() const { return m_childA; } + void setChildA(GOL_Object *a) { m_childA = a; } + + enum Enum { V1, V2 }; + Q_ENUM(Enum) + Enum e() const { return Enum::V2; } + +signals: + void childAChanged(); + +private: + int m_i = 5; + QString m_s = "6"; + GOL_Object *m_childA = nullptr; +}; + +#endif // GETOPTIONALLOOKUP_H diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml new file mode 100644 index 0000000000..ee360d7142 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml @@ -0,0 +1,34 @@ +pragma Strict +pragma ValueTypeBehavior: Addressable + +import QtQuick + +GOL_Object { + id: root + + property rect r: Qt.rect(0, 0, 20, 50) + property point p: Qt.point(0, -10) + property var v: Qt.point(5, 5) + property var u: undefined + + property int to1: root?.i + property string to2: root?.s + property GOL_Object to3: root?.childA + property var to4: root.childA?.i + property var to5: (undefined as GOL_Object)?.childA + property int to6: (root as GOL_Object)?.s.length + + property int tv1: root.r?.bottom + property int tv2: root.p?.y + + property int te1: root?.e + property int te2: GOL_Object?.V2 + property bool te3: root?.e === GOL_Object?.V1 + property bool te4: root?.e === GOL_Object?.V2 + + property int tc1: root?.p.y + property int tc2: root.r?.x + + property var tc4: root?.childA?.s + property var tc5: root.childA?.s +} diff --git a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml index 2090926872..6ca1f7f66b 100644 --- a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml +++ b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml @@ -1,3 +1,4 @@ +pragma Strict import QtQuick Item { @@ -11,5 +12,9 @@ Item { Text { id: ttt + onTextChanged: { + root.objectName = "dead" + ttt.objectName = "context" + } } } diff --git a/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml b/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml new file mode 100644 index 0000000000..640e2bc22a --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml @@ -0,0 +1,14 @@ +import QtQuick + +Item { + id: root + + Component { + id: comp + Rectangle { + color: "blue" + } + } + + Component.onCompleted: comp.createObject(root, {"width": 200, "height": 200}) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml b/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml new file mode 100644 index 0000000000..de31527e5b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml @@ -0,0 +1,39 @@ +import QtQml + +QtObject { + id: self + objectName: "self" + + component Inner : QtObject { + property QtObject shadowable: QtObject { + objectName: "shadowable" + } + } + + component Outer : QtObject { + property Inner inner: Inner {} + } + + component Evil : Outer { + property string inner: "evil" + } + + property Outer outer: Outer {} + property Outer evil: Evil {} + + property QtObject notShadowable: QtObject { + objectName: "notShadowable" + } + + function getInnerShadowable() { + notShadowable = outer.inner.shadowable; + } + + function setInnerShadowable() { + outer.inner.shadowable = self; + } + + function turnEvil() { + outer = evil; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml b/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml new file mode 100644 index 0000000000..e255f4e8f4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml @@ -0,0 +1,7 @@ +pragma Strict +import TestTypes + +MyType { + a: myEnumType.type === 4 ? 2 : 1 + property int b: method("12", "hh") +} diff --git a/tests/auto/qml/qmlcppcodegen/data/interactive.qml b/tests/auto/qml/qmlcppcodegen/data/interactive.qml index e857df96e7..be5e5f0d40 100644 --- a/tests/auto/qml/qmlcppcodegen/data/interactive.qml +++ b/tests/auto/qml/qmlcppcodegen/data/interactive.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only pragma Strict import QtQuick 2.9 diff --git a/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml b/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml new file mode 100644 index 0000000000..7304b7a6b9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml @@ -0,0 +1,16 @@ +pragma Strict +import QtQml + +QtObject { + property QtObject offset: QtObject { + id: a + property string mark + } + + function markInputs(mark: string) { + offset.objectName = mark; + a.mark = mark; + } + + Component.onCompleted: markInputs("hello") +} diff --git a/tests/auto/qml/qmlcppcodegen/data/invisible.h b/tests/auto/qml/qmlcppcodegen/data/invisible.h index a385ee975f..4f4ebb87ad 100644 --- a/tests/auto/qml/qmlcppcodegen/data/invisible.h +++ b/tests/auto/qml/qmlcppcodegen/data/invisible.h @@ -1,11 +1,12 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef INVISIBLE_H #define INVISIBLE_H #include <QtCore/qobject.h> #include <QtQmlIntegration/qqmlintegration.h> +#include <QtQml/qqmllist.h> class Invisible : public QObject { @@ -45,6 +46,39 @@ class DerivedFromInvisible : public Invisible { Q_OBJECT QML_ELEMENT + Q_PROPERTY(double implicitWidth MEMBER m_implicitWidth NOTIFY implicitWidthChanged FINAL) +public: + DerivedFromInvisible(QObject *parent = nullptr) : Invisible(parent) {} + +signals: + void implicitWidthChanged(); + +private: + double m_implicitWidth = 27; +}; + +class WithListPropertyOfDerivedFromInvisible : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QQmlListProperty<DerivedFromInvisible> children READ children NOTIFY childrenChanged FINAL) + +public: + WithListPropertyOfDerivedFromInvisible(QObject *parent = nullptr) : QObject(parent) + { + m_children.append(new DerivedFromInvisible(this)); + } + + QQmlListProperty<DerivedFromInvisible> children() + { + return QQmlListProperty<DerivedFromInvisible>(this, &m_children); + } + +signals: + void childrenChanged(); + +private: + QList<DerivedFromInvisible *> m_children; }; #endif // INVISIBLE_H diff --git a/tests/auto/qml/qmlcppcodegen/data/iteration.qml b/tests/auto/qml/qmlcppcodegen/data/iteration.qml new file mode 100644 index 0000000000..8632eefa1b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/iteration.qml @@ -0,0 +1,20 @@ +pragma Strict + +import QtQml + +QtObject { + property list<int> ints: [3, 4, 5] + property list<QtObject> objects: [ + QtObject { objectName: "a" }, + QtObject { objectName: "b" }, + QtObject { objectName: "c" } + ] + + Component.onCompleted: { + for (var a in objects) { + objectName += objects[a].objectName; + for (var b in ints) + objectName += ints[b]; + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml new file mode 100644 index 0000000000..ff372bca45 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml @@ -0,0 +1,28 @@ +pragma Strict +import QML + +QtObject { + id: self + + property QtObject l1: QtObject { objectName: "klaus" } + property QtObject l2: QtObject { function toString(): string { return "teil" } } + property QtObject l3: QtObject { } + + function jsArray() : list<var> { return [l1, l2, l3, l1, l2, l3] } + property list<QtObject> listProperty: [l1, l2, l3, l1, l2, l3] + + property string jsArrayToString: jsArray().toString() + property string listPropertyToString: listProperty.toString() + + property bool listPropertyIncludes: listProperty.includes(l3) + property bool jsArrayIncludes: jsArray().includes(l3) + + property string listPropertyJoin: listProperty.join() + property string jsArrayJoin: jsArray().join() + + property int listPropertyIndexOf: listProperty.indexOf(l2) + property int jsArrayIndexOf: jsArray().indexOf(l2) + + property int listPropertyLastIndexOf: listProperty.lastIndexOf(l3) + property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml new file mode 100644 index 0000000000..7426c692fe --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml @@ -0,0 +1,17 @@ +import QML + +QtObject { + id: self + + property QtObject l1 + property QtObject l2 + property QtObject l3 + + function jsArray() { return [l1, l2, l3, l1, l2, l3] } + + property string jsArrayToString: jsArray().toString() + property bool jsArrayIncludes: jsArray().includes(l3) + property string jsArrayJoin: jsArray().join() + property int jsArrayIndexOf: jsArray().indexOf(l2) + property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml new file mode 100644 index 0000000000..293e7cbda5 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml @@ -0,0 +1,26 @@ +pragma Strict +import QML + +QtObject { + id: self + + required property int i + required property int j + required property int k + + property QtObject l1: QtObject { objectName: "klaus" } + property QtObject l2: QtObject { function toString(): string { return "teil" } } + property QtObject l3: QtObject { } + + function jsArray() : list<var> { return [l1, l2, l3, l1, l2, l3] } + property list<QtObject> listProperty: [l1, l2, l3, l1, l2, l3] + + property list<QtObject> listPropertySlice: listProperty.slice(i, j) + property list<var> jsArraySlice: jsArray().slice(i, j) + + property int listPropertyIndexOf: listProperty.indexOf(l2, i) + property int jsArrayIndexOf: jsArray().indexOf(l2, i) + + property int listPropertyLastIndexOf: listProperty.lastIndexOf(l3, i) + property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3, i) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml new file mode 100644 index 0000000000..9e928bd6f6 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml @@ -0,0 +1,18 @@ +import QML + +QtObject { + id: self + + required property int i + required property int j + required property int k + + property QtObject l1 + property QtObject l2 + property QtObject l3 + + function jsArray() { return [l1, l2, l3, l1, l2, l3] } + property var jsArraySlice: jsArray().slice(i, j) + property int jsArrayIndexOf: jsArray().indexOf(l2, i) + property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3, i) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/listConversion.qml b/tests/auto/qml/qmlcppcodegen/data/listConversion.qml new file mode 100644 index 0000000000..ca86d9a1d6 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/listConversion.qml @@ -0,0 +1,17 @@ +pragma Strict +import QtQml +import TestTypes + +BirthdayParty { + id: self + + guests: [ + Person { name: "Horst 1" }, + Person { name: "Horst 2" }, + Person { name: "Horst 3" } + ] + + property list<QtObject> o: self.guests + property list<string> s: self.guestNames + property list<var> v: self.stuffs +} diff --git a/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml b/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml new file mode 100644 index 0000000000..f3698d78ab --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml @@ -0,0 +1,6 @@ +pragma Strict +import TestTypes + +WithListPropertyOfDerivedFromInvisible { + property real width: children[0].implicitWidth +} diff --git a/tests/auto/qml/qmlcppcodegen/data/listToString.qml b/tests/auto/qml/qmlcppcodegen/data/listToString.qml new file mode 100644 index 0000000000..e9e4b85956 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/listToString.qml @@ -0,0 +1,25 @@ +pragma Strict +import QtQml + +QtObject { + property list<string> stringList: ["one", "two"] + property list<int> intList: [1, 2] + property list<QtObject> objectList: [this, this] + + Component.onCompleted: { + console.log(stringList) + console.log(stringList + "") + + console.log(intList) + console.log(intList + "") + + console.log(objectList) + console.log(objectList + "") + + console.log(["a", "b"]); + + // TODO: Cannot do this, yet, because we cannot coerce a list to string on the fly. + // We need to store it as list first. + // console.log(["a", "b"] + ""); + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/listprovider.h b/tests/auto/qml/qmlcppcodegen/data/listprovider.h new file mode 100644 index 0000000000..076944b586 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/listprovider.h @@ -0,0 +1,24 @@ +#ifndef QLISTPROVIDER_H +#define QLISTPROVIDER_H + +#include <QObject> +#include <QQmlEngine> + +class QListProvider : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(ListProvider) + +public: + explicit QListProvider(QObject *parent = nullptr) : QObject(parent) { } + + Q_INVOKABLE QList<int> intList() const + { + QList<int> list; + for (int i = 0; i < 3; ++i) + list.append(i); + return list; + } +}; + +#endif // QLISTPROVIDER_H diff --git a/tests/auto/qml/qmlcppcodegen/data/math.qml b/tests/auto/qml/qmlcppcodegen/data/math.qml index cc6cd3741a..ad6303e682 100644 --- a/tests/auto/qml/qmlcppcodegen/data/math.qml +++ b/tests/auto/qml/qmlcppcodegen/data/math.qml @@ -3,4 +3,5 @@ import QML QtObject { property int a: Math.max(5, 7, 9, -111) property var b: 50 / 22 + property real c: Math.PI * 2 } diff --git a/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml b/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml new file mode 100644 index 0000000000..654b699918 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml @@ -0,0 +1,59 @@ +pragma Strict +import QtQml +import QtQuick + +Rectangle { + Component.onCompleted: { + // Math.max() + console.log(Math.max(1, 1)); + console.log(Math.max(1, 2)); + console.log(Math.max(2, 1)); + console.log(Math.max(0, 0)); + console.log(Math.max(-1, 0)); + console.log(Math.max(0, -1)); + console.log(Math.max(-1, -1)); + + console.log(Math.max(0, 0, 0)); + console.log(Math.max(0, 0, 1, 0, 0, 0)); + console.log(Math.max(-2, -1, 0, 1, 2)); + console.log(Math.max(2, 1, 0, -1, -2)); + console.log(Math.max(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + console.log(Math.max(0.0, 0.0, 0.0)) + console.log(Math.max(-0.001, 0.001, 0.002)) + console.log(Math.max(5.4, 1, 0.002)) + console.log(Math.max(null, 0, -1, 8E-2, NaN, undefined, true, false, Infinity)) + console.log(Math.max(0, -1, 8E-2, true, false, Infinity)) + console.log(Math.max(0, -1, 8E-2, true, false)) + console.log(Math.max(0, -1, 8E-2, false)) + console.log(Math.max(0, -1, 8E-2, true, false, Infinity)) + console.log(Math.max(-1, -8, null)) + console.log(Math.max(undefined, 20, 70)) + + // Math.min() + console.log(Math.min(1, 1)); + console.log(Math.min(1, +2)); + console.log(Math.min(2, 1)); + console.log(Math.min(0, 0)); + console.log(Math.min(-1, 0)); + console.log(Math.min(0, -1)); + console.log(Math.min(-1, -1)); + + console.log(Math.min(0, 0, 0)); + console.log(Math.min(0, 0, 1, 0, 0, 0)); + console.log(Math.min(-2, -1, 0, 1, 2)); + console.log(Math.min(2, 1, 0, -1, -2)); + console.log(Math.min(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + console.log(Math.min(0.0, 0.0, 0.0)) + console.log(Math.min(-0.001, 0.001, 0.002)) + console.log(Math.min(5.4, 1, 0.002)) + console.log(Math.min(null, 0, -1, 8E-2, NaN, undefined, true, false, Infinity)) + console.log(Math.min(0, -1, 8E-2, true, false, Infinity)) + console.log(Math.min(0, -1, 8E-2, true, false)) + console.log(Math.min(0, -1, 8E-2, false)) + console.log(Math.min(0, -1, 8E-2, true, false, Infinity)) + console.log(Math.min(-1, -8, null)) + console.log(Math.min(undefined, 20, 70)) + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml new file mode 100644 index 0000000000..fad74a28bd --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +pragma Strict + +import QML + +QtObject { + property double e: Math.E + property double ln10: Math.LN10 + property double ln2: Math.LN2 + property double log10e: Math.LOG10E + property double log2e: Math.LOG2E + property double pi: Math.PI + property double sqrt1_2: Math.SQRT1_2 + property double sqrt2: Math.SQRT2 +} diff --git a/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml b/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml new file mode 100644 index 0000000000..161e21e643 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml @@ -0,0 +1,14 @@ +pragma Strict +import QtQuick + +Item { + objectName: "a" + + function f(arg: Item) : string { + // Read arg as QtObject and Item, merged into QtObject. + console.log(arg) + return arg.x + } + + Component.onCompleted: objectName = f(null) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml b/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml new file mode 100644 index 0000000000..5f4bb4ff87 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml @@ -0,0 +1,15 @@ +pragma Strict +import QtQuick + +Item { + objectName: "a" + + function f(arg: Item) : string { + // Write arg as Item, read it as QtObject. + arg.x = 5 + console.log(arg) + return arg.objectName + } + + Component.onCompleted: objectName = f(null) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml b/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml new file mode 100644 index 0000000000..b6b7179438 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml @@ -0,0 +1,16 @@ +pragma Strict +import QtQml +import TestTypes + +QtObject { + objectName: people[0].getName() + property list<Person> people: [ + Person { + name: "no one" + } + ] + + function boom() : string { + return people[1].getName() + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/methods.qml b/tests/auto/qml/qmlcppcodegen/data/methods.qml index 3abd14c9c1..c045c2249b 100644 --- a/tests/auto/qml/qmlcppcodegen/data/methods.qml +++ b/tests/auto/qml/qmlcppcodegen/data/methods.qml @@ -35,6 +35,8 @@ BirthdayParty { } function stuff(sn) { + // Warning: to not test for signal handlers like this in actual code. + // Use the helper methods in QQmlSignalNames instead. if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase()) return sn return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1) diff --git a/tests/auto/qml/qmlcppcodegen/data/multiforeign.h b/tests/auto/qml/qmlcppcodegen/data/multiforeign.h index 290b6370f5..6c46d5ad86 100644 --- a/tests/auto/qml/qmlcppcodegen/data/multiforeign.h +++ b/tests/auto/qml/qmlcppcodegen/data/multiforeign.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef MULTIFOREIGN_H #define MULTIFOREIGN_H diff --git a/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml b/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml new file mode 100644 index 0000000000..61dfdb7ca5 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml @@ -0,0 +1,13 @@ +pragma Strict + +import TestTypes +import QtQml + +QtObject { + property rect r: Qt.rect(1, 2, 3, 4) + property point p: Qt.point(5, 6); + + property withLength wr: r + property withLength wp: p + property withLength wi: 17 +} diff --git a/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml b/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml new file mode 100644 index 0000000000..8fe47b7296 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml @@ -0,0 +1,33 @@ +import QtQuick + +Item { + id: root + visible: true + + property var speaker + signal say_hello() + + Component{ + id: speakerComp + Text { + text: "HELLO" + function say_hello() { + console.log(text) + } + } + } + + Timer { + interval: 1; running: true; repeat: false + onTriggered: root.say_hello(); + } + + Component.onCompleted: + { + root.speaker = speakerComp.createObject(root); + + root.say_hello.connect(root.speaker.say_hello); + + root.speaker.destroy(); + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml index 1f9af7169b..53b3697c9b 100644 --- a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml +++ b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml @@ -6,6 +6,7 @@ QtObject { property int w: 1 property int x: 1 property int y: 1 + property int z: 1 Component.onCompleted: { var g = null; if (g !== null) { @@ -22,5 +23,15 @@ QtObject { if (h === undefined) { y = 5; } + + var o = this; + if (o != null) + z += 7; + if (o == null) + z += 6; + if (g == null) + z += 10; + if (g != null) + z += 20; } } diff --git a/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml b/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml new file mode 100644 index 0000000000..f84f93c5d2 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml @@ -0,0 +1,37 @@ +pragma Strict +pragma ValueTypeBehavior: Addressable + +import QtQuick + +GOL_Object { + id: root + + property int p1: 5 ?? -1 + property string p2: "6" ?? "-1" + + property var p3: undefined ?? undefined + property var p4: undefined ?? null + property var p5: undefined ?? -1 + property var p6: undefined ?? "-1" + + property var p7: null ?? undefined + property var p8: null ?? null + property var p9: null ?? -1 + property var p10: null ?? "-1" + + property int p11: GOL_Object.V2 ?? "-1" + + property int p12: 1 ?? 2 ?? 3 + property int p13: "1" ?? "2" ?? "3" + property var p14: undefined ?? "2" ?? undefined + property var p15: undefined ?? undefined ?? 1 + + property var p16 + property var p17 + + Component.onCompleted: { + p16 = (root.childA as GOL_Object)?.i ?? -1 + root.childA = root + p17 = (root.childA as GOL_Object)?.i ?? -1 + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml index f12d3f5ea2..ec848429e8 100644 --- a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml +++ b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml @@ -4,23 +4,57 @@ import TestTypes QtObject { function writeValues() { + Druggeljug.myInt8 = 35 + Druggeljug.myUint8 = 36 + Druggeljug.myInt16 = 37 + Druggeljug.myUint16 = 38 Druggeljug.myInt = 39 Druggeljug.myUint = 40 Druggeljug.myInt32 = 41 Druggeljug.myUint32 = 42 } + function negateValues() { + Druggeljug.myInt8 = -Druggeljug.myInt8; + Druggeljug.myUint8 = -Druggeljug.myUint8; + Druggeljug.myInt16 = -Druggeljug.myInt16; + Druggeljug.myUint16 = -Druggeljug.myUint16; + Druggeljug.myInt = -Druggeljug.myInt; + Druggeljug.myUint = -Druggeljug.myUint; + Druggeljug.myInt32 = -Druggeljug.myInt32; + Druggeljug.myUint32 = -Druggeljug.myUint32; + } + + function shuffleValues() { + Druggeljug.myInt8 = Druggeljug.myUint8; + Druggeljug.myUint8 = Druggeljug.myInt16; + Druggeljug.myInt16 = Druggeljug.myUint16; + Druggeljug.myUint16 = Druggeljug.myInt; + Druggeljug.myInt = Druggeljug.myUint; + Druggeljug.myUint = Druggeljug.myInt32; + Druggeljug.myInt32 = Druggeljug.myUint32; + Druggeljug.myUint32 = Druggeljug.myInt8; + } + function readValueAsString(i: int) : string { switch (i) { - case 0: return Druggeljug.myInt; - case 1: return Druggeljug.myUint; - case 2: return Druggeljug.myInt32; - case 3: return Druggeljug.myUint32; + case 0: return Druggeljug.myInt8; + case 1: return Druggeljug.myUint8; + case 2: return Druggeljug.myInt16; + case 3: return Druggeljug.myUint16; + case 4: return Druggeljug.myInt; + case 5: return Druggeljug.myUint; + case 6: return Druggeljug.myInt32; + case 7: return Druggeljug.myUint32; default: return ""; } } function storeValues() { + Druggeljug.storeMyInt8(1330) + Druggeljug.storeMyUint8(1331) + Druggeljug.storeMyInt16(1332) + Druggeljug.storeMyUint16(1333) Druggeljug.storeMyInt(1334) Druggeljug.storeMyUint(1335) Druggeljug.storeMyInt32(1336) diff --git a/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml b/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml new file mode 100644 index 0000000000..4804921b02 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml @@ -0,0 +1,34 @@ +pragma Strict +import QtQuick + +Item { + id: stack + + property int current: 0 + + onCurrentChanged: setZOrders() + Component.onCompleted: setZOrders() + + function setZOrders() { + for (var i = 0; i < Math.max(stack.children.length, 3); ++i) { + stack.children[i].z = (i == current ? 1 : 0) + stack.children[i].enabled = (i == current) + } + } + + function zOrders() : list<int> { + return [ + stack.children[0].z, + stack.children[1].z, + stack.children[2].z + ] + } + + function clearChildren() { + children.length = 0; + } + + Item {} + Item {} + Item {} +} diff --git a/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml b/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml new file mode 100644 index 0000000000..14f84c57d0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml @@ -0,0 +1,7 @@ +import QtQml +import TestTypes + +QtObject { + readonly property ObjectWithStringListMethod foo: ObjectFactory.getFoo() + Component.onCompleted: console.log(foo ? foo.names().length : "-") +} diff --git a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h index 348862985f..f43a0d5531 100644 --- a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h +++ b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef OBJECTWITHMETOD_H #define OBJECTWITHMETOD_H @@ -7,6 +7,7 @@ #include <QtCore/qobject.h> #include <QtCore/qproperty.h> #include <QtQml/qqml.h> +#include <QtQml/private/qv4engine_p.h> // Make objectName available. It doesn't exist on the builtin QtObject type struct QObjectForeignForObjectName { @@ -27,6 +28,20 @@ public: Q_INVOKABLE int doThing() const { return theThing; } QProperty<int> theThing; QBindable<int> theThingBindable() { return QBindable<int>(&theThing); } + + // The meta methods are populated back to front. + // The V4Function flag should not bleed into the others in either case. + + Q_INVOKABLE void overloaded(QQmlV4FunctionPtr) { setObjectName(QStringLiteral("javaScript")); } + Q_INVOKABLE void overloaded(double) { setObjectName(QStringLiteral("double")); } + Q_INVOKABLE void overloaded(const QString &) { setObjectName(QStringLiteral("string")); } + + Q_INVOKABLE void foo(const QString &bla) { setObjectName(bla); } + Q_INVOKABLE void foo(ObjectWithMethod *) { setObjectName(QStringLiteral("ObjectWithMethod")); } + + Q_INVOKABLE void overloaded2(double) { setObjectName(QStringLiteral("double")); } + Q_INVOKABLE void overloaded2(const QString &) { setObjectName(QStringLiteral("string")); } + Q_INVOKABLE void overloaded2(QQmlV4FunctionPtr) { setObjectName(QStringLiteral("javaScript")); } }; class OverriddenObjectName : public ObjectWithMethod @@ -56,4 +71,40 @@ private: QProperty<QString> m_objectName; }; +class ObjectWithStringListMethod : public QObject +{ + Q_OBJECT + QML_ELEMENT + +public: + explicit ObjectWithStringListMethod(QObject *parent = nullptr) : QObject(parent) + { + m_names.append("One"); + m_names.append("Two"); + } + + Q_INVOKABLE QStringList names() const { return m_names; } + +private: + QStringList m_names; +}; + +class ObjectFactory : public QObject { + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + +public: + explicit ObjectFactory(QObject *parent = nullptr) : QObject(parent) {} + Q_INVOKABLE ObjectWithStringListMethod *getFoo() + { + if (!m_foo) + m_foo = new ObjectWithStringListMethod(this); + return m_foo; + } + +private: + ObjectWithStringListMethod *m_foo = nullptr; +}; + #endif // OBJECTWITHMETHOD_H diff --git a/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml b/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml new file mode 100644 index 0000000000..4dbc541721 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml @@ -0,0 +1,77 @@ +pragma Strict +import QtQml + +QtObject { + property list<QtObject> elms: [this, null, null] + + property int found: find(this) + property int foundNot: findNot(this) + property int foundStrict: findStrict(this) + property int foundStrictNot: findStrictNot(this) + + function find(elm : QtObject) : int { + let found = 0; + for (var i = 0; i < elms.length; i++) { + var value = elms[i]; + if (value == elm) + ++found; + } + return found; + } + + function findNot(elm : QtObject) : int { + let found = 0; + for (var i = 0; i < elms.length; i++) { + var value = elms[i]; + if (value != elm) + ++found; + } + return found; + } + + function findStrict(elm : QtObject) : int { + let found = 0; + for (var i = 0; i < elms.length; i++) { + var value = elms[i]; + if (value === elm) + ++found; + } + return found; + } + + function findStrictNot(elm : QtObject) : int { + let found = 0; + for (var i = 0; i < elms.length; i++) { + var value = elms[i]; + if (value !== elm) + ++found; + } + return found; + } + + property bool optionalNull: { + let a // Produces a QJsPrimitiveValue we can compare to null below + if (objectName.length === 0) + a = null + else + a = undefined + + return a === null + } + + property int undefinedEqualsUndefined: { + var matches = 0; + + // Overrun the array so that we get some undefined !== undefined. + for (var i = 0; i < 4; i++) { + var val1 = elms[i] + for (var j = 0; j < 4; j++) { + var val2 = elms[j] + if (!(val1 !== val2)) + ++matches + } + } + + return matches; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/person.cpp b/tests/auto/qml/qmlcppcodegen/data/person.cpp index 4dcd6fd56f..946dfbcdaa 100644 --- a/tests/auto/qml/qmlcppcodegen/data/person.cpp +++ b/tests/auto/qml/qmlcppcodegen/data/person.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "person.h" @@ -98,3 +98,21 @@ void Person::setCousins(const QList<Person *> &newCousins) m_cousins = newCousins; emit cousinsChanged(); } + +QRectF Person::area() const +{ + return m_area; +} + +void Person::setArea(const QRectF &newArea) +{ + if (m_area.valueBypassingBindings() == newArea) + return; + m_area = newArea; + emit areaChanged(); +} + +QBindable<QRectF> Person::areaBindable() +{ + return QBindable<QRectF>(&m_area); +} diff --git a/tests/auto/qml/qmlcppcodegen/data/person.h b/tests/auto/qml/qmlcppcodegen/data/person.h index fba4a9e9a5..c46aa757a7 100644 --- a/tests/auto/qml/qmlcppcodegen/data/person.h +++ b/tests/auto/qml/qmlcppcodegen/data/person.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef PERSON_H #define PERSON_H @@ -8,6 +8,35 @@ #include <QtQml/qqml.h> #include <QtQml/qqmlengine.h> #include <QtCore/qproperty.h> +#include <QtCore/qrect.h> + +struct Inner +{ + Q_GADGET + QML_VALUE_TYPE(inner) + QML_STRUCTURED_VALUE + Q_PROPERTY(int i MEMBER i) + +private: + friend bool operator==(const Inner &lhs, const Inner &rhs) { return lhs.i == rhs.i; } + friend bool operator!=(const Inner &lhs, const Inner &rhs) { return !(lhs == rhs); } + + int i = 11; +}; + +struct Outer +{ + Q_GADGET + QML_VALUE_TYPE(outer) + QML_STRUCTURED_VALUE + Q_PROPERTY(Inner inner MEMBER inner) + +private: + friend bool operator==(const Outer &lhs, const Outer &rhs) { return lhs.inner == rhs.inner; } + friend bool operator!=(const Outer &lhs, const Outer &rhs) { return !(lhs == rhs); } + + Inner inner; +}; // Intentionally opaque type class Barzle : public QObject {}; @@ -22,6 +51,8 @@ class Person : public QObject Q_PROPERTY(QList<Barzle *> barzles READ barzles WRITE setBarzles NOTIFY barzlesChanged FINAL) Q_PROPERTY(QList<Person *> cousins READ cousins WRITE setCousins NOTIFY cousinsChanged FINAL) Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged FINAL) + Q_PROPERTY(QRectF area READ area WRITE setArea NOTIFY areaChanged) // not FINAL + Q_PROPERTY(QRectF area2 READ area WRITE setArea NOTIFY areaChanged BINDABLE areaBindable FINAL) QML_ELEMENT public: Person(QObject *parent = nullptr); @@ -52,6 +83,12 @@ public: QList<Person *> cousins() const; void setCousins(const QList<Person *> &newCousins); + QRectF area() const; + void setArea(const QRectF &newArea); + QBindable<QRectF> areaBindable(); + + Q_INVOKABLE QString getName() const { return m_name; } + signals: void nameChanged(); void shoeSizeChanged(); @@ -62,6 +99,10 @@ signals: void ambiguous(int a = 9); void cousinsChanged(); + void objectListHappened(const QList<QObject *> &); + void variantListHappened(const QList<QVariant> &); + + void areaChanged(); private: QString m_name; @@ -70,6 +111,7 @@ private: QList<Barzle *> m_barzles; QList<Person *> m_cousins; QProperty<QByteArray> m_data; + QProperty<QRectF> m_area; }; class BarzleListRegistration diff --git a/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml new file mode 100644 index 0000000000..c7103eaf05 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml @@ -0,0 +1,13 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +pragma Strict + +import QtQuick + +Window { + // If static properties of the Math global object are not directly + // supported, a warning should be issued in turn failing the build + // due to `pragma Strict`. + width: 200 * Math.PI +} diff --git a/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml b/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml new file mode 100644 index 0000000000..d0176e6b15 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml @@ -0,0 +1,16 @@ +import QtQml +import TestTypes + +QtObject { + id: root + + property int priority: Backend.gadget.VeryHigh + property int prop2: Backend.priority + + property bool priorityIsVeryHigh: root.priority == Backend.VeryHigh + + function cyclePriority() : int { + root.priority = Backend.gadget.High; + return root.priority; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml b/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml new file mode 100644 index 0000000000..149638283b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml @@ -0,0 +1,17 @@ +import QtQml + +QtObject { + id: testObj + + // "readonly" means the identity of the list cannot be changed. + // Its contents can be changed. + readonly default property list<QtObject> theList + + Component.onCompleted: { + for (var i = 0; i < 4; i++) + testObj.theList.push(testObj) + } + + property int l: theList.length +} + diff --git a/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml new file mode 100644 index 0000000000..c6fda8c739 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml @@ -0,0 +1,18 @@ +import QtQml + +QtObject { + id: mainItem + property int topPadding: 12 + property int bottomPadding: 12 + + property int preferredHeight: mainItem.children.reduce(maximumImplicitHeightReducer, 0) + topPadding + bottomPadding + function maximumImplicitHeightReducer(accumulator: real, item: Binding): real { + return Math.max(accumulator, (item.objectName + "b").length); + } + + property int preferredHeight2: mainItem.children.reduce((accumulator, item) => { + return Math.max(accumulator, (item.objectName + "b").length); + }, 0) + topPadding + bottomPadding + + property list<Binding> children: [ Binding { objectName: "aaa" } ] +} diff --git a/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml new file mode 100644 index 0000000000..9352163ba7 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml @@ -0,0 +1,19 @@ +pragma Strict +import QtQml + +QtObject { + id: last + property int value: 10 + + function verify(i: int) { + if (last.value !== i) + console.error("failed", last.value, i); + else + console.log("success") + } + + Component.onCompleted: { + verify(10) + verify(11) + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.h b/tests/auto/qml/qmlcppcodegen/data/resettable.h new file mode 100644 index 0000000000..755c8de237 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/resettable.h @@ -0,0 +1,39 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef RESETTABLE_H +#define RESETTABLE_H + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class ResettableProperty : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(Resettable) + Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal shadowable READ shadowable CONSTANT) + +public: + explicit ResettableProperty(QObject *parent = nullptr) : QObject(parent) {} + qreal value() const { return m_value; } + qreal shadowable() const { return 25; } + +public slots: + void resetValue() { setValue(0); } + void setValue(qreal value) + { + if (m_value == value) + return; + m_value = value; + emit valueChanged(); + } + +signals: + void valueChanged(); + +private: + qreal m_value = 0; +}; + +#endif // RESETTABLE_H diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.qml b/tests/auto/qml/qmlcppcodegen/data/resettable.qml new file mode 100644 index 0000000000..561655032d --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/resettable.qml @@ -0,0 +1,23 @@ +pragma Strict +import QtQml +import TestTypes + +Resettable { + id: self + value: 999 + + property double notResettable: 10 + property double notResettable2: { return undefined } + + property Resettable shadowing: Resettable { + property var shadowable: undefined + } + + function doReset() { self.value = undefined } + function doReset2() { self.value = shadowing.shadowable } + function doNotReset() { self.notResettable = undefined } + + signal aaa() + signal bbb() + onAaa: objectName = self.bbb() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml b/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml new file mode 100644 index 0000000000..e0b85eb270 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml @@ -0,0 +1,15 @@ +import QtQml + +QtObject { + id: remaining + + property int bar: 0 + + Component.onCompleted: { + let remainingTime = 123 + if (remainingTime < 0) { + remainingTime += 24 * 60 * 60 + } + remaining.bar = isNaN(remainingTime) ? 0 : remainingTime + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml new file mode 100644 index 0000000000..e23f180598 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml @@ -0,0 +1,20 @@ +pragma ComponentBehavior: Bound + +import QtQml + +QtObject { + id: root + + property QtObject b: QtObject { + id: bar + objectName: "outer" + } + + property Instantiator i: Instantiator { + model: 1 + delegate: QtObject { + property QtObject bar: QtObject { objectName: "inner" } + Component.onCompleted: root.objectName = bar.objectName + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml b/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml new file mode 100644 index 0000000000..8b9f161b06 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml @@ -0,0 +1,19 @@ +import QtQml +import TestTypes + +QtObject { + property int good: Data.DType.A + property int bad: Data.A + + property int wrong: Data.EType.C + property int right: Data.C + + property int notgood: Data2.DType.A + property int notbad: Data2.A + + property int notwrong: Data2.EType.C + property int notright: Data2.C + + property int passable: Enums.AppState.Blue + property int wild: Enums.Green +} diff --git a/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h new file mode 100644 index 0000000000..76c72fff36 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h @@ -0,0 +1,56 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef SEQUENCETOITERABLE_H +#define SEQUENCETOITERABLE_H + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class Entry : public QObject { + Q_OBJECT + +public: + explicit Entry(const QString &name, QObject *parent = nullptr) + : QObject(parent), m_name(name) + { + setObjectName(name); + } + +private: + QString m_name; +}; + +class EntryWrapper { + Q_GADGET + QML_FOREIGN(Entry) + QML_NAMED_ELEMENT(Entry) + QML_UNCREATABLE("These are my Entry objects") +}; + +class EntryListRegistration +{ + Q_GADGET + QML_FOREIGN(QList<Entry*>) + QML_ANONYMOUS + QML_SEQUENTIAL_CONTAINER(Entry*) +}; + +class EntrySource : public QObject { + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + +public: + explicit EntrySource(QObject* parent = nullptr) : QObject(parent) { + for (int i = 0; i < 10; i++) { + m_entries.push_back(new Entry(QString("Item %1").arg(i), this)); + } + } + Q_INVOKABLE QList<Entry*> getEntries() const { return m_entries; } + +private: + QList<Entry*> m_entries; +}; + +#endif // SEQUENCETOITERABLE_H diff --git a/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml new file mode 100644 index 0000000000..23e2645128 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml @@ -0,0 +1,20 @@ +pragma Strict +import QtQuick +import TestTypes + +Item { + Component.onCompleted: () => { + repeater.model = EntrySource.getEntries() + } + + Repeater { + id: repeater + Item { + required property int index + required property QtObject modelData + objectName: modelData + ": " + index + } + } + + property int c: children.length +} diff --git a/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml b/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml new file mode 100644 index 0000000000..404ee8653b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml @@ -0,0 +1,17 @@ +pragma Strict +import QtQml + +QtObject { + id: first + property int value + + objectName: a.objectName + property QtObject a: QtObject {} + function t() { a.objectName = "a" } + + Component.onCompleted: { + for (let i = 0; i < 10; ++i) { + first.value = i; + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml b/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml new file mode 100644 index 0000000000..9975bfdfa4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml @@ -0,0 +1,17 @@ +pragma Strict +import QtQml + +QtObject { + property Component newEditConstraint: EditConstraint {} + property Variable variable: Variable {} + property EditConstraint edit + + function trigger() { + change(variable, 55); + } + + function change(v: Variable, newValue: int) { + edit = newEditConstraint.createObject(null, {myOutput: v}) as EditConstraint; + v.value = newValue; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml new file mode 100644 index 0000000000..ccb50f4934 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml @@ -0,0 +1,31 @@ +pragma Strict +import QtQml + +QtObject { + property ShadowedObjectName shadowed1: ShadowedObjectName {} + property ShadowedObjectName shadowed2: ShadowedObjectName {} + property QtObject shadowed3: ShadowedObjectNameDerived {} + + function returnShadowed2() : QtObject { return shadowed2 } + + function a(mark: int) { + // as-cast can be optimized out if we're clever. + (shadowed1 as QtObject).objectName = mark; + } + + function b(mark: int) { + // method return values can contain shadowed properties! + returnShadowed2().objectName = mark; + } + + function c(mark: int) { + // Has to do an actual as-cast, but results in ShadowedObjectNameDerived! + (shadowed3 as ShadowedObjectName).objectName = mark; + } + + Component.onCompleted: { + a(43); + b(42); + c(41); + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml new file mode 100644 index 0000000000..590fb40b17 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml @@ -0,0 +1,35 @@ +pragma Strict +import QtQuick + +Item { + component B: Item { + function contains(point: point) : string { + return "b" + } + } + + + component C: Item { + function contains(point: point) : string { + return "c" + } + } + + property Item a: Item {} + property B b: B {} + property C c: C {} + + function doThing() : var { return a.contains(Qt.point(0, 0)) } + + property var athing; + property var bthing; + property var cthing; + + Component.onCompleted: { + athing = doThing(); + a = b; + bthing = doThing(); + a = c; + cthing = doThing(); + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml new file mode 100644 index 0000000000..0e130b9afc --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml @@ -0,0 +1,16 @@ +import QtQuick + +QtObject { + id: win + + component Foo: QtObject { + property int progress: 0 + } + + property int progress: 0 + readonly property Foo configuring: Foo {} + + Component.onCompleted: { + win.configuring.progress = win?.progress ?? 0 + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml index 58a567f4db..3a0349966d 100644 --- a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml +++ b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml b/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml new file mode 100644 index 0000000000..9a07b206d4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml @@ -0,0 +1,18 @@ +pragma Strict +import QtQml +import TestTypes + +Person { + property list<var> varlist: [1, "foo", this, undefined, true] + property list<QtObject> objlist: [this, null, this] + + function sendSignals() { + variantListHappened(varlist); + objectListHappened(objlist); + } + + property int happening: 0 + + onObjectListHappened: (objects) => { happening += objects.length } + onVariantListHappened: (variants) => { happening += variants.length } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/state.h b/tests/auto/qml/qmlcppcodegen/data/state.h index 1afcebc4ea..708e681781 100644 --- a/tests/auto/qml/qmlcppcodegen/data/state.h +++ b/tests/auto/qml/qmlcppcodegen/data/state.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef STATE_H #define STATE_H diff --git a/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml b/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml new file mode 100644 index 0000000000..158dff2d0b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml @@ -0,0 +1,8 @@ +pragma Strict +import QtQml + +QtObject { + property rect r: ({x: 1, y: 2, width: 3, height: 4}) + property rect r2: { var x = 42; return {x}; } + property weatherModelUrl w: ({ strings: ["one", "two", "three"] }) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/theme.cpp b/tests/auto/qml/qmlcppcodegen/data/theme.cpp index c8a2612753..10302645c0 100644 --- a/tests/auto/qml/qmlcppcodegen/data/theme.cpp +++ b/tests/auto/qml/qmlcppcodegen/data/theme.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "theme.h" diff --git a/tests/auto/qml/qmlcppcodegen/data/theme.h b/tests/auto/qml/qmlcppcodegen/data/theme.h index f70a3b440e..3e56ec1f9f 100644 --- a/tests/auto/qml/qmlcppcodegen/data/theme.h +++ b/tests/auto/qml/qmlcppcodegen/data/theme.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef THEME_H #define THEME_H diff --git a/tests/auto/qml/qmlcppcodegen/data/thisObject.qml b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml new file mode 100644 index 0000000000..50664eced2 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml @@ -0,0 +1,11 @@ +pragma Strict +import QtQml + +QtObject { + property QtObject warned + + function f(arg: QtObject) { warned = arg } + function warn() { f(this) } + + Component.onCompleted: warn() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp index 1853d2dc12..04e1797c7c 100644 --- a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp +++ b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "timelinetheme.h" diff --git a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h index 335e43b570..3e7da77cc9 100644 --- a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h +++ b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TIMELINETHEME_H #define TIMELINETHEME_H diff --git a/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml b/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml new file mode 100644 index 0000000000..a73e28f642 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml @@ -0,0 +1,14 @@ +pragma Strict +import QtQml + +Component { + QtObject { + id: root + + function myOpen() { + root.objectName = "foo" + } + + Component.onCompleted: myOpen() + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml b/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml new file mode 100644 index 0000000000..e7bf5ccec9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + objectName: "??= ??/ ??' ??( ??) ??! ??< ??> ??-" +} diff --git a/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp b/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp new file mode 100644 index 0000000000..02629ad7f6 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. + +#include <QtTest> +#include <QtCore/qobject.h> +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> + +class tst_QmlCppCodegenVerify : public QObject +{ + Q_OBJECT +private slots: + void verifyGeneratedSources_data(); + void verifyGeneratedSources(); +}; + +void tst_QmlCppCodegenVerify::verifyGeneratedSources_data() +{ + QTest::addColumn<QString>("file"); + + QDir a(":/a"); + const QStringList entries = a.entryList(QDir::Files); + for (const QString &entry : entries) + QTest::addRow("%s", entry.toUtf8().constData()) << entry; +} + +void tst_QmlCppCodegenVerify::verifyGeneratedSources() +{ + QFETCH(QString, file); + QFile a(":/a/" + file); + QFile b(":/b/" + file.replace("codegen_test_module", "codegen_test_module_verify")); + + QVERIFY(a.open(QIODevice::ReadOnly)); + QVERIFY(b.open(QIODevice::ReadOnly)); + + const QByteArray aData = a.readAll(); + const QByteArray bData = b.readAll() + .replace("verify/TestTypes", "TestTypes") + .replace("verify_TestTypes", "TestTypes"); + + QCOMPARE(aData, bData); +} + +QTEST_MAIN(tst_QmlCppCodegenVerify) + +#include "tst_qmlcppcodegen_verify.moc" diff --git a/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml new file mode 100644 index 0000000000..e76443a2e0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +QtObject { + property double d: Math.max(undefined, 40) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/urlString.qml b/tests/auto/qml/qmlcppcodegen/data/urlString.qml index 511c54532c..a83855ebdb 100644 --- a/tests/auto/qml/qmlcppcodegen/data/urlString.qml +++ b/tests/auto/qml/qmlcppcodegen/data/urlString.qml @@ -9,5 +9,12 @@ QtObject { Component.onCompleted: { c = "http://dddddd.com"; self.d = "http://aaaaaa.com"; + myUrlChanged(c) + } + + signal myUrlChanged(urlParam: url) + + onMyUrlChanged: (urlParam) => { + objectName = urlParam; } } diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml new file mode 100644 index 0000000000..a775773dda --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml @@ -0,0 +1,43 @@ +pragma ValueTypeBehavior: Addressable +import QtQml + +QtObject { + id: root + property rect r: Qt.rect(10, 20, 3, 4) + property var v: r + property real x: (v as rect).x + + function f(input: bool) : var { + if (input) + return 0 + return Qt.point(2, 2) + } + + property var vv: Qt.point(5, 5) + property var uu: undefined + + property int tv3: (root.vv as point)?.x + property var tv4: (root.uu as rect)?.x + property int tc3: (root?.vv as point)?.y + property var tc6: (root?.uu as rect)?.height + property var tc7: (f(true) as point)?.x + property var tc8: (f(false) as point)?.x + + property string greeting1 + property string greeting2 + + readonly property string defaultGreeting: "Default Greeting" + property QtObject o: QtObject { + id: o + property var customGreeting + function greet() : string { + return (o.customGreeting as string) ?? root.defaultGreeting + } + } + + Component.onCompleted: { + root.greeting1 = o.greet() + o.customGreeting = "Custom Greeting" + root.greeting2 = o.greet() + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml index cca634753d..d33133bb6b 100644 --- a/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml +++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml @@ -1,4 +1,4 @@ -pragma ValueTypeBehavior: Copy +pragma ValueTypeBehavior: Copy, Addressable import QtQml QtObject { diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml new file mode 100644 index 0000000000..42a55e832d --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml @@ -0,0 +1,34 @@ +import QtQml + + +QtObject { + id: root + + property list<double> numbers: { + var result = []; + for (var i = 0; i < 10; ++i) + result[i] = i; + return result; + } + + property rect r: ({x: 1, y: 2, width: 3, height: 4}) + + function evil() : double { + var numbers = root.numbers; + root.numbers = []; + var a = 0; + for (var j = 0; j < 10; ++j) { + a += numbers[j]; + } + return a; + } + + function fvil() : double { + var r = root.r; + root.r = {x: 5, y: 6, width: 7, height: 8}; + return r.x; + } + + property double e: evil() + property double f: fvil() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml index 568f39820c..a87f88ecf9 100644 --- a/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml +++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml @@ -1,4 +1,4 @@ -pragma ValueTypeBehavior: Reference +pragma ValueTypeBehavior: Reference, Inaddressable import QtQml QtObject { diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMap.qml b/tests/auto/qml/qmlcppcodegen/data/variantMap.qml new file mode 100644 index 0000000000..d7147ec5fc --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/variantMap.qml @@ -0,0 +1,27 @@ +pragma Strict +import QtQml + +QtObject { + property Component shadowable: QtObject {} + property B b: B { id: theB } + property rect r: theB.r + property var v: { "1": null, "25": undefined, "19": "19" } + + property Component c: Component { + id: unshadowable + QtObject {} + } + + // We need this extra function in order to coerce the result of the shadowable + // method call back to QtObject + function createShadowable() : QtObject { + return shadowable.createObject(this, {objectName: "a"}) + } + + objectName: { + return createShadowable().objectName + + " " + unshadowable.createObject(this, {objectName: "b"}).objectName + } + + Component.onCompleted: b.r = { x: 12, y: 13, width: 14, height: 15 } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h new file mode 100644 index 0000000000..61d38228b0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h @@ -0,0 +1,17 @@ +#pragma once +#include <QObject> +#include <QVariantMap> +#include <QtQml/qqmlregistration.h> + +class VariantMapLookupFoo : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QVariantMap data READ data CONSTANT) + +public: + VariantMapLookupFoo(QObject *parent = nullptr) : QObject(parent) { } + +private: + QVariantMap data() const { return { { "value", 42 } }; } +}; diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml new file mode 100644 index 0000000000..4e56cb9448 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml @@ -0,0 +1,11 @@ +pragma Strict +import TestTypes +import QtQuick + +Item { + property int i: moo.data.value + + VariantMapLookupFoo { + id: moo + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml b/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml new file mode 100644 index 0000000000..cca26265c9 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml @@ -0,0 +1,15 @@ +pragma Strict +import QtQml +import TestTypes + +QtObject { + property DirectBindable a: DirectBindable { + id: aId + x: WeatherModelUrlUtils.url(1) + } + + property IndirectBindable b: IndirectBindable { + id: bId + y: aId.x + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/variantreturn.h b/tests/auto/qml/qmlcppcodegen/data/variantreturn.h new file mode 100644 index 0000000000..87718aaef3 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/variantreturn.h @@ -0,0 +1,63 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef VARIANTERETURN_H +#define VARIANTERETURN_H + +#include <QtCore/qobject.h> +#include <QtCore/qproperty.h> +#include <QtQml/qqmlregistration.h> + +#include "weathermoduleurl.h" + +class DirectBindable : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(WeatherModelUrl x READ x WRITE setX NOTIFY xChanged BINDABLE bindableX) + +public: + explicit DirectBindable(QObject *parent = nullptr) : QObject(parent) {} + + WeatherModelUrl x() const { return m_x.value(); } + void setX(const WeatherModelUrl& newX) { m_x.setValue(newX);} + QBindable<WeatherModelUrl> bindableX() { return QBindable<WeatherModelUrl>(&m_x); } + +Q_SIGNALS: + void xChanged(); + +private: + Q_OBJECT_BINDABLE_PROPERTY(DirectBindable, WeatherModelUrl, m_x, &DirectBindable::xChanged) +}; + +class IndirectBindable : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(WeatherModelUrl y READ y WRITE setY NOTIFY yChanged BINDABLE bindableY) + Q_PROPERTY(int z READ z NOTIFY zChanged) + +public: + explicit IndirectBindable(QObject *parent = nullptr) : QObject(parent) { + m_z.setBinding([this]()->int { + return m_y.value().timeIndex() * 2; + }); + } + + WeatherModelUrl y() const { return m_y.value(); } + void setY(const WeatherModelUrl& newY) { m_y.setValue(newY); } + QBindable<WeatherModelUrl> bindableY() { return QBindable<WeatherModelUrl>(&m_y); } + + int z() const { return m_z.value(); } + QBindable<int> bindableZ() const { return QBindable<int>(&m_z); } + +Q_SIGNALS: + void yChanged(); + void zChanged(); + +private: + Q_OBJECT_BINDABLE_PROPERTY(IndirectBindable, WeatherModelUrl, m_y, &IndirectBindable::yChanged) + Q_OBJECT_BINDABLE_PROPERTY(IndirectBindable, int, m_z, &IndirectBindable::zChanged) +}; + +#endif // VARIANTRETURN_H diff --git a/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml b/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml new file mode 100644 index 0000000000..f5826d9e48 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml @@ -0,0 +1,10 @@ +import QtQml + +QtObject { + id: item + property point p: Qt.point(20, 10) + + Component.onCompleted: { + item.p = undefined + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h b/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h new file mode 100644 index 0000000000..998118dc5b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h @@ -0,0 +1,61 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef WEATHERMODELURL_H +#define WEATHERMODELURL_H + +#include <QtQml/qqmlregistration.h> +#include <QtCore/qobject.h> + +class WeatherModelUrl +{ + Q_GADGET + QML_STRUCTURED_VALUE + QML_VALUE_TYPE(weatherModelUrl) + Q_PROPERTY(qsizetype timeIndex READ timeIndex CONSTANT) + Q_PROPERTY(QStringList strings READ strings WRITE setStrings) + +public: + WeatherModelUrl() : m_timeIndex(-1) {} + WeatherModelUrl(qsizetype timeIdx) : m_timeIndex(timeIdx) {} + + qsizetype timeIndex() const { return m_timeIndex; } + + QStringList strings() const { return m_strings; } + void setStrings(const QStringList &newStrings) + { + if (m_strings != newStrings) + m_strings = newStrings; + } + +private: + friend bool operator==(const WeatherModelUrl &a, const WeatherModelUrl &b) + { + return a.m_timeIndex == b.m_timeIndex; + } + + friend bool operator!=(const WeatherModelUrl &a, const WeatherModelUrl &b) + { + return !(a == b); + } + + qsizetype m_timeIndex; + QStringList m_strings; +}; + +class WeatherModelUrlUtils : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + +public: + WeatherModelUrlUtils(QObject *parent = nullptr) : QObject(parent) {} + + Q_INVOKABLE static WeatherModelUrl url(int timeIdx) + { + return WeatherModelUrl(timeIdx); + } +}; + +#endif // WEATHERMODELURL_H diff --git a/tests/auto/qml/qmlcppcodegen/data/withlength.h b/tests/auto/qml/qmlcppcodegen/data/withlength.h index ba95522c53..26c6307f2b 100644 --- a/tests/auto/qml/qmlcppcodegen/data/withlength.h +++ b/tests/auto/qml/qmlcppcodegen/data/withlength.h @@ -1,11 +1,13 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WITHLENGTH_H #define WITHLENGTH_H #include <QtCore/qobject.h> -#include <QtQmlIntegration/qqmlintegration.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtQml/qqml.h> struct ValueTypeWithLength { @@ -18,6 +20,8 @@ struct ValueTypeWithLength public: ValueTypeWithLength() = default; Q_INVOKABLE ValueTypeWithLength(int length) : m_length(length) {} + Q_INVOKABLE ValueTypeWithLength(QPointF point) : m_length(point.manhattanLength()) {} + Q_INVOKABLE ValueTypeWithLength(QRectF rect) : m_length(rect.width()) {} Q_INVOKABLE QString toString() const { return QStringLiteral("no"); } int length() const { return m_length; } @@ -26,4 +30,24 @@ private: int m_length = 19; }; +struct InnerWithLength { + int m_length; +}; + +struct UnconstructibleWithLength +{ + Q_GADGET + QML_VALUE_TYPE(unconstructibleWithLength) + + QML_FOREIGN(InnerWithLength) + QML_EXTENDED(UnconstructibleWithLength) + +public: + UnconstructibleWithLength() = default; + Q_INVOKABLE UnconstructibleWithLength(int length) : v{length} {} + +private: + InnerWithLength v; +}; + #endif // WITHLENGTH_H diff --git a/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h b/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h index dce78fa9c9..7de49f095a 100644 --- a/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h +++ b/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WRAPWITHVARIANT_H #define WRAPWITHVARIANT_H diff --git a/tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h b/tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h new file mode 100644 index 0000000000..3c0fedd28b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h @@ -0,0 +1,31 @@ +#pragma once +#include <QObject> +#include <QVariantMap> +#include <QtQml/qqmlregistration.h> + +class WritableVariantMap : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QVariantMap data READ data WRITE setData NOTIFY dataChanged) + +public: + WritableVariantMap(QObject *parent = nullptr) : QObject(parent) { } + + QVariantMap data() const { return m_data; } + void setData(const QVariantMap &data) + { + if (m_data != data) { + m_data = data; + emit dataChanged(); + } + } + +signals: + void dataChanged(); + +private: + QVariantMap m_data; +}; + + diff --git a/tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml b/tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml new file mode 100644 index 0000000000..536e53b408 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml @@ -0,0 +1,10 @@ +pragma Strict +import StringBuilderTestTypes + +WritableVariantMap { + id: dragSource + property string modelData: "Drag Me" + data: ({ + "text/plain": "%" + dragSource.modelData + "%" + }) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/writeback.qml b/tests/auto/qml/qmlcppcodegen/data/writeback.qml new file mode 100644 index 0000000000..359f00efb7 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/writeback.qml @@ -0,0 +1,43 @@ +pragma Strict + +import TestTypes +import QtQml + +Person { + id: self + + area { + width: 19 + height: 199 + } + + property list<int> ints: [4, 3, 2, 1] + + property outer recursive + property Person shadowable: Person { + id: notShadowable + area.width: self.area.width + area2.height: self.area2.height + } + + Component.onCompleted: { + area.width = 16 + area2.height = 17 + + self.area.x = 4 + self.area2.y = 5 + + // You cannot do this on the shadowable Person because + // shadowable.area may not actually be a QRectF anymore. + notShadowable.area.x = 40 + notShadowable.area2.y = 50 + + self.recursive.inner.i = 99; + + self.ints[0] = 12; + ints[1] = 22; + ints[6] = 33; + } + + property int inner: recursive.inner.i +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 400075c08d..53cc068e8c 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -1,10 +1,16 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include "data/druggeljug.h" #include <data/birthdayparty.h> #include <data/cppbaseclass.h> +#include <data/druggeljug.h> +#include <data/enumProperty.h> #include <data/enumproblems.h> +#include <data/getOptionalLookup.h> #include <data/objectwithmethod.h> +#include <data/resettable.h> +#include <data/weathermoduleurl.h> +#include <data/withlength.h> #include <QtQml/private/qqmlengine_p.h> #include <QtQml/private/qqmlpropertycachecreator_p.h> @@ -12,6 +18,7 @@ #include <QtTest> #include <QtQml> #include <QtGui/qcolor.h> +#include <QtGui/qpa/qplatformdialoghelper.h> #if QT_CONFIG(process) #include <QtCore/qprocess.h> @@ -26,146 +33,460 @@ class tst_QmlCppCodegen : public QObject Q_OBJECT private slots: void initTestCase(); + void cleanupTestCase(); - void simpleBinding(); - void cppValueTypeList(); + void accessModelMethodFromOutSide(); + void aliasLookup(); + void ambiguousAs(); + void ambiguousSignals(); void anchorsFill(); - void signalHandler(); - void idAccess(); - void globals(); - void multiLookup(); - void enums(); - void funcWithParams(); - void intOverflow(); - void stringLength(); - void scopeVsObject(); - void compositeTypeMethod(); - void excessiveParameters(); - void jsImport(); - void jsmoduleImport(); - void methods(); - void math(); - void unknownParameter(); + void argumentConversion(); void array(); - void equalsUndefined(); - void conversions(); - void interestingFiles_data(); - void interestingFiles(); - void extendedTypes(); - void construct(); - void contextParam(); - void attachedType(); - void componentReturnType(); - void onAssignment(); - void failures(); - void enumScope(); - void unusedAttached(); + void arrayCtor(); + void asCast(); void attachedBaseEnum(); - void nullAccess(); - void interceptor(); - void nonNotifyable(); - void importsFromImportPath(); - void aliasLookup(); - void outOfBoundsArray(); - void compositeSingleton(); - void lotsOfRegisters(); - void inPlaceDecrement(); - void shifts(); - void valueTypeProperty(); - void propertyOfParent(); - void accessModelMethodFromOutSide(); - void functionArguments(); + void attachedSelf(); + void attachedType(); + void badSequence(); + void basicBlocksWithBackJump(); + void basicBlocksWithBackJump_infinite(); + void basicDTZ(); + void bindToValueType(); void bindingExpression(); - void voidFunction(); - void overriddenProperty(); - void listLength(); - void parentProperty(); - void registerElimination(); - void asCast(); - void noQQmlData(); - void scopeObjectDestruction(); + void blockComments(); + void boolCoercions(); + void boolPointerMerge(); + void boundComponents(); + void callContextPropertyLookupResult(); + void callWithSpread(); void colorAsVariant(); - void bindToValueType(); - void undefinedResets(); - void innerObjectNonShadowable(); - void ownPropertiesNonShadowable(); - void modulePrefix(); void colorString(); - void urlString(); - void callContextPropertyLookupResult(); + void compareOriginals(); + void comparisonTypes(); + void componentReturnType(); + void compositeSingleton(); + void compositeTypeMethod(); + void consoleObject(); + void consoleTrace(); + void construct(); + void contextParam(); + void conversionDecrement(); + void conversionInDeadCode(); + void conversions(); + void convertPrimitiveToVar(); + void convertQJSPrimitiveValueToIntegral(); + void convertToOriginalReadAcumulatorForUnaryOperators(); + void cppMethodListReturnType(); + void cppValueTypeList(); + void dateConstruction(); + void dateConversions(); void deadShoeSize(); - void listIndices(); - void jsMathObject(); - void intEnumCompare(); - void attachedSelf(); - void functionReturningVoid(); - void functionCallOnNamespaced(); + void dialogButtonBox(); + void enumConversion(); + void enumFromBadSingleton(); + void enumLookup(); + void enumMarkedAsFlag(); + void enumProblems(); + void enumScope(); + void enums(); + void enforceSignature(); + void enumsInOtherObject(); + void equalityQObjects(); + void equalityQUrl(); + void equalityTestsWithNullOrUndefined(); + void equalityVarAndNonStorable(); + void equalityVarAndStorable(); + void equalsUndefined(); + void evadingAmbiguity(); + void exceptionFromInner(); + void excessiveParameters(); + void extendedTypes(); + void failures(); + void fallbackLookups(); + void fileImportsContainCxxTypes(); + void flagEnum(); void flushBeforeCapture(); - void unknownAttached(); - void variantlist(); - void popContextAfterRet(); - void revisions(); - void invisibleBase(); - void notEqualsInt(); - void infinities(); - void blockComments(); + void fromBoolValue(); + void funcWithParams(); + void functionArguments(); + void functionCallOnNamespaced(); void functionLookup(); - void objectInVar(); + void functionReturningVoid(); void functionTakingVar(); - void testIsnan(); - void fallbackLookups(); - void typedArray(); - void prefixedType(); - void evadingAmbiguity(); - void fromBoolValue(); - void invisibleTypes(); + void getLookupOfScript(); + void getOptionalLookup(); + void getOptionalLookup_data(); + void getOptionalLookupOnQJSValueNonStrict(); + void getOptionalLookupShadowed(); + void globals(); + void idAccess(); + void ignoredFunctionReturn(); + void importsFromImportPath(); + void inPlaceDecrement(); + void inaccessibleProperty(); + void indirectlyShadowable(); + void infinities(); + void infinitiesToInt(); + void innerObjectNonShadowable(); + void intEnumCompare(); + void intOverflow(); + void intToEnum(); + void interceptor(); + void interestingFiles(); + void interestingFiles_data(); + void internalConversion(); void invalidPropertyType(); - void valueTypeLists(); - void boundComponents(); + void invisibleBase(); void invisibleListElementType(); - void typePropertyClash(); - void objectToString(); - void throwObjectName(); + void invisibleSingleton(); + void invisibleTypes(); + void iteration(); void javaScriptArgument(); - void translation(); - void stringArg(); - void conversionDecrement(); - void unstoredUndefined(); - void registerPropagation(); - void argumentConversion(); - void badSequence(); - void enumLookup(); - void trivialSignalHandler(); - void stringToByteArray(); + void jsArrayMethods(); + void jsArrayMethodsWithParams(); + void jsArrayMethodsWithParams_data(); + void jsImport(); + void jsMathObject(); + void jsmoduleImport(); + void lengthAccessArraySequenceCompat(); + void letAndConst(); + void listAsArgument(); + void listConversion(); + void listIndices(); + void listLength(); + void listOfInvisible(); void listPropertyAsModel(); - void notNotString(); + void listToString(); + void lotsOfRegisters(); + void math(); + void mathMinMax(); void mathOperations(); - void inaccessibleProperty(); - void typePropagationLoop(); - void signatureIgnored(); - void listAsArgument(); - void letAndConst(); - void signalIndexMismatch(); - void callWithSpread(); - void nullComparison(); - void consoleObject(); + void mathStaticProperties(); + void mergedObjectReadWrite(); + void methodOnListLookup(); + void methods(); + void modulePrefix(); + void multiDirectory_data(); + void multiDirectory(); void multiForeign(); + void multiLookup(); + void multipleCtors(); void namespaceWithEnum(); - void enumProblems(); - void enumConversion(); - void ambiguousSignals(); - void fileImportsContainCxxTypes(); - void lengthAccessArraySequenceCompat(); - void storeElementSideEffects(); + void noQQmlData(); + void nonNotifyable(); + void notEqualsInt(); + void notNotString(); + void nullAccess(); + void nullAccessInsideSignalHandler(); + void nullComparison(); + void nullishCoalescing(); + void nullishCoalescing_data(); void numbersInJsPrimitive(); - void infinitiesToInt(); - void equalityVarAndNonStorable(); - void equalityQObjects(); - void dateConversions(); + void objectInVar(); + void objectLookupOnListElement(); + void objectToString(); + void objectWithStringListMethod(); + void onAssignment(); + void optionalComparison(); + void outOfBoundsArray(); + void overriddenProperty(); + void ownPropertiesNonShadowable(); + void parentProperty(); + void popContextAfterRet(); + void prefixedType(); + void propertyOfParent(); + void reduceWithNullThis(); + void readEnumFromInstance(); + void readonlyListProperty(); + void registerElimination(); + void registerPropagation(); + void renameAdjust(); + + void resettableProperty(); + void resettableProperty_data(); + + void returnAfterReject(); + void revisions(); + void scopeIdLookup(); + void scopeObjectDestruction(); + void scopeVsObject(); + void scopedEnum(); + void sequenceToIterable(); + void setLookupConversion(); + void setLookupOriginalScope(); + void shadowedAsCasts(); + void shadowedMethod(); + void shadowedPrimitiveCmpEqNull(); + void shifts(); + void signalHandler(); + void signalIndexMismatch(); + void signalsWithLists(); + void signatureIgnored(); + void simpleBinding(); + void storeElementSideEffects(); + void storeMetaEnum(); + void stringArg(); + void stringLength(); + void stringToByteArray(); + void structuredValueType(); + void testIsnan(); + void thisObject(); + void throwObjectName(); + void topLevelComponent(); + void translation(); + void trigraphs(); + void trivialSignalHandler(); + void typePropagationLoop(); + void typePropertyClash(); + void typedArray(); + void undefinedResets(); + void undefinedToDouble(); + void unknownAttached(); + void unknownParameter(); + void unstoredUndefined(); + void unusedAttached(); + void urlString(); void valueTypeBehavior(); - void invisibleSingleton(); + void valueTypeLists(); + void valueTypeProperty(); + void variantMapLookup(); + void variantReturn(); + void variantlist(); + void variantMap(); + void voidConversion(); + void voidFunction(); + void writeBack(); + void writeVariantMap(); }; +static QByteArray arg1() +{ + const QStringList args = QCoreApplication::instance()->arguments(); + return args.size() > 1 ? args[1].toUtf8() : QByteArray("undefined"); +} + +namespace QmlCacheGeneratedCode { +namespace _qt_qml_TestTypes_failures_qml { +extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[]; +} +} + +static void checkColorProperties(QQmlComponent *component) +{ + QVERIFY2(component->isReady(), qPrintable(component->errorString())); + QScopedPointer<QObject> rootObject(component->create()); + QVERIFY(rootObject); + + const QMetaObject *mo = QMetaType::fromName("QQuickIcon").metaObject(); + QVERIFY(mo != nullptr); + + const QMetaProperty prop = mo->property(mo->indexOfProperty("color")); + QVERIFY(prop.isValid()); + + const QVariant a = rootObject->property("a"); + QVERIFY(a.isValid()); + + const QVariant iconColor = prop.readOnGadget(rootObject->property("icon").data()); + QVERIFY(iconColor.isValid()); + + const QMetaType colorType = QMetaType::fromName("QColor"); + QVERIFY(colorType.isValid()); + + QCOMPARE(a.metaType(), colorType); + QCOMPARE(iconColor.metaType(), colorType); + + QCOMPARE(iconColor, a); +} + +static const double numbers[] = { + qQNaN(), -qInf(), + std::numeric_limits<double>::min(), + std::numeric_limits<float>::min(), + std::numeric_limits<qint32>::min(), + -1000.2, -100, -2, -1.333, -1, -0.84, -0.5, + + // -0 and 0 are not different on the QML side. Therefore, don't keep them adjacent. + // Otherwise the bindings won't get re-evaluated. + std::copysign(0.0, -1), 1, 0.0, + + 0.5, 0.77, 1.4545, 2, 199, 2002.13, + std::numeric_limits<qint32>::max(), + std::numeric_limits<quint32>::max(), + std::numeric_limits<float>::max(), + std::numeric_limits<double>::max(), + qInf() +}; + +class MyCppType : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool useListDelegate + READ useListDelegate + WRITE setUseListDelegate + NOTIFY useListDelegateChanged) +public: + explicit MyCppType(QObject * parent = nullptr) : QObject(parent) {} + + bool useListDelegate() const { return m_useListDelegate; } + void setUseListDelegate(bool useListDelegate) + { + if (useListDelegate != m_useListDelegate) { + m_useListDelegate = useListDelegate; + emit useListDelegateChanged(); + } + } + +signals: + void useListDelegateChanged(); + +private: + bool m_useListDelegate = false; +}; + +class InvisibleListElementType : public QObject +{ + Q_OBJECT +public: + InvisibleListElementType(QObject *parent = nullptr) : QObject(parent) {} +}; + +template<typename T> +QString toOperand(double arg); + +template<> +QString toOperand<double>(double arg) +{ + if (qIsNull(arg)) + return std::signbit(arg) ? QStringLiteral("(-0)") : QStringLiteral("(0)"); + + return u'(' + QJSPrimitiveValue(arg).toString() + u')'; +} + +template<> +QString toOperand<int>(double arg) +{ + const int iArg = QJSPrimitiveValue(arg).toInteger(); + return u'(' + QJSPrimitiveValue(iArg).toString() + u')'; +} + +template<> +QString toOperand<bool>(double arg) +{ + const bool bArg = QJSPrimitiveValue(arg).toBoolean(); + return u'(' + QJSPrimitiveValue(bArg).toString() + u')'; +} + +template<typename T1, typename T2> +double jsEval(double arg1, double arg2, const QString &op, QJSEngine *engine) +{ + auto evalBinary = [&](const QString &jsOp) { + return engine->evaluate(toOperand<T1>(arg1) + jsOp + toOperand<T2>(arg2)).toNumber(); + }; + + auto evalBinaryConst = [&](const QString &jsOp) { + return engine->evaluate(toOperand<T1>(arg1) + jsOp + u'9').toNumber(); + }; + + auto evalUnary = [&](const QString &jsOp) { + return engine->evaluate(jsOp + toOperand<T1>(arg1)).toNumber(); + }; + + auto evalInPlace = [&](const QString &jsOp) { + return engine->evaluate( + u"(function() {var a = "_s + toOperand<T1>(arg1)+ u"; return "_s + + jsOp + u"a;})()"_s).toNumber(); + }; + + if (op == u"unot") + return evalUnary(u"!"_s); + if (op == u"uplus") + return evalUnary(u"+"_s); + if (op == u"uminus") + return evalUnary(u"-"_s); + if (op == u"ucompl") + return evalUnary(u"~"_s); + + if (op == u"increment") + return evalInPlace(u"++"_s); + if (op == u"decrement") + return evalInPlace(u"--"_s); + + if (op == u"add") + return evalBinary(u"+"_s); + if (op == u"sub") + return evalBinary(u"-"_s); + if (op == u"mul") + return evalBinary(u"*"_s); + if (op == u"div") + return evalBinary(u"/"_s); + if (op == u"exp") + return evalBinary(u"**"_s); + if (op == u"mod") + return evalBinary(u"%"_s); + + if (op == u"bitAnd") + return evalBinary(u"&"_s); + if (op == u"bitOr") + return evalBinary(u"|"_s); + if (op == u"bitXor") + return evalBinary(u"^"_s); + + if (op == u"bitAndConst") + return evalBinaryConst(u"&"_s); + if (op == u"bitOrConst") + return evalBinaryConst(u"|"_s); + if (op == u"bitXorConst") + return evalBinaryConst(u"^"_s); + + if (op == u"ushr") + return evalBinary(u">>>"_s); + if (op == u"shr") + return evalBinary(u">>"_s); + if (op == u"shl") + return evalBinary(u"<<"_s); + + if (op == u"ushrConst") + return evalBinaryConst(u">>>"_s); + if (op == u"shrConst") + return evalBinaryConst(u">>"_s); + if (op == u"shlConst") + return evalBinaryConst(u"<<"_s); + + qDebug() << op; + Q_UNREACHABLE_RETURN(0); +} + +static QList<QString> convertToStrings(const QList<qint64> &ints) +{ + QList<QString> strings; + for (qint64 i : ints) + strings.append(QString::number(i)); + return strings; +} + +static QRegularExpression bindingLoopMessage(const QUrl &url, char var) +{ + // The actual string depends on how many times QObject* was registered with what parameters. + return QRegularExpression( + "%1:4:1: QML [^:]+: Binding loop detected for property \"%2\""_L1 + .arg(url.toString()).arg(QLatin1Char(var))); +} + +static void listsEqual(QObject *listProp, QObject *array, const char *method) +{ + const QByteArray listPropertyPropertyName = QByteArray("listProperty") + method; + const QByteArray jsArrayPropertyName = QByteArray("jsArray") + method; + + const QQmlListReference listPropertyProperty(listProp, listPropertyPropertyName.constData()); + const QVariantList jsArrayProperty = array->property(jsArrayPropertyName.constData()).toList(); + + const qsizetype listPropertyCount = listPropertyProperty.count(); + QCOMPARE(listPropertyCount, jsArrayProperty.count()); + + for (qsizetype i = 0; i < listPropertyCount; ++i) + QCOMPARE(listPropertyProperty.at(i), jsArrayProperty.at(i).value<QObject *>()); +} + void tst_QmlCppCodegen::initTestCase() { #ifdef QT_TEST_FORCE_INTERPRETER @@ -173,40 +494,91 @@ void tst_QmlCppCodegen::initTestCase() #endif } -void tst_QmlCppCodegen::simpleBinding() +void tst_QmlCppCodegen::cleanupTestCase() +{ + // This code checks for basic blocks validation failures in the tests + QStringList expectedFailures = { + "codegen_test_module_basicBlocksWithBackJump_infinite_qml.cpp", + "codegen_test_module_verify_basicBlocksWithBackJump_infinite_qml.cpp", + }; + + QString generatedCppFolder = GENERATED_CPP_FOLDER; + QDirIterator dirIterator(generatedCppFolder, { "*.cpp" }, QDir::Files); + while (dirIterator.hasNext()) { + QFile file(dirIterator.next()); + if (!file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text)) { + qDebug() << "Couldn't open generated file"; + continue; + } + + const auto content = file.readAll(); + if (bool validationFailed = content.contains("// QV4_BASIC_BLOCK_VALIDATION_FAILED:"_L1)) { + if (expectedFailures.contains(dirIterator.fileInfo().fileName())) { + QEXPECT_FAIL("", "Expected failure", Continue); + } + const auto message = file.fileName() + ": Basic blocks validation failed."; + QVERIFY2(!validationFailed, message.toStdString().c_str()); + } + } +} + +void tst_QmlCppCodegen::accessModelMethodFromOutSide() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/AccessModelMethodsFromOutside.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + + QTest::ignoreMessage(QtDebugMsg, "3"); + QTest::ignoreMessage(QtDebugMsg, "Apple"); QScopedPointer<QObject> object(component.create()); - QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData()); - QCOMPARE(object->property("foo").toInt(), int(3)); - { - CppBaseClass *base = qobject_cast<CppBaseClass *>(object.data()); - Q_ASSERT(base); - QVERIFY(!base->cppProp.hasBinding()); - QCOMPARE(base->cppProp.value(), 7); - QVERIFY(base->cppProp2.hasBinding()); - QCOMPARE(base->cppProp2.value(), 14); - base->cppProp.setValue(9); - QCOMPARE(base->cppProp.value(), 9); - QCOMPARE(base->cppProp2.value(), 18); - } + QCOMPARE(object->property("cost1").toDouble(), 3); + QCOMPARE(object->property("name1").toString(), u"Orange"_s); + QCOMPARE(object->property("cost2").toDouble(), 1.95); + QCOMPARE(object->property("name2").toString(), u"Banana"_s); } -void tst_QmlCppCodegen::cppValueTypeList() +void tst_QmlCppCodegen::aliasLookup() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/aliasLookup.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); - QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData()); - QCOMPARE(object->property("a").toInt(), 16); - QMetaObject::invokeMethod(object.data(), "incA"); - QCOMPARE(object->property("a").toInt(), 17); + QVERIFY(!object.isNull()); - QCOMPARE(object->property("b").toDouble(), 0.25); - QMetaObject::invokeMethod(object.data(), "incB"); - QCOMPARE(object->property("b").toDouble(), 13.5); + const QVariant t = object->property("t"); + QCOMPARE(t.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(t.toString(), u"12"_s); +} + +void tst_QmlCppCodegen::ambiguousAs() +{ + QQmlEngine e; + const QUrl url(u"qrc:/qt/qml/TestTypes/ambiguousAs.qml"_s); + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("other").value<QObject *>(), o.data()); + o->setProperty("useSelf", QVariant::fromValue(false)); + QCOMPARE(o->property("other").value<QObject *>(), nullptr); +} + +void tst_QmlCppCodegen::ambiguousSignals() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguousSignals.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), u"tomorrow"_s); + Person *p = qobject_cast<Person *>(o.data()); + QVERIFY(p); + emit p->ambiguous(12); + QCOMPARE(o->objectName(), u"12foo"_s); + emit p->ambiguous(); + QCOMPARE(o->objectName(), u"9foo"_s); } void tst_QmlCppCodegen::anchorsFill() @@ -231,342 +603,629 @@ void tst_QmlCppCodegen::anchorsFill() QCOMPARE(child->property("width").toInt(), 47); } -void tst_QmlCppCodegen::signalHandler() +void tst_QmlCppCodegen::argumentConversion() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signal.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/argumentConversion.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + auto checkNaN = [&](const char *propName) { + const QVariant prop = o->property(propName); + QCOMPARE(prop.metaType(), QMetaType::fromType<double>()); + QVERIFY(qIsNaN(prop.toDouble())); + }; + + checkNaN("a"); + checkNaN("b"); + checkNaN("e"); + + QCOMPARE(o->property("c").toDouble(), 3.0); + QCOMPARE(o->property("d").toDouble(), -1.0); + QCOMPARE(o->property("f").toDouble(), 10.0); +} + +void tst_QmlCppCodegen::array() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/array.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QCOMPARE(object->objectName(), QString()); - QCOMPARE(object->property("ff").toInt(), 4); + const QJSValue value1 = object->property("values1").value<QJSValue>(); + QVERIFY(value1.isArray()); + QCOMPARE(value1.property(u"length"_s).toInt(), 3); + QCOMPARE(value1.property(0).toInt(), 1); + QCOMPARE(value1.property(1).toInt(), 2); + QCOMPARE(value1.property(2).toInt(), 3); - object->setObjectName(u"foo"_s); - QCOMPARE(object->property("ff").toInt(), 12); + const QJSValue value2 = object->property("values2").value<QJSValue>(); + QVERIFY(value2.isArray()); + QCOMPARE(value2.property(u"length"_s).toInt(), 0); } -void tst_QmlCppCodegen::idAccess() +void tst_QmlCppCodegen::arrayCtor() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/idAccess.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/arrayCtor.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QVERIFY(object->property("y").toInt() != 48); - QCOMPARE(object->property("y").toInt(), 12); - object->setProperty("z", 13); - QCOMPARE(object->property("y").toInt(), 13); - object->setProperty("x", QVariant::fromValue(333)); - QCOMPARE(object->property("y").toInt(), 48); + QCOMPARE(object->property("defaultCtor"), QVariant::fromValue(QList<int>())); + QCOMPARE(object->property("oneArgCtor"), QVariant::fromValue(QList<int>(5))); + QCOMPARE(object->property("multiArgCtor"), QVariant::fromValue(QList<int>({2, 3, 3, 4}))); + QCOMPARE(object->property("arrayTrue"), QVariant::fromValue(QList<bool>({true}))); + QCOMPARE(object->property("arrayFalse"), QVariant::fromValue(QList<bool>({false}))); + QCOMPARE(object->property("arrayNegative"), QVariant::fromValue(QList<double>())); +} - // The binding was broken by setting the property - object->setProperty("z", 14); - QCOMPARE(object->property("y").toInt(), 48); +void tst_QmlCppCodegen::asCast() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/asCast.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); - QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_s); - QFont f = qvariant_cast<QFont>(ttt->property("font")); - QCOMPARE(f.pointSize(), 22); + QQmlContext *context = qmlContext(root.data()); + const QObject *object = context->objectForName(u"object"_s); + const QObject *item = context->objectForName(u"item"_s); + const QObject *rectangle = context->objectForName(u"rectangle"_s); + const QObject *dummy = context->objectForName(u"dummy"_s); + + QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsObject")), object); + QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsItem")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsRectangle")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsDummy")), nullptr); + + QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsObject")), item); + QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsItem")), item); + QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsRectangle")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsDummy")), nullptr); + + QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsObject")), rectangle); + QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsItem")), rectangle); + QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsRectangle")), rectangle); + QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsDummy")), nullptr); + + QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsObject")), dummy); + QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsItem")), dummy); + QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsRectangle")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsDummy")), dummy); + + QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsObject")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsItem")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsRectangle")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsDummy")), nullptr); + + QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsObject")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsItem")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsRectangle")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsDummy")), nullptr); } -static QByteArray arg1() +void tst_QmlCppCodegen::attachedBaseEnum() { - const QStringList args = QCoreApplication::instance()->arguments(); - return args.size() > 1 ? args[1].toUtf8() : QByteArray("undefined"); + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/attachedBaseEnum.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + + QObject *drag = qvariant_cast<QObject *>(object->property("drag")); + QVERIFY(drag); + + // Drag.YAxis is 2, but we cannot #include it here. + bool ok = false; + QCOMPARE(drag->property("axis").toInt(&ok), 2); + QVERIFY(ok); } -void tst_QmlCppCodegen::globals() +void tst_QmlCppCodegen::attachedSelf() { QQmlEngine engine; - int exitCode = -1; - QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; }); - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/globals.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/SelectionRectangle.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - const QByteArray message = QByteArray("Start 2 ") + arg1(); - QTest::ignoreMessage(QtDebugMsg, message.constData()); + QObject *handle = qvariant_cast<QObject *>(o->property("aa")); + QVERIFY(handle); + QVERIFY(qvariant_cast<QObject *>(handle->property("rect")) != nullptr); +} +void tst_QmlCppCodegen::attachedType() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/text.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QTRY_COMPARE(exitCode, 0); + QCOMPARE(object->property("dayz").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); + QCOMPARE(object->property("oParty").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); - QObject *application = qvariant_cast<QObject *>(object->property("application")); - QVERIFY(application); - QCOMPARE(QString::fromUtf8(application->metaObject()->className()), - u"QQuickApplication"_s); + QObject *party = qvariant_cast<QObject *>(object->property("party")); + QVERIFY(party); + QCOMPARE(party->property("eee").toInt(), 21); + QCOMPARE(party->property("fff").toInt(), 33); + QCOMPARE(object->property("ggg").toInt(), 37); +} - QTest::ignoreMessage(QtDebugMsg, "End"); - QMetaObject::invokeMethod(application, "aboutToQuit"); +void tst_QmlCppCodegen::badSequence() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/badSequence.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - const QVariant somewhere = object->property("somewhere"); - QCOMPARE(somewhere.userType(), QMetaType::QUrl); - QCOMPARE(qvariant_cast<QUrl>(somewhere).toString(), u"qrc:/somewhere/else.qml"_s); + Person *self = qobject_cast<Person *>(o.data()); + QVERIFY(self); + QVERIFY(self->barzles().isEmpty()); + QVERIFY(self->cousins().isEmpty()); - const QVariant somewhereString = object->property("somewhereString"); - QCOMPARE(somewhereString.userType(), QMetaType::QString); - QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_s); + Person *other = o->property("other").value<Person *>(); + QVERIFY(other); - const QVariant plain = object->property("plain"); - QCOMPARE(plain.userType(), QMetaType::QUrl); - QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_s); + QVERIFY(other->barzles().isEmpty()); + QVERIFY(other->cousins().isEmpty()); + + Barzle f1; + Barzle f2; + const QList<Barzle *> barzles { &f1, &f2 }; + const QList<Person *> cousins { self, other }; + + other->setBarzles(barzles); + QCOMPARE(self->barzles(), barzles); + QCOMPARE(self->property("l").toInt(), 2); + + other->setCousins(cousins); + QCOMPARE(self->cousins(), cousins); + QCOMPARE(self->property("m").toInt(), 2); + + QQmlListProperty<Person> others + = self->property("others").value<QQmlListProperty<Person>>(); + QCOMPARE(others.count(&others), 2); + QCOMPARE(others.at(&others, 0), cousins[0]); + QCOMPARE(others.at(&others, 1), cousins[1]); + + QQmlListProperty<Person> momsCousins + = self->property("momsCousins").value<QQmlListProperty<Person>>(); + QCOMPARE(momsCousins.count(&momsCousins), 2); + QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]); + QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]); + + QQmlListProperty<Person> dadsCousins + = self->property("dadsCousins").value<QQmlListProperty<Person>>(); + QCOMPARE(dadsCousins.count(&dadsCousins), 1); + QCOMPARE(dadsCousins.at(&dadsCousins, 0), other); } -void tst_QmlCppCodegen::multiLookup() +static bool expectingMessage = false; +static void handler(QtMsgType type, const QMessageLogContext &, const QString &message) +{ + QVERIFY(expectingMessage); + QCOMPARE(type, QtDebugMsg); + QCOMPARE(message, u"false"); + expectingMessage = false; +} + +void tst_QmlCppCodegen::basicBlocksWithBackJump() { - // Multiple lookups of singletons (Qt in this case) don't clash with one another. QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/immediateQuit.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicBlocksWithBackJump.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); - const QByteArray message = QByteArray("End: ") + arg1(); - QTest::ignoreMessage(QtDebugMsg, message.constData()); + const auto oldHandler = qInstallMessageHandler(&handler); + const auto guard = qScopeGuard([oldHandler]() { qInstallMessageHandler(oldHandler); }); - QSignalSpy quitSpy(&engine, &QQmlEngine::quit); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(quitSpy.size(), 1); + // t1 does not log anything + QMetaObject::invokeMethod(o.data(), "t1"); + + // t2 logs "false" exactly once + expectingMessage = true; + QMetaObject::invokeMethod(o.data(), "t2"); + QVERIFY(!expectingMessage); + + // t3 logs "false" exactly once + expectingMessage = true; + QMetaObject::invokeMethod(o.data(), "t3"); + QVERIFY(!expectingMessage); } -void tst_QmlCppCodegen::enums() +void tst_QmlCppCodegen::basicBlocksWithBackJump_infinite() { QQmlEngine engine; - { - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Enums.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicBlocksWithBackJump_infinite.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); +} - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/Enums.qml:4:1: " - "QML Enums: Layout must be attached to Item elements"); - QScopedPointer<QObject> object(component.create()); +void tst_QmlCppCodegen::basicDTZ() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicDTZ.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); - QVERIFY(!object.isNull()); - bool ok = false; - QCOMPARE(object->property("appState").toInt(&ok), 2); - QVERIFY(ok); - QCOMPARE(object->property("color").toString(), u"blue"_s); + QCOMPARE(o->property("title").toString(), u"none"); - QTRY_COMPARE(object->property("appState").toInt(&ok), 1); - QVERIFY(ok); - QCOMPARE(object->property("color").toString(), u"green"_s); + QMetaObject::invokeMethod(o.data(), "t1"); + QMetaObject::invokeMethod(o.data(), "t2"); + QMetaObject::invokeMethod(o.data(), "t3"); + + QCOMPARE(o->property("title").toString(), u"Baz 41"); +} + +void tst_QmlCppCodegen::bindToValueType() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/bindToValueType.qml"_s)); + checkColorProperties(&component); +} + +void tst_QmlCppCodegen::bindingExpression() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BindingExpression.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + + QObject *child = qmlContext(object.data())->objectForName(u"child"_s); + + double width = 200; + double y = 10; + for (int i = 0; i < 10; ++i) { + QCOMPARE(object->property("width").toDouble(), width); + QCOMPARE(object->property("height").toDouble(), width); + QCOMPARE(object->property("y").toDouble(), y); - const auto func = qmlAttachedPropertiesFunction( - object.data(), QMetaType::fromName("QQuickLayout*").metaObject()); + const double childY = y + (width - 100) / 2; + QCOMPARE(child->property("y").toDouble(), childY); + QCOMPARE(object->property("mass"), childY > 100 ? u"heavy"_s : u"light"_s); + QCOMPARE(object->property("test_division").toDouble(), width / 1000 + 50); + QCOMPARE(object->property("test_ternary").toDouble(), 2.2); - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/enumsInOtherObject.qml:4:25: " - "QML Enums: Layout must be attached to Item elements"); - QObject *attached = qmlAttachedPropertiesObject(object.data(), func); + const int test_switch = object->property("test_switch").toInt(); + switch (int(width) % 3) { + case 0: + QCOMPARE(test_switch, 130); + break; + case 1: + QCOMPARE(test_switch, 380); + break; + case 2: + QCOMPARE(test_switch, 630); + break; + } - const QVariant prop = attached->property("alignment"); - QVERIFY(prop.isValid()); - QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter); + width = 200 * i; + y = 10 + i; + object->setProperty("width", width); + object->setProperty("y", y); } - { - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumsInOtherObject.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("color").toString(), u"blue"_s); - QTRY_COMPARE(object->property("color").toString(), u"green"_s); +} + +void tst_QmlCppCodegen::blockComments() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/blockComments.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("implicitHeight").toDouble(), 8.0); +} + +void tst_QmlCppCodegen::boolCoercions() +{ + QQmlEngine e; + const QUrl url(u"qrc:/qt/qml/TestTypes/boolCoercions.qml"_s); + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":41:5: Unable to assign [undefined] to bool"_L1)); + QScopedPointer<QObject> o(c.create()); + + for (char p = '1'; p <= '8'; ++p) { + const QVariant t = o->property(qPrintable(QLatin1String("t%1").arg(p))); + QCOMPARE(t.metaType(), QMetaType::fromType<bool>()); + QVERIFY(t.toBool()); + } + + for (char p = '1'; p <= '5'; ++p) { + const QVariant f = o->property(qPrintable(QLatin1String("f%1").arg(p))); + QCOMPARE(f.metaType(), QMetaType::fromType<bool>()); + QVERIFY(!f.toBool()); } } -void tst_QmlCppCodegen::funcWithParams() +void tst_QmlCppCodegen::boolPointerMerge() +{ + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/boolPointerMerge.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QObject *item = o->property("item").value<QObject *>(); + QVERIFY(item); + QCOMPARE(item->property("ppp").toInt(), -99); +} + +void tst_QmlCppCodegen::boundComponents() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/funcWithParams.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("bar").toInt(), 30); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/boundComponents.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QObject *c1o = o->property("o").value<QObject *>(); + QVERIFY(c1o != nullptr); + QCOMPARE(c1o->objectName(), u"bar"_s); + + QObject *c2o = c1o->property("o").value<QObject *>(); + QVERIFY(c2o != nullptr); + QCOMPARE(c2o->objectName(), u"bar12"_s); } -void tst_QmlCppCodegen::intOverflow() +void tst_QmlCppCodegen::callContextPropertyLookupResult() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intOverflow.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("a").toDouble(), 1.09951162778e+12); - QCOMPARE(object->property("b").toInt(), 5); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callContextPropertyLookupResult.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr); } -void tst_QmlCppCodegen::stringLength() +void tst_QmlCppCodegen::callWithSpread() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringLength.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("stringLength").toInt(), 8); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callWithSpread.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtCriticalMsg, "That is great!"); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); } -void tst_QmlCppCodegen::scopeVsObject() +void tst_QmlCppCodegen::colorAsVariant() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeVsObject.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorAsVariant.qml"_s)); + checkColorProperties(&component); +} + +void tst_QmlCppCodegen::colorString() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorString.qml"_s)); + + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); + + QCOMPARE(qvariant_cast<QColor>(rootObject->property("c")), QColor::fromRgb(0xdd, 0xdd, 0xdd)); + QCOMPARE(qvariant_cast<QColor>(rootObject->property("d")), QColor::fromRgb(0xaa, 0xaa, 0xaa)); + QCOMPARE(qvariant_cast<QColor>(rootObject->property("e")), QColor::fromRgb(0x11, 0x22, 0x33)); +} + +void tst_QmlCppCodegen::compareOriginals() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compareOriginals.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QCOMPARE(object->property("objectName").toString(), u"foobar"_s); + + QCOMPARE(object->property("compareOriginals").toInt(), 5); + QVERIFY(object->property("optionalThis").toBool()); } -void tst_QmlCppCodegen::compositeTypeMethod() +void tst_QmlCppCodegen::comparisonTypes() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositeTypeMethod.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/comparisonTypes.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QSignalSpy spy(object.data(), SIGNAL(foo())); - QTRY_VERIFY(spy.size() > 0); + + QCOMPARE(object->property("found").toInt(), 1); + QCOMPARE(object->property("foundStrict").toInt(), 0); + + QCOMPARE(object->property("foundNot").toInt(), 2); + QCOMPARE(object->property("foundNotStrict").toInt(), 10); } -void tst_QmlCppCodegen::excessiveParameters() +void tst_QmlCppCodegen::componentReturnType() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/excessiveParameters.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/componentReturnType.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QSignalSpy spy(object.data(), SIGNAL(foo())); - QTRY_VERIFY(spy.size() > 0); + + QCOMPARE(object->property("count").toInt(), 10); + QCOMPARE(QQmlListReference(object.data(), "children").count(), 11); } -void tst_QmlCppCodegen::jsImport() +void tst_QmlCppCodegen::compositeSingleton() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsimport.qml"_s)); + engine.addImportPath(u":/qt/qml/TestTypes/imports/"_s); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositesingleton.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("value").toInt(), 42); + QScopedPointer<QObject> o(component.create()); + QCOMPARE(o->property("x").toDouble(), 4.5); + QCOMPARE(o->property("y").toDouble(), 10.0); + QCOMPARE(o->property("smooth").toBool(), true); } -void tst_QmlCppCodegen::jsmoduleImport() +void tst_QmlCppCodegen::compositeTypeMethod() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsmoduleimport.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositeTypeMethod.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QCOMPARE(object->property("ok").toBool(), true); - QVariant okFunc = object->property("okFunc"); - QCOMPARE(okFunc.metaType(), QMetaType::fromType<QJSValue>()); - QJSValue val = engine.toScriptValue(okFunc); - QJSValue result = val.call(); - QVERIFY(result.isBool()); - QVERIFY(result.toBool()); + QSignalSpy spy(object.data(), SIGNAL(foo())); + QTRY_VERIFY(spy.size() > 0); } -void tst_QmlCppCodegen::methods() +void tst_QmlCppCodegen::consoleObject() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/methods.qml"_s)); - QVERIFY(component.isReady()); - - QTest::ignoreMessage(QtDebugMsg, "The Bar"); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_s)); - QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj); - BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data())); - - QVERIFY(party && party->host()); - QCOMPARE(party->guestCount(), 5); + static const QString urlString = u"qrc:/qt/qml/TestTypes/consoleObject.qml"_s; + QQmlComponent c(&engine, QUrl(urlString)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); - bool foundGreen = false; - bool foundFoo = false; - for (int ii = 0; ii < party->guestCount(); ++ii) { - if (party->guest(ii)->name() == u"William Green"_s) - foundGreen = true; - if (party->guest(ii)->name() == u"The Foo"_s) - foundFoo = true; - } + QTest::ignoreMessage(QtDebugMsg, "b 4.55"); + QTest::ignoreMessage(QtDebugMsg, "b 4.55"); + QTest::ignoreMessage(QtInfoMsg, "b 4.55"); + QTest::ignoreMessage(QtWarningMsg, "b 4.55"); + QTest::ignoreMessage(QtCriticalMsg, "b 4.55"); - QVERIFY(foundGreen); - QVERIFY(foundFoo); + // Unfortunately we cannot check the logging category with QTest::ignoreMessage + QTest::ignoreMessage(QtDebugMsg, "b 4.55"); + QTest::ignoreMessage(QtDebugMsg, "b 4.55"); + QTest::ignoreMessage(QtInfoMsg, "b 4.55"); + QTest::ignoreMessage(QtWarningMsg, "b 4.55"); + QTest::ignoreMessage(QtCriticalMsg, "b 4.55"); - QCOMPARE(obj->property("n1").toString(), u"onGurk"_s); - QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_s); - QCOMPARE(obj->property("n3"), QVariant()); + const QRegularExpression re(u"QQmlComponentAttached\\(0x[0-9a-f]+\\) b 4\\.55"_s); + QTest::ignoreMessage(QtDebugMsg, re); + QTest::ignoreMessage(QtDebugMsg, re); + QTest::ignoreMessage(QtInfoMsg, re); + QTest::ignoreMessage(QtWarningMsg, re); + QTest::ignoreMessage(QtCriticalMsg, re); - { - QVariant ret; - obj->metaObject()->invokeMethod(obj.data(), "retrieveVar", Q_RETURN_ARG(QVariant, ret)); - QCOMPARE(ret.typeId(), QMetaType::QString); - QCOMPARE(ret.toString(), u"Jack Smith"_s); - } + QTest::ignoreMessage(QtDebugMsg, "a undefined b false null 7"); + QTest::ignoreMessage(QtDebugMsg, ""); + QTest::ignoreMessage(QtDebugMsg, "4"); + QTest::ignoreMessage(QtDebugMsg, ""); - { - QString ret; - obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret)); - QCOMPARE(ret, u"Jack Smith"_s); - } + const QRegularExpression re2(u"QQmlComponentAttached\\(0x[0-9a-f]+\\)"_s); + QTest::ignoreMessage(QtDebugMsg, re2); - QCOMPARE(party->host()->shoeSize(), 12); - obj->metaObject()->invokeMethod(obj.data(), "storeElement"); - QCOMPARE(party->host()->shoeSize(), 13); - QJSManagedValue v = engine.toManagedValue(obj->property("dresses")); - QVERIFY(v.isArray()); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QJSManagedValue inner(v.property(2), &engine); - QVERIFY(inner.isArray()); - QCOMPARE(inner.property(0).toInt(), 1); - QCOMPARE(inner.property(1).toInt(), 2); - QCOMPARE(inner.property(2).toInt(), 3); + auto oldHandler = qInstallMessageHandler( + [](QtMsgType, const QMessageLogContext &ctxt, const QString &) { + QCOMPARE(ctxt.file, urlString.toUtf8()); + QCOMPARE(ctxt.function, QByteArray("expression for onCompleted")); + QVERIFY(ctxt.line > 0); + }); + const auto guard = qScopeGuard([oldHandler]() { qInstallMessageHandler(oldHandler); }); - QCOMPARE(obj->property("enumValue").toInt(), 2); + QScopedPointer<QObject> p(c.create()); + QVERIFY(!p.isNull()); } -void tst_QmlCppCodegen::math() +void tst_QmlCppCodegen::consoleTrace() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/math.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleTrace.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); + +#if !defined(QT_NO_DEBUG) || defined(QT_TEST_FORCE_INTERPRETER) + // All line numbers in debug mode or when interpreting + + QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6) +b (qrc:/qt/qml/TestTypes/consoleTrace.qml:5) +a (qrc:/qt/qml/TestTypes/consoleTrace.qml:4) +expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml:7))"); +#else + // Only top-most line number otherwise + + QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6) +b (qrc:/qt/qml/TestTypes/consoleTrace.qml) +a (qrc:/qt/qml/TestTypes/consoleTrace.qml) +expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml))"); +#endif + QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QCOMPARE(object->property("a").toInt(), 9); - QCOMPARE(object->property("b").toDouble(), 50.0 / 22.0); } -void tst_QmlCppCodegen::unknownParameter() +void tst_QmlCppCodegen::construct() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownParameter.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/construct.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - QCOMPARE(object->property("cppProp").toInt(), 18); + + const QJSManagedValue v = engine.toManagedValue(object->property("foo")); + QVERIFY(v.isError()); + QCOMPARE(v.toString(), u"Error: bar"_s); + + QCOMPARE(object->property("aaa").toInt(), 12); + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/construct.qml:9: Error: ouch"); + object->metaObject()->invokeMethod(object.data(), "ouch"); + QCOMPARE(object->property("aaa").toInt(), 13); } -void tst_QmlCppCodegen::array() +void tst_QmlCppCodegen::contextParam() { + // The compiler cannot resolve context parameters. + // Make sure the binding is interpreted. + QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/array.qml"_s)); + + QVariantMap m; + m.insert(u"foo"_s, 10); + engine.rootContext()->setContextProperty(u"contextParam"_s, m); + + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/contextParam.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - const QJSValue value1 = object->property("values1").value<QJSValue>(); - QVERIFY(value1.isArray()); - QCOMPARE(value1.property(u"length"_s).toInt(), 3); - QCOMPARE(value1.property(0).toInt(), 1); - QCOMPARE(value1.property(1).toInt(), 2); - QCOMPARE(value1.property(2).toInt(), 3); - const QJSValue value2 = object->property("values2").value<QJSValue>(); - QVERIFY(value2.isArray()); - QCOMPARE(value2.property(u"length"_s).toInt(), 0); + QCOMPARE(object->property("foo").toInt(), 10); } -void tst_QmlCppCodegen::equalsUndefined() +void tst_QmlCppCodegen::conversionDecrement() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalsUndefined.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionDecrement.qml"_s)); - QCOMPARE(object->property("a").toInt(), 50); - QCOMPARE(object->property("b").toInt(), 5000); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("currentPageIndex").toInt(), 0); + o->setProperty("pages", 5); + QCOMPARE(o->property("currentPageIndex").toInt(), 3); + o->setProperty("pages", 4); + QCOMPARE(o->property("currentPageIndex").toInt(), 0); + o->setProperty("pages", 6); + QCOMPARE(o->property("currentPageIndex").toInt(), 4); + o->setProperty("pages", 60); + QCOMPARE(o->property("currentPageIndex").toInt(), 3); +} + +void tst_QmlCppCodegen::conversionInDeadCode() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionInDeadCode.qml"_s)); + + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("a").toInt(), -4); + QCOMPARE(o->property("b").toInt(), 12); + QCOMPARE(o->property("c").toInt(), 10); + QCOMPARE(o->property("d").toInt(), 10); + QCOMPARE(o->property("e").toInt(), 10); + QCOMPARE(o->property("f").toInt(), 20); + QCOMPARE(o->property("g").toInt(), 33); } void tst_QmlCppCodegen::conversions() @@ -699,157 +1358,260 @@ void tst_QmlCppCodegen::conversions() QVERIFY(!undef.isValid()); } -void tst_QmlCppCodegen::interestingFiles_data() +void tst_QmlCppCodegen::convertPrimitiveToVar() { - QTest::addColumn<QString>("file"); - QTest::addColumn<bool>("isValid"); + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertPrimitiveToVar.qml"_s)); - QTest::addRow("conversions2") << u"conversions2.qml"_s << true; - QTest::addRow("TestCase") << u"TestCase.qml"_s << true; - QTest::addRow("layouts") << u"layouts.qml"_s << true; - QTest::addRow("interactive") << u"interactive.qml"_s << true; - QTest::addRow("Panel") << u"Panel.qml"_s << true; - QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_s << true; - QTest::addRow("Root") << u"ProgressBar/Root.qml"_s << true; - QTest::addRow("noscope") << u"noscope.qml"_s << true; - QTest::addRow("dynamicscene") << u"dynamicscene.qml"_s << true; - QTest::addRow("curlygrouped") << u"curlygrouped.qml"_s << true; - QTest::addRow("cycleHead") << u"cycleHead.qml"_s << false; - QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_s << true; - QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_s << true; + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("offsetValue").toInt(), 41); } -void tst_QmlCppCodegen::interestingFiles() +void tst_QmlCppCodegen::convertQJSPrimitiveValueToIntegral() { - QFETCH(QString, file); - QFETCH(bool, isValid); - QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/%1"_s.arg(file))); - if (isValid) { - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - } else { - QVERIFY(component.isError()); - } + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertQJSPrimitiveValueToIntegral.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); } -void tst_QmlCppCodegen::extendedTypes() +void tst_QmlCppCodegen::convertToOriginalReadAcumulatorForUnaryOperators() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/extendedTypes.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - - QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30"); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertToOriginalReadAcumulatorForUnaryOperators.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} - QCOMPARE(object->property("a").toInt(), 6); - QCOMPARE(qvariant_cast<QSizeF>(object->property("b")), QSizeF(10, 20)); - QCOMPARE(object->property("c").toInt(), 30); - QCOMPARE(object->property("d").toString(), u"QSizeF(10, 20)"_s); +void tst_QmlCppCodegen::cppMethodListReturnType() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/CppMethodListReturnType.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QCOMPARE(object->property("e").toInt(), 2); + QCOMPARE(o->property("list").toList()[2].toInt(), 2); } -void tst_QmlCppCodegen::construct() +void tst_QmlCppCodegen::cppValueTypeList() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/construct.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s)); QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - - const QJSManagedValue v = engine.toManagedValue(object->property("foo")); - QVERIFY(v.isError()); - QCOMPARE(v.toString(), u"Error: bar"_s); + QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData()); + QCOMPARE(object->property("a").toInt(), 16); + QMetaObject::invokeMethod(object.data(), "incA"); + QCOMPARE(object->property("a").toInt(), 17); - QCOMPARE(object->property("aaa").toInt(), 12); - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/construct.qml:9: Error: ouch"); - object->metaObject()->invokeMethod(object.data(), "ouch"); - QCOMPARE(object->property("aaa").toInt(), 13); + QCOMPARE(object->property("b").toDouble(), 0.25); + QMetaObject::invokeMethod(object.data(), "incB"); + QCOMPARE(object->property("b").toDouble(), 13.5); } -void tst_QmlCppCodegen::contextParam() +void tst_QmlCppCodegen::dateConstruction() { - // The compiler cannot resolve context parameters. - // Make sure the binding is interpreted. + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConstruction.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QDateTime now = QDateTime::currentDateTime(); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QVERIFY(o->property("now").value<QDateTime>().toMSecsSinceEpoch() >= now.toMSecsSinceEpoch()); + QCOMPARE(o->property("now2"), o->property("now")); + QCOMPARE(o->property("fromString").value<QDateTime>(), + QDateTime(QDate(1995, 12, 17), QTime(3, 24), QTimeZone::LocalTime)); + QCOMPARE(o->property("fromNumber").value<QDateTime>().toMSecsSinceEpoch(), 777); + QCOMPARE(o->property("fromPrimitive").value<QDateTime>().toMSecsSinceEpoch(), 57); + o->setObjectName("foo"_L1); + QCOMPARE(o->property("fromPrimitive").value<QDateTime>(), + QDateTime(QDate(1997, 2, 13), QTime(13, 4, 12), QTimeZone::LocalTime)); + + QCOMPARE(o->property("from2").value<QDateTime>(), + QDateTime(QDate(1996, 2, 1), QTime(), QTimeZone::LocalTime)); + QCOMPARE(o->property("from3").value<QDateTime>(), + QDateTime(QDate(1996, 3, 3), QTime(), QTimeZone::LocalTime)); + QCOMPARE(o->property("from4").value<QDateTime>(), + QDateTime(QDate(1996, 4, 4), QTime(5, 0), QTimeZone::LocalTime)); + QCOMPARE(o->property("from5").value<QDateTime>(), + QDateTime(QDate(1996, 5, 5), QTime(6, 7), QTimeZone::LocalTime)); + QCOMPARE(o->property("from6").value<QDateTime>(), + QDateTime(QDate(1996, 6, 6), QTime(7, 8, 9), QTimeZone::LocalTime)); + QCOMPARE(o->property("from7").value<QDateTime>(), + QDateTime(QDate(1996, 7, 7), QTime(8, 9, 10, 11), QTimeZone::LocalTime)); + QCOMPARE(o->property("from8").value<QDateTime>(), + QDateTime(QDate(1996, 8, 8), QTime(9, 10, 11, 12), QTimeZone::LocalTime)); + + QCOMPARE(o->property("withUnderflow").value<QDateTime>(), + QDateTime(QDate(-6, 7, 24), QTime(16, 51, 50, 990), QTimeZone::LocalTime)); + QCOMPARE(o->property("invalid").value<QDateTime>(), QDateTime()); +} +void tst_QmlCppCodegen::dateConversions() +{ QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - QVariantMap m; - m.insert(u"foo"_s, 10); - engine.rootContext()->setContextProperty(u"contextParam"_s, m); + Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug"); - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/contextParam.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); + const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate()); + const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime()); + + QCOMPARE(o->property("date").value<QDateTime>(), refDate); + QCOMPARE(o->property("time").value<QDateTime>(), refTime); + + QCOMPARE(o->property("dateString").toString(), + (engine.coerceValue<QDateTime, QString>(refDate))); + QCOMPARE(o->property("dateNumber").toDouble(), + (engine.coerceValue<QDateTime, double>(refDate))); + QCOMPARE(o->property("timeString").toString(), + (engine.coerceValue<QDateTime, QString>(refTime))); + QCOMPARE(o->property("timeNumber").toDouble(), + (engine.coerceValue<QDateTime, double>(refTime))); + + QMetaObject::invokeMethod(o.data(), "shuffle"); + + QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate))); + QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime))); + + const QDate date = ref->myDate(); + const QTime time = ref->myTime(); + + QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date))); + QCOMPARE(o->property("dateNumber").toDouble(), (engine.coerceValue<QDate, double>(date))); + QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time))); + QCOMPARE(o->property("timeNumber").toDouble(), (engine.coerceValue<QTime, double>(time))); + + QMetaObject::invokeMethod(o.data(), "fool"); + + QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time))); + QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date))); + + QMetaObject::invokeMethod(o.data(), "invalidate"); + QMetaObject::invokeMethod(o.data(), "shuffle"); + + QCOMPARE(o->property("dateString").toString(), "Invalid Date"_L1); + QVERIFY(qIsNaN(o->property("dateNumber").toDouble())); + QCOMPARE(o->property("timeString").toString(), "Invalid Date"_L1); + QVERIFY(qIsNaN(o->property("timeNumber").toDouble())); - QCOMPARE(object->property("foo").toInt(), 10); } -void tst_QmlCppCodegen::attachedType() +void tst_QmlCppCodegen::deadShoeSize() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/text.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("dayz").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); - QCOMPARE(object->property("oParty").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); - - QObject *party = qvariant_cast<QObject *>(object->property("party")); - QVERIFY(party); - QCOMPARE(party->property("eee").toInt(), 21); - QCOMPARE(party->property("fff").toInt(), 33); - QCOMPARE(object->property("ggg").toInt(), 37); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/deadShoeSize.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/deadShoeSize.qml:5: Error: ouch"); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("shoeSize").toInt(), 0); } -void tst_QmlCppCodegen::componentReturnType() +void tst_QmlCppCodegen::dialogButtonBox() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/componentReturnType.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); + const QUrl copy(u"qrc:/qt/qml/TestTypes/dialogButtonBox.qml"_s); + QQmlComponent c(&engine, copy); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QObject *footer = o->property("footer").value<QObject *>(); + QVERIFY(footer); - QCOMPARE(object->property("count").toInt(), 10); - QCOMPARE(QQmlListReference(object.data(), "children").count(), 11); + QCOMPARE(footer->property("standardButtons").value<QPlatformDialogHelper::StandardButton>(), + QPlatformDialogHelper::Ok | QPlatformDialogHelper::Cancel); } -void tst_QmlCppCodegen::onAssignment() +void tst_QmlCppCodegen::enumConversion() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/pressAndHoldButton.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QCOMPARE(object->property("pressed").toBool(), false); - QCOMPARE(object->property("scale").toDouble(), 1.0); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumConversion.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); - object->metaObject()->invokeMethod(object.data(), "press"); - QTRY_COMPARE(object->property("pressed").toBool(), true); - QCOMPARE(object->property("scale").toDouble(), 0.9); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("test").toInt(), 0x04); + QCOMPARE(o->property("test_1").toBool(), true); + QCOMPARE(o->objectName(), u"0m"_s); +} - object->metaObject()->invokeMethod(object.data(), "release"); - QCOMPARE(object->property("pressed").toBool(), false); - QCOMPARE(object->property("scale").toDouble(), 1.0); +void tst_QmlCppCodegen::enumFromBadSingleton() +{ + QQmlEngine e; + const QUrl url(u"qrc:/qt/qml/TestTypes/enumFromBadSingleton.qml"_s); + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + +#if QT_DEPRECATED_SINCE(6,4) + QTest::ignoreMessage( + QtWarningMsg, qPrintable( + url.toString() + + u":5:5: TypeError: Cannot read property 'TestA' of undefined"_s)); +#else + QTest::ignoreMessage( + QtWarningMsg, qPrintable( + url.toString() + + u":5:5: ReferenceError: DummyObjekt is not defined"_s)); +#endif + + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QVERIFY(o->objectName().isEmpty()); } -namespace QmlCacheGeneratedCode { -namespace _qt_qml_TestTypes_failures_qml { -extern const QQmlPrivate::TypedFunction aotBuiltFunctions[]; +void tst_QmlCppCodegen::enumLookup() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumLookup.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QCOMPARE(o->property("ready").toBool(), true); } + +void tst_QmlCppCodegen::enumMarkedAsFlag() +{ + QQmlEngine engine; + + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumMarkedAsFlag.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QCOMPARE(o->property("flagValue").toInt(), 3); } -void tst_QmlCppCodegen::failures() +void tst_QmlCppCodegen::enumProblems() { - const auto &aotFailure - = QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0]; - QVERIFY(aotFailure.argumentTypes.isEmpty()); - QVERIFY(!aotFailure.functionPtr); - QCOMPARE(aotFailure.extraData, 0); + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumProblems.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> outer(c.create()); + QVERIFY(!outer.isNull()); + QObject *inner = outer->property("o").value<QObject *>(); + QVERIFY(inner); + + Foo *bar = inner->property("bar").value<Foo *>(); + QVERIFY(bar); + QCOMPARE(bar->type(), Foo::Component); + + Foo *fighter = inner->property("fighter").value<Foo *>(); + QVERIFY(fighter); + QCOMPARE(fighter->type(), Foo::Fighter); + + QCOMPARE(outer->property("a").toInt(), FooFactory::B); + QCOMPARE(outer->property("b").toInt(), FooFactory::C); + QCOMPARE(outer->property("c").toInt(), FooFactory::D); + QCOMPARE(outer->property("d").toInt(), FooFactory::E); } void tst_QmlCppCodegen::enumScope() @@ -861,283 +1623,370 @@ void tst_QmlCppCodegen::enumScope() QCOMPARE(object->property("flow").toInt(), 1); } -void tst_QmlCppCodegen::unusedAttached() +void tst_QmlCppCodegen::enums() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unusedAttached.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Enums.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); + + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/Enums.qml:4:1: " + "QML Enums: Layout must be attached to Item elements"); QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + bool ok = false; + QCOMPARE(object->property("appState").toInt(&ok), 2); + QVERIFY(ok); + QCOMPARE(object->property("color").toString(), u"blue"_s); + + QTRY_COMPARE(object->property("appState").toInt(&ok), 1); + QVERIFY(ok); + QCOMPARE(object->property("color").toString(), u"green"_s); + const auto func = qmlAttachedPropertiesFunction( - object.data(), QMetaType::fromName("QQuickKeyNavigationAttached*").metaObject()); + object.data(), QMetaType::fromName("QQuickLayout*").metaObject()); + QObject *attached = qmlAttachedPropertiesObject(object.data(), func); - const QVariant prop = attached->property("priority"); + + const QVariant prop = attached->property("alignment"); QVERIFY(prop.isValid()); - QCOMPARE(QByteArray(prop.metaType().name()), "QQuickKeyNavigationAttached::Priority"); - bool ok = false; - QCOMPARE(prop.toInt(&ok), 0); - QVERIFY(ok); + QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter); + } -void tst_QmlCppCodegen::attachedBaseEnum() +void tst_QmlCppCodegen::enforceSignature() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/attachedBaseEnum.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enforceSignature.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - QObject *drag = qvariant_cast<QObject *>(object->property("drag")); - QVERIFY(drag); + const QVariant a = object->property("a"); + QCOMPARE(a.metaType(), QMetaType::fromType<QObject *>()); + QCOMPARE(a.value<QObject *>(), nullptr); - // Drag.YAxis is 2, but we cannot #include it here. - bool ok = false; - QCOMPARE(drag->property("axis").toInt(&ok), 2); - QVERIFY(ok); + const QVariant b = object->property("b"); + QCOMPARE(b.metaType(), QMetaType::fromType<QObject *>()); + QCOMPARE(b.value<QObject *>(), nullptr); } -void tst_QmlCppCodegen::nullAccess() +void tst_QmlCppCodegen::enumsInOtherObject() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccess.qml"_s)); + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/enumsInOtherObject.qml:4:25: " + "QML Enums: Layout must be attached to Item elements"); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumsInOtherObject.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); - - QTest::ignoreMessage(QtWarningMsg, - "qrc:/qt/qml/TestTypes/nullAccess.qml:4:5: TypeError: " - "Cannot read property 'width' of null"); - QTest::ignoreMessage(QtWarningMsg, - "qrc:/qt/qml/TestTypes/nullAccess.qml:5:5: TypeError: " - "Cannot read property 'height' of null"); - QTest::ignoreMessage(QtWarningMsg, - "qrc:/qt/qml/TestTypes/nullAccess.qml:6: TypeError: Value is null and " - "could not be converted to an object"); QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("color").toString(), u"blue"_s); + QTRY_COMPARE(object->property("color").toString(), u"green"_s); +} - QCOMPARE(object->property("width").toDouble(), 0.0); - QCOMPARE(object->property("height").toDouble(), 0.0); +void tst_QmlCppCodegen::equalityQObjects() +{ + QQmlEngine engine; + QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQObjects.qml"_s)); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); + QScopedPointer<QObject> object(c1.create()); + QVERIFY(!object.isNull() && !c1.isError()); + + QVERIFY(object->property("derivedIsNotNull").toBool()); + QVERIFY(object->property("nullObjectIsNull").toBool()); + QVERIFY(object->property("nonNullObjectIsNotNull").toBool()); + QVERIFY(object->property("compareSameObjects").toBool()); + QVERIFY(object->property("compareDifferentObjects").toBool()); + QVERIFY(object->property("compareObjectWithNullObject").toBool()); + + QVERIFY(object->property("nonStrict_derivedIsNotNull").toBool()); + QVERIFY(object->property("nonStrict_nullObjectIsNull").toBool()); + QVERIFY(object->property("nonStrict_nonNullObjectIsNotNull").toBool()); + QVERIFY(object->property("nonStrict_compareSameObjects").toBool()); + QVERIFY(object->property("nonStrict_compareDifferentObjects").toBool()); + QVERIFY(object->property("nonStrict_compareObjectWithNullObject").toBool()); } -void tst_QmlCppCodegen::interceptor() +void tst_QmlCppCodegen::equalityQUrl() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/interceptor.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("x").toInt(), 100); - QCOMPARE(object->property("y").toInt(), 100); + QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQUrl.qml"_s)); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - QVERIFY(object->property("width").toInt() != 200); - QVERIFY(object->property("height").toInt() != 200); - QVERIFY(object->property("qProperty1").toInt() != 300); - QVERIFY(object->property("qProperty2").toInt() != 300); - QTRY_COMPARE(object->property("width").toInt(), 200); - QTRY_COMPARE(object->property("height").toInt(), 200); - QTRY_COMPARE(object->property("qProperty1").toInt(), 300); - QTRY_COMPARE(object->property("qProperty2").toInt(), 300); + QScopedPointer<QObject> object(c1.create()); + QVERIFY(!object.isNull() && !c1.isError()); + QVERIFY(object->property("emptyUrlStrict").toBool()); + QVERIFY(object->property("emptyUrlWeak").toBool()); + QVERIFY(object->property("sourceUrlStrict").toBool()); + QVERIFY(object->property("sourceUrlWeak").toBool()); + QVERIFY(object->property("sourceIsNotEmptyStrict").toBool()); + QVERIFY(object->property("sourceIsNotEmptyWeak").toBool()); } -void tst_QmlCppCodegen::nonNotifyable() +void tst_QmlCppCodegen::equalityTestsWithNullOrUndefined() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nonNotifyable.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityTestsWithNullOrUndefined.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); +} - QCOMPARE(qvariant_cast<QDateTime>(object->property("dayz")), - QDateTime(QDate(2121, 1, 12), QTime())); - QCOMPARE(qvariant_cast<QDateTime>(object->property("oParty")), - QDateTime(QDate(2111, 12, 11), QTime())); +void tst_QmlCppCodegen::equalityVarAndNonStorable() +{ + QQmlEngine engine; + + QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityVarAndNonStorable.qml"_s)); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); + + QScopedPointer<QObject> object(c1.create()); + QVERIFY(!object.isNull() && !c1.isError()); + QVERIFY(!object->property("aIsNull").toBool()); + QVERIFY(object->property("aIsNotNull").toBool()); + QVERIFY(object->property("aIsNotUndefined").toBool()); + QVERIFY(object->property("objectIsNotNull").toBool()); + QVERIFY(!object->property("typedArrayIsNull").toBool()); + QVERIFY(object->property("isUndefined").toBool()); + QVERIFY(!object->property("derivedIsNull").toBool()); + + QVERIFY(object->property("primitiveIsNull").toBool()); + QVERIFY(object->property("primitiveIsDefined").toBool()); + QVERIFY(object->property("primitiveIsUndefined").toBool()); + + QVERIFY(object->property("jsValueIsNull").toBool()); + QVERIFY(object->property("jsValueIsDefined").toBool()); + QVERIFY(object->property("jsValueIsUndefined").toBool()); + + QVERIFY(object->property("nullVarIsUndefined").toBool()); + QVERIFY(object->property("nullIsUndefined").toBool()); + QVERIFY(object->property("nullVarIsNull").toBool()); + QVERIFY(object->property("nullIsNotUndefined").toBool()); } -void tst_QmlCppCodegen::importsFromImportPath() +void tst_QmlCppCodegen::equalityVarAndStorable() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml"_s)); + QQmlComponent planner(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Planner.qml"_s)); + QVERIFY2(!planner.isError(), qPrintable(planner.errorString())); + QScopedPointer<QObject> p(planner.create()); + QVERIFY(!p.isNull()); - // We might propagate the import path, eventually, but for now instantiating is not important. - // If the compiler accepts the file, it's probably fine. - QVERIFY(component.isError()); - QCOMPARE(component.errorString(), - u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_s); + QQmlComponent variable(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Variable.qml"_s)); + QVERIFY2(!variable.isError(), qPrintable(variable.errorString())); + QScopedPointer<QObject> v(variable.create()); + QVERIFY(!v.isNull()); + + QVERIFY(p->objectName().isEmpty()); + QMetaObject::invokeMethod(p.data(), "typeErasedRemoveOne", v.data()); + QCOMPARE(p->objectName(), u"n"); + + v->setProperty("value", 1); + QMetaObject::invokeMethod(p.data(), "typeErasedRemoveOne", v.data()); + QCOMPARE(p->objectName(), u"nd"); + + QQmlComponent constraint(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BaseConstraint.qml"_s)); + QVERIFY2(!constraint.isError(), qPrintable(constraint.errorString())); + QScopedPointer<QObject> c(constraint.create()); + QVERIFY(!c.isNull()); + + c->setProperty("output", QVariant::fromValue(v.data())); + QCOMPARE(v->property("mark").toInt(), 0); + QMetaObject::invokeMethod(p.data(), "typeErasedRun", c.data()); + QCOMPARE(v->property("mark").toInt(), 5); + + QTest::ignoreMessage(QtDebugMsg, "success"); + QMetaObject::invokeMethod(p.data(), "verify", 10); + + QTest::ignoreMessage(QtCriticalMsg, "failed 10 11"); + QMetaObject::invokeMethod(p.data(), "verify", 11); } -void tst_QmlCppCodegen::aliasLookup() +void tst_QmlCppCodegen::equalsUndefined() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/aliasLookup.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalsUndefined.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); - const QVariant t = object->property("t"); - QCOMPARE(t.metaType(), QMetaType::fromType<QString>()); - QCOMPARE(t.toString(), u"12"_s); + QCOMPARE(object->property("a").toInt(), 50); + QCOMPARE(object->property("b").toInt(), 5000); } -void tst_QmlCppCodegen::outOfBoundsArray() +void tst_QmlCppCodegen::evadingAmbiguity() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/outOfBounds.qml"_s)); - QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QTest::ignoreMessage(QtDebugMsg, "oob undefined"); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QVERIFY(object->metaObject()->indexOfProperty("oob") > 0); - QVERIFY(!object->property("oob").isValid()); - const QVariant oob2 = object->property("oob2"); - QCOMPARE(oob2.metaType(), QMetaType::fromType<QObject *>()); - QCOMPARE(oob2.value<QObject *>(), nullptr); + // We need to add an import path here because we cannot namespace the implicit import. + // The implicit import is what we use for all the other tests, even if we explicitly + // import TestTypes. That is because the TestTypes module is in a subdirectory "data". + engine.addImportPath(u":/"_s); + + QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous1/Ambiguous.qml"_s)); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); + QScopedPointer<QObject> o1(c1.create()); + QCOMPARE(o1->objectName(), QStringLiteral("Ambiguous")); + QCOMPARE(o1->property("i").toString(), QStringLiteral("Ambiguous1")); + + QQmlComponent c2(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous2/Ambiguous.qml"_s)); + QVERIFY2(c2.isReady(), qPrintable(c2.errorString())); + QScopedPointer<QObject> o2(c2.create()); + QCOMPARE(o2->objectName(), QStringLiteral("Ambiguous")); + QCOMPARE(o2->property("i").toString(), QStringLiteral("Ambiguous2")); } -void tst_QmlCppCodegen::compositeSingleton() +void tst_QmlCppCodegen::exceptionFromInner() { QQmlEngine engine; - engine.addImportPath(u":/qt/qml/TestTypes/imports/"_s); - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositesingleton.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/exceptionFromInner.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QScopedPointer<QObject> o(component.create()); - QCOMPARE(o->property("x").toDouble(), 4.5); - QCOMPARE(o->property("y").toDouble(), 10.0); - QCOMPARE(o->property("smooth").toBool(), true); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QTest::ignoreMessage( + QtWarningMsg, + "qrc:/qt/qml/TestTypes/exceptionFromInner.qml:7: TypeError: " + "Cannot read property 'objectName' of null"); + QMetaObject::invokeMethod(object.data(), "disbelieveFail"); } -void tst_QmlCppCodegen::lotsOfRegisters() +void tst_QmlCppCodegen::excessiveParameters() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/page.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/excessiveParameters.qml"_s)); QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); + QSignalSpy spy(object.data(), SIGNAL(foo())); + QTRY_VERIFY(spy.size() > 0); +} - const auto compare = [&]() { - const qreal implicitBackgroundWidth = object->property("implicitBackgroundWidth").toDouble(); - const qreal leftInset = object->property("leftInset").toDouble(); - const qreal rightInset = object->property("rightInset").toDouble(); - const qreal contentWidth = object->property("contentWidth").toDouble(); - const qreal leftPadding = object->property("leftPadding").toDouble(); - const qreal rightPadding = object->property("rightPadding").toDouble(); - const qreal implicitFooterWidth = object->property("implicitFooterWidth").toDouble(); - const qreal implicitHeaderWidth = object->property("implicitHeaderWidth").toDouble(); +void tst_QmlCppCodegen::extendedTypes() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/extendedTypes.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); - const qreal implicitWidth = object->property("implicitWidth").toDouble(); - QCOMPARE(implicitWidth, qMax(qMax(implicitBackgroundWidth + leftInset + rightInset, - contentWidth + leftPadding + rightPadding), - qMax(implicitHeaderWidth, implicitFooterWidth))); - }; + QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30"); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - compare(); + QCOMPARE(object->property("a").toInt(), 6); + QCOMPARE(qvariant_cast<QSizeF>(object->property("b")), QSizeF(10, 20)); + QCOMPARE(object->property("c").toInt(), 30); + QCOMPARE(object->property("d").toString(), u"QSizeF(10, 20)"_s); - const QList<const char *> props = { - "leftInset", "rightInset", "contentWidth", "leftPadding", "rightPadding" - }; + QCOMPARE(object->property("e").toInt(), 2); +} - for (int i = 0; i < 100; ++i) { - QVERIFY(object->setProperty(props[i % props.size()], (i * 17) % 512)); - compare(); - } +void tst_QmlCppCodegen::failures() +{ + const auto &aotFailure + = QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0]; + QVERIFY(!aotFailure.functionPtr); + QCOMPARE(aotFailure.functionIndex, 0); } -void tst_QmlCppCodegen::inPlaceDecrement() +void tst_QmlCppCodegen::fallbackLookups() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dialog.qml"_s)); - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QObject *header = qvariant_cast<QObject *>(object->property("header")); - QVERIFY(header); - QObject *background = qvariant_cast<QObject *>(header->property("background")); - QObject *parent = qvariant_cast<QObject *>(background->property("parent")); + const QUrl document(u"qrc:/qt/qml/TestTypes/fallbacklookups.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1); - QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1); + QCOMPARE(o->objectName(), QString()); + int result = 0; - QVERIFY(object->setProperty("width", QVariant::fromValue(17))); - QVERIFY(parent->property("width").toInt() > 0); - QVERIFY(object->setProperty("height", QVariant::fromValue(53))); - QVERIFY(parent->property("height").toInt() > 0); + QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 16); + QCOMPARE(o->objectName(), QStringLiteral("aa93")); - QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1); - QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1); + QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 17); + QCOMPARE(o->objectName(), QStringLiteral("bb94")); - QCOMPARE(object->property("a").toInt(), 1024); + QObject *singleton = nullptr; + QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton)); + QVERIFY(singleton); + + QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 18); + QCOMPARE(singleton->objectName(), QStringLiteral("cc95")); + + QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 19); + QCOMPARE(singleton->objectName(), QStringLiteral("dd96")); } -void tst_QmlCppCodegen::shifts() +void tst_QmlCppCodegen::fileImportsContainCxxTypes() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/shifts.qml"_s)); - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - - QCOMPARE(object->property("a").toInt(), 9728); - QCOMPARE(object->property("b").toInt(), 4864); - QCOMPARE(object->property("c").toInt(), 19448); - QCOMPARE(object->property("d").toInt(), 9731); - QCOMPARE(object->property("e").toInt(), 0); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/usingCxxTypesFromFileImports.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), u"horst guenther"_s); } -void tst_QmlCppCodegen::valueTypeProperty() +void tst_QmlCppCodegen::flagEnum() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeProperty.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/flagEnum.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QFont font = qvariant_cast<QFont>(object->property("font")); - QCOMPARE(object->property("foo").toString(), font.family()); - font.setFamily(u"Bar"_s); - object->setProperty("font", QVariant::fromValue(font)); - QCOMPARE(object->property("foo").toString(), u"Bar"_s); + QQmlCommunicationPermission *p = qobject_cast<QQmlCommunicationPermission *>(o.data()); + QCOMPARE(p->communicationModes(), CommunicationPermission::Access); } -void tst_QmlCppCodegen::propertyOfParent() +void tst_QmlCppCodegen::flushBeforeCapture() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/RootWithoutId.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - - QObject *child = qmlContext(object.data())->objectForName(u"item"_s); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noBindingLoop.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - bool expected = false; + QCOMPARE(o->property("deviation").toDouble(), 9.0 / 3.3333); + QCOMPARE(o->property("samples").toInt(), 16); + QCOMPARE(o->property("radius").toDouble(), 8.0); +} - for (int i = 0; i < 3; ++i) { - const QVariant foo = object->property("foo"); - QCOMPARE(foo.metaType(), QMetaType::fromType<bool>()); - QCOMPARE(foo.toBool(), expected); +void tst_QmlCppCodegen::fromBoolValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fromBoolValue.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QCOMPARE(o->property("a").toBool(), true); + o->setProperty("x", 100); + QCOMPARE(o->property("a").toBool(), false); - const QVariant bar = object->property("bar"); - QCOMPARE(bar.metaType(), QMetaType::fromType<bool>()); - QCOMPARE(bar.toBool(), expected); + QCOMPARE(o->property("width").toInt(), 100); + QCOMPARE(o->property("b").toBool(), false); - const QVariant visible = child->property("visible"); - QCOMPARE(visible.metaType(), QMetaType::fromType<bool>()); - QCOMPARE(visible.toBool(), expected); + QScopedPointer<QObject> parent(c.create()); + o->setProperty("parent", QVariant::fromValue(parent.data())); + QCOMPARE(o->property("width").toInt(), 100); + QCOMPARE(o->property("b").toBool(), false); - expected = !expected; - object->setProperty("foo", expected); - } + o->setProperty("state", QVariant::fromValue(u"foo"_s)); + QCOMPARE(o->property("width").toInt(), 0); + QCOMPARE(o->property("b").toBool(), false); } -void tst_QmlCppCodegen::accessModelMethodFromOutSide() +void tst_QmlCppCodegen::funcWithParams() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/AccessModelMethodsFromOutside.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - - QTest::ignoreMessage(QtDebugMsg, "3"); - QTest::ignoreMessage(QtDebugMsg, "Apple"); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/funcWithParams.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); - - QCOMPARE(object->property("cost1").toDouble(), 3); - QCOMPARE(object->property("name1").toString(), u"Orange"_s); - QCOMPARE(object->property("cost2").toDouble(), 1.95); - QCOMPARE(object->property("name2").toString(), u"Banana"_s); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("bar").toInt(), 30); } // QML-generated types have no C++ names, but we want to call a method that @@ -1150,42 +1999,55 @@ void tst_QmlCppCodegen::accessModelMethodFromOutSide() // the metatype of the argument we pass does not match the metatype of the // argument the method expects. In order to work around it, we specialize // qMetaTypeInterfaceForType() and produce a "correct" metatype this way. -class Dummy_QMLTYPE_0; +class Dummy2_QMLTYPE_0; // We set this to the actual value retrieved from an actual instance of the QML // type before retrieving the metatype interface for the first time. static const QtPrivate::QMetaTypeInterface *dummyMetaTypeInterface = nullptr; template<> -const QtPrivate::QMetaTypeInterface *QtPrivate::qMetaTypeInterfaceForType<Dummy_QMLTYPE_0 *>() { +const QtPrivate::QMetaTypeInterface *QtPrivate::qMetaTypeInterfaceForType<Dummy2_QMLTYPE_0 *>() { return dummyMetaTypeInterface; } void tst_QmlCppCodegen::functionArguments() { + qmlClearTypeRegistrations(); + QQmlEngine engine; + QQmlComponent preheat(&engine); + preheat.setData(R"( + import QtQml + import TestTypes + QtObject { + objectName: Style.objectName + } + )", QUrl(u"qrc:/qt/qml/TestTypes/preheat.qml"_s)); + QVERIFY2(preheat.isReady(), qPrintable(preheat.errorString())); + QScopedPointer<QObject> hot(preheat.create()); + QVERIFY(!hot.isNull()); - // Ensure that Dummy gets counter value 0. Don't do that at home + // Ensure that Dummy gets counter value 1. Don't do that at home QScopedValueRollback<QAtomicInt> rb(QQmlPropertyCacheCreatorBase::classIndexCounter, 0); - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Dummy.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Dummy2.qml"_s)); QVERIFY2(component.isReady(), component.errorString().toUtf8()); QScopedPointer<QObject> object(component.create()); const QMetaObject *metaObject = object->metaObject(); dummyMetaTypeInterface = metaObject->metaType().iface(); const QByteArray className = QByteArray(metaObject->className()); - QCOMPARE(className, "Dummy_QMLTYPE_0"); + QCOMPARE(className, "Dummy2_QMLTYPE_0"); int result; int a = 1; bool b = false; - Dummy_QMLTYPE_0 *c = nullptr; + Dummy2_QMLTYPE_0 *c = nullptr; double d = -1.2; int e = 3; metaObject->invokeMethod( object.data(), "someFunction", Q_RETURN_ARG(int, result), - Q_ARG(int, a), Q_ARG(bool, b), Q_ARG(Dummy_QMLTYPE_0 *, c), + Q_ARG(int, a), Q_ARG(bool, b), Q_ARG(Dummy2_QMLTYPE_0 *, c), Q_ARG(double, d), Q_ARG(int, e)); QCOMPARE(result, 42); @@ -1198,473 +2060,780 @@ void tst_QmlCppCodegen::functionArguments() QCOMPARE(concatenated, u"foobar"_s); } -void tst_QmlCppCodegen::bindingExpression() +void tst_QmlCppCodegen::functionCallOnNamespaced() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BindingExpression.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - - QObject *child = qmlContext(object.data())->objectForName(u"child"_s); - - double width = 200; - double y = 10; - for (int i = 0; i < 10; ++i) { - QCOMPARE(object->property("width").toDouble(), width); - QCOMPARE(object->property("height").toDouble(), width); - QCOMPARE(object->property("y").toDouble(), y); - - const double childY = y + (width - 100) / 2; - QCOMPARE(child->property("y").toDouble(), childY); - QCOMPARE(object->property("mass"), childY > 100 ? u"heavy"_s : u"light"_s); - QCOMPARE(object->property("test_division").toDouble(), width / 1000 + 50); - QCOMPARE(object->property("test_ternary").toDouble(), 2.2); - - const int test_switch = object->property("test_switch").toInt(); - switch (int(width) % 3) { - case 0: - QCOMPARE(test_switch, 130); - break; - case 1: - QCOMPARE(test_switch, 380); - break; - case 2: - QCOMPARE(test_switch, 630); - break; - } + { + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themergood.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("i").toInt(), 12); + } - width = 200 * i; - y = 10 + i; - object->setProperty("width", width); - object->setProperty("y", y); + { + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themerbad.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(5.0, 10.0, 1.0, 1.0))); } } -void tst_QmlCppCodegen::voidFunction() +void tst_QmlCppCodegen::functionLookup() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/voidfunction.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QVERIFY(object->objectName().isEmpty()); - object->metaObject()->invokeMethod(object.data(), "doesNotReturnValue"); - QCOMPARE(object->objectName(), u"barbar"_s); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionLookup.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + const QVariant foo = o->property("bar"); + QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>()); + const QJSManagedValue method(engine.toScriptValue(foo), &engine); + QVERIFY(method.isFunction()); + const QJSValue result = method.call(); + QVERIFY(result.isString()); + QCOMPARE(result.toString(), QStringLiteral("a99")); } -void tst_QmlCppCodegen::overriddenProperty() +void tst_QmlCppCodegen::functionReturningVoid() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/childobject.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->objectName(), u"kraut"_s); - QCOMPARE(object->property("doneThing").toInt(), 5); - QCOMPARE(object->property("usingFinal").toInt(), 5); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionReturningVoid.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - auto checkAssignment = [&]() { - const QString newName = u"worscht"_s; - QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName)); - QCOMPARE(object->objectName(), newName); - }; - checkAssignment(); + // It should be able to call the methods and wrap the void values into invalid QVariants, + // without crashing. + QVERIFY(o->metaObject()->indexOfProperty("aa") >= 0); + QVERIFY(o->metaObject()->indexOfProperty("bb") >= 0); + QVERIFY(!o->property("aa").isValid()); + QVERIFY(!o->property("bb").isValid()); +} - ObjectWithMethod *benign = new ObjectWithMethod(object.data()); - benign->theThing = 10; - benign->setObjectName(u"cabbage"_s); - object->setProperty("child", QVariant::fromValue(benign)); - QCOMPARE(object->objectName(), u"cabbage"_s); - checkAssignment(); - QCOMPARE(object->property("doneThing").toInt(), 10); - QCOMPARE(object->property("usingFinal").toInt(), 10); +void tst_QmlCppCodegen::functionTakingVar() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/qt/qml/TestTypes/functionTakingVar.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - OverriddenObjectName *evil = new OverriddenObjectName(object.data()); - QTest::ignoreMessage(QtWarningMsg, - "Final member fff is overridden in class OverriddenObjectName. " - "The override won't be used."); - object->setProperty("child", QVariant::fromValue(evil)); + QVERIFY(!o->property("c").isValid()); - QCOMPARE(object->objectName(), u"borschtsch"_s); + int value = 11; + QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine); + void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) }; + QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() }; + e->executeRuntimeFunction(document, 0, o.data(), 1, args, types); - checkAssignment(); - QCOMPARE(object->property("doneThing").toInt(), 7); - QCOMPARE(object->property("usingFinal").toInt(), 5); + QCOMPARE(o->property("c"), QVariant::fromValue<int>(11)); } -void tst_QmlCppCodegen::listLength() +void tst_QmlCppCodegen::getLookupOfScript() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listlength.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("l").toInt(), 2); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/NotificationItem.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), u"heading"_s); } -void tst_QmlCppCodegen::parentProperty() +void tst_QmlCppCodegen::getOptionalLookup_data() { - QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/parentProp.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> object(component.create()); - QVERIFY(!object.isNull()); - QCOMPARE(object->property("c").toInt(), 11); - QCOMPARE(object->property("i").toInt(), 22); - object->setProperty("a", QVariant::fromValue(22)); - QCOMPARE(object->property("c").toInt(), 28); - object->setProperty("implicitWidth", QVariant::fromValue(14)); - QCOMPARE(object->property("i").toInt(), 26); + QTest::addColumn<QString>("propertyName"); + QTest::addColumn<QVariant>("expected"); - QObject *child = qmlContext(object.data())->objectForName(u"child"_s); - QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_s); - QObject *evil = qmlContext(object.data())->objectForName(u"evil"_s); + // Objects + QTest::addRow("int on object") << u"to1"_s << QVariant(5); + QTest::addRow("string on object") << u"to2"_s << QVariant("6"); + QTest::addRow("object on object") << u"to3"_s << QVariant::fromValue<QObject *>(nullptr); + QTest::addRow("int on null") << u"to4"_s << QVariant(); // undefined + QTest::addRow("any on undefined as object") << u"to5"_s << QVariant(); // undefined + QTest::addRow("int on string on object") << u"to6"_s << QVariant(1); - child->setProperty("parent", QVariant::fromValue(sibling)); + // Value Types + QTest::addRow("int on rect") << u"tv1"_s << QVariant(50); + QTest::addRow("int on point") << u"tv2"_s << QVariant(-10); + QTest::addRow("int on undefined as point") << u"tv4"_s << QVariant(); // undefined - QCOMPARE(child->property("b").toInt(), 0); - QCOMPARE(child->property("i").toInt(), 28); - QCOMPARE(object->property("i").toInt(), 56); + // Enums + QTest::addRow("enum on object") << u"te1"_s << QVariant(1); + QTest::addRow("enum on type") << u"te2"_s << QVariant(1); + QTest::addRow("enums comparison 1") << u"te3"_s << QVariant(false); + QTest::addRow("enums comparison 2") << u"te4"_s << QVariant(true); - child->setProperty("parent", QVariant::fromValue(evil)); + // Complex chains + QTest::addRow("mixed 1") << u"tc1"_s << QVariant(-10); + QTest::addRow("mixed 2") << u"tc2"_s << QVariant(0); + QTest::addRow("early out 1") << u"tc4"_s << QVariant(); // undefined + QTest::addRow("early out 2") << u"tc5"_s << QVariant(); // undefined +} - QCOMPARE(child->property("b").toInt(), 5994); - QCOMPARE(object->property("c").toInt(), 5996); +void tst_QmlCppCodegen::getOptionalLookup() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/qt/qml/TestTypes/getOptionalLookup.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(child->property("i").toInt(), 443); - QCOMPARE(object->property("i").toInt(), 886); + QFETCH(QString, propertyName); + QFETCH(QVariant, expected); - { - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/specificParent.qml"_s)); + QVariant actual = o->property(propertyName.toLocal8Bit()); + QCOMPARE(actual, expected); +} - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); +void tst_QmlCppCodegen::getOptionalLookupOnQJSValueNonStrict() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/qt/qml/TestTypes/GetOptionalLookupOnQJSValueNonStrict.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(rootObject->property("a").toReal(), 77.0); - } + QVERIFY(o->property("b").toBool()); } -void tst_QmlCppCodegen::registerElimination() +void tst_QmlCppCodegen::getOptionalLookupShadowed() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerelimination.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); + const QUrl document(u"qrc:/qt/qml/TestTypes/GetOptionalLookupShadowed.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->property("res").toString(), "a"); +} + +void tst_QmlCppCodegen::globals() +{ + QQmlEngine engine; + int exitCode = -1; + QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; }); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/globals.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + + const QByteArray message = QByteArray("Start 2 ") + arg1(); + QTest::ignoreMessage(QtDebugMsg, message.constData()); + QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); + QTRY_COMPARE(exitCode, 0); - // Increment of 23 hits both 0 and 460 - for (int input = -23; input < 700; input += 23) { - object->setProperty("input", input); - if (input <= 0 || input >= 460) - QCOMPARE(object->property("output").toInt(), 459); - else - QCOMPARE(object->property("output").toInt(), input); - } + QObject *application = qvariant_cast<QObject *>(object->property("application")); + QVERIFY(application); + QCOMPARE(QString::fromUtf8(application->metaObject()->className()), + u"QQuickApplication"_s); + + QTest::ignoreMessage(QtDebugMsg, "End"); + QMetaObject::invokeMethod(application, "aboutToQuit"); + + const QVariant somewhere = object->property("somewhere"); + QCOMPARE(somewhere.userType(), QMetaType::QUrl); + QCOMPARE(qvariant_cast<QUrl>(somewhere).toString(), u"qrc:/somewhere/else.qml"_s); + + const QVariant somewhereString = object->property("somewhereString"); + QCOMPARE(somewhereString.userType(), QMetaType::QString); + QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_s); + + const QVariant plain = object->property("plain"); + QCOMPARE(plain.userType(), QMetaType::QUrl); + QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_s); } -void tst_QmlCppCodegen::asCast() +void tst_QmlCppCodegen::idAccess() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/asCast.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QScopedPointer<QObject> root(component.create()); - QVERIFY(!root.isNull()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/idAccess.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - QQmlContext *context = qmlContext(root.data()); - const QObject *object = context->objectForName(u"object"_s); - const QObject *item = context->objectForName(u"item"_s); - const QObject *rectangle = context->objectForName(u"rectangle"_s); - const QObject *dummy = context->objectForName(u"dummy"_s); + QVERIFY(object->property("y").toInt() != 48); + QCOMPARE(object->property("y").toInt(), 12); + object->setProperty("z", 13); + QCOMPARE(object->property("y").toInt(), 13); + object->setProperty("x", QVariant::fromValue(333)); + QCOMPARE(object->property("y").toInt(), 48); - QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsObject")), object); - QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsItem")), nullptr); - QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsRectangle")), nullptr); - QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsDummy")), nullptr); + // The binding was broken by setting the property + object->setProperty("z", 14); + QCOMPARE(object->property("y").toInt(), 48); - QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsObject")), item); - QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsItem")), item); - QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsRectangle")), nullptr); - QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsDummy")), nullptr); + QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_s); + QFont f = qvariant_cast<QFont>(ttt->property("font")); + QCOMPARE(f.pointSize(), 22); - QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsObject")), rectangle); - QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsItem")), rectangle); - QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsRectangle")), rectangle); - QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsDummy")), nullptr); + QObject::connect(object.data(), &QObject::objectNameChanged, ttt, [&](){ + ttt->setParent(nullptr); + QJSEngine::setObjectOwnership(ttt, QJSEngine::CppOwnership); + object.reset(ttt); + }); - QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsObject")), dummy); - QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsItem")), dummy); - QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsRectangle")), nullptr); - QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsDummy")), dummy); + QVERIFY(object->objectName().isEmpty()); + QVERIFY(ttt->objectName().isEmpty()); + ttt->setProperty("text", u"kill"_s); + QCOMPARE(object.data(), ttt); + QCOMPARE(ttt->objectName(), u"context"_s); } -void tst_QmlCppCodegen::noQQmlData() +void tst_QmlCppCodegen::ignoredFunctionReturn() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noQQmlData.qml"_s)); - QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ignoredFunctionReturn.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); +} - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: " - "Cannot read property 'name' of null"); - QScopedPointer<QObject> root(component.create()); - QVERIFY(!root.isNull()); +void tst_QmlCppCodegen::importsFromImportPath() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml"_s)); - BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data()); - QVERIFY(party != nullptr); + // We might propagate the import path, eventually, but for now instantiating is not important. + // If the compiler accepts the file, it's probably fine. + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), + u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_s); +} - QCOMPARE(party->host(), nullptr); - QCOMPARE(party->property("n").toString(), QString()); +void tst_QmlCppCodegen::inPlaceDecrement() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dialog.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QObject *header = qvariant_cast<QObject *>(object->property("header")); + QVERIFY(header); + QObject *background = qvariant_cast<QObject *>(header->property("background")); + QObject *parent = qvariant_cast<QObject *>(background->property("parent")); - Person *host1 = new Person(party); - party->setHost(host1); - QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s); - host1->setName(u"Marge"_s); - QCOMPARE(party->property("n").toString(), u"Marge in da house!"_s); + QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1); + QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1); - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: " - "Cannot read property 'name' of null"); + QVERIFY(object->setProperty("width", QVariant::fromValue(17))); + QVERIFY(parent->property("width").toInt() > 0); + QVERIFY(object->setProperty("height", QVariant::fromValue(53))); + QVERIFY(parent->property("height").toInt() > 0); - // Doesn't crash - party->setHost(nullptr); + QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1); + QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1); - // Lookups are initialized now, and we introduce an object without QQmlData - Person *host2 = new Person(party); - party->setHost(host2); - QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s); - host2->setName(u"Homer"_s); - QCOMPARE(party->property("n").toString(), u"Homer in da house!"_s); + QCOMPARE(object->property("a").toInt(), 1024); +} - QMetaObject::invokeMethod(party, "burn"); - engine.collectGarbage(); +void tst_QmlCppCodegen::inaccessibleProperty() +{ + QQmlEngine engine; - // Does not crash - party->setProperty("inDaHouse", u" burns!"_s); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/versionmismatch.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - // Mr Burns may or may not burn, depending on whether we use lookups. - // If using lookups, the binding is aborted when we find the isQueuedForDeletion flag. - // If reading the property directly, we don't have to care about it. - QVERIFY(party->property("n").toString().startsWith(u"Mr Burns"_s)); + QCOMPARE(o->property("c").toInt(), 5); } -void tst_QmlCppCodegen::scopeObjectDestruction() +void tst_QmlCppCodegen::indirectlyShadowable() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fileDialog.qml"_s)); - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); + const QString url = u"qrc:/qt/qml/TestTypes/indirectlyShadowable.qml"_s; + QQmlComponent c(&engine, QUrl(url)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QObject *dialog = rootObject->property("dialog").value<QObject *>(); - QVERIFY(dialog); + const auto verifyShadowable = [&](const QString &objectName) { + QObject *outer = o->property("outer").value<QObject *>(); + QVERIFY(outer); + QObject *inner = outer->property("inner").value<QObject *>(); + QVERIFY(inner); + QObject *shadowable = inner->property("shadowable").value<QObject *>(); + QVERIFY(shadowable); + QCOMPARE(shadowable->objectName(), objectName); + }; - // We cannot check the warning messages. The AOT compiled code complains about reading the - // "parent" property of an object scheduled for deletion. The runtime silently returns undefined - // at that point and then complains about not being able to read a property on undefined. + const auto verifyNotShadowable = [&](const QString &objectName) { + QObject *notShadowable = o->property("notShadowable").value<QObject *>(); + QCOMPARE(notShadowable->objectName(), objectName); + }; - // Doesn't crash, even though it triggers bindings on scope objects scheduled for deletion. - QMetaObject::invokeMethod(dialog, "open"); -} + const auto verifyEvil = [&]() { + QObject *outer = o->property("outer").value<QObject *>(); + QVERIFY(outer); + QCOMPARE(outer->property("inner").toString(), u"evil"_s); + }; -static void checkColorProperties(QQmlComponent *component) -{ - QVERIFY2(component->isReady(), qPrintable(component->errorString())); - QScopedPointer<QObject> rootObject(component->create()); - QVERIFY(rootObject); + verifyShadowable(u"shadowable"_s); + verifyNotShadowable(u"notShadowable"_s); - const QMetaObject *mo = QMetaType::fromName("QQuickIcon").metaObject(); - QVERIFY(mo != nullptr); + QMetaObject::invokeMethod(o.data(), "setInnerShadowable"); - const QMetaProperty prop = mo->property(mo->indexOfProperty("color")); - QVERIFY(prop.isValid()); + verifyShadowable(u"self"_s); + verifyNotShadowable(u"notShadowable"_s); - const QVariant a = rootObject->property("a"); - QVERIFY(a.isValid()); + QMetaObject::invokeMethod(o.data(), "getInnerShadowable"); - const QVariant iconColor = prop.readOnGadget(rootObject->property("icon").data()); - QVERIFY(iconColor.isValid()); + verifyShadowable(u"self"_s); + verifyNotShadowable(u"self"_s); - const QMetaType colorType = QMetaType::fromName("QColor"); - QVERIFY(colorType.isValid()); + QMetaObject::invokeMethod(o.data(), "turnEvil"); - QCOMPARE(a.metaType(), colorType); - QCOMPARE(iconColor.metaType(), colorType); + verifyEvil(); + verifyNotShadowable(u"self"_s); - QCOMPARE(iconColor, a); + // Does not produce an error message because JavaScript. + QMetaObject::invokeMethod(o.data(), "setInnerShadowable"); + + verifyEvil(); + verifyNotShadowable(u"self"_s); + + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":29: Error: Cannot assign [undefined] to QObject*"_s)); + QMetaObject::invokeMethod(o.data(), "getInnerShadowable"); + + verifyEvil(); + verifyNotShadowable(u"self"_s); } -void tst_QmlCppCodegen::colorAsVariant() +void tst_QmlCppCodegen::infinities() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorAsVariant.qml"_s)); - checkColorProperties(&component); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinities.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity()); + QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity()); + + const double positiveZero = o->property("positiveZero").toDouble(); + QCOMPARE(positiveZero, 0.0); + QVERIFY(!std::signbit(positiveZero)); + + const double negativeZero = o->property("negativeZero").toDouble(); + QCOMPARE(negativeZero, -0.0); + QVERIFY(std::signbit(negativeZero)); + + QVERIFY(qIsNaN(o->property("naN").toDouble())); } -void tst_QmlCppCodegen::bindToValueType() +void tst_QmlCppCodegen::infinitiesToInt() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/bindToValueType.qml"_s)); - checkColorProperties(&component); + + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinitiesToInt.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + const char *props[] = {"a", "b", "c"}; + for (const char *prop : props) { + const QVariant i = o->property(prop); + QCOMPARE(i.metaType(), QMetaType::fromType<int>()); + bool ok = false; + QCOMPARE(i.toInt(&ok), 0); + QVERIFY(ok); + } } -void tst_QmlCppCodegen::undefinedResets() +void tst_QmlCppCodegen::innerObjectNonShadowable() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedResets.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ownProperty.qml"_s)); QVERIFY2(component.isReady(), qPrintable(component.errorString())); QScopedPointer<QObject> rootObject(component.create()); QVERIFY(rootObject); - Person *person = qobject_cast<Person *>(rootObject.data()); - QVERIFY(person); - QCOMPARE(person->shoeSize(), 0); - QCOMPARE(person->name(), u"Marge"_s); - - person->setShoeSize(11); + QCOMPARE(rootObject->objectName(), u"foo"_s); +} - QCOMPARE(person->shoeSize(), 11); - QCOMPARE(person->name(), u"Bart"_s); +void tst_QmlCppCodegen::intEnumCompare() +{ + QQmlEngine engine; + { + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intEnumCompare.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("a").toBool(), true); + QCOMPARE(o->property("b").toBool(), false); + QCOMPARE(o->property("c").toBool(), true); + QCOMPARE(o->property("d").toBool(), false); + } - person->setShoeSize(10); - QCOMPARE(person->shoeSize(), 10); - QCOMPARE(person->name(), u"Marge"_s); + { + // We cannot use Qt.red in QML because it's lower case. + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumInvalid.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("c").toBool(), true); + QCOMPARE(o->property("d").toBool(), false); + } +} - person->setName(u"no one"_s); - QCOMPARE(person->name(), u"no one"_s); +void tst_QmlCppCodegen::intOverflow() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intOverflow.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("a").toDouble(), 1.09951162778e+12); + QCOMPARE(object->property("b").toInt(), 5); +} - person->setObjectName(u"the one"_s); - QCOMPARE(person->name(), u"Bart"_s); +void tst_QmlCppCodegen::intToEnum() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intToEnum.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + MyType *m = qobject_cast<MyType *>(o.data()); + QCOMPARE(m->a(), MyType::D); + QCOMPARE(m->property("b").toInt(), 24); } -void tst_QmlCppCodegen::innerObjectNonShadowable() +void tst_QmlCppCodegen::interceptor() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ownProperty.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/interceptor.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); + QCOMPARE(object->property("x").toInt(), 100); + QCOMPARE(object->property("y").toInt(), 100); - QCOMPARE(rootObject->objectName(), u"foo"_s); + QVERIFY(object->property("width").toInt() != 200); + QVERIFY(object->property("height").toInt() != 200); + QVERIFY(object->property("qProperty1").toInt() != 300); + QVERIFY(object->property("qProperty2").toInt() != 300); + QTRY_COMPARE(object->property("width").toInt(), 200); + QTRY_COMPARE(object->property("height").toInt(), 200); + QTRY_COMPARE(object->property("qProperty1").toInt(), 300); + QTRY_COMPARE(object->property("qProperty2").toInt(), 300); } -void tst_QmlCppCodegen::ownPropertiesNonShadowable() +void tst_QmlCppCodegen::interestingFiles() { + QFETCH(QString, file); + QFETCH(bool, isValid); + QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/overriddenMember.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/%1"_s.arg(file))); + if (isValid) { + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + } else { + QVERIFY(component.isError()); + } +} - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); +void tst_QmlCppCodegen::interestingFiles_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<bool>("isValid"); - QCOMPARE(rootObject->property("ppp").toInt(), 16); - QCOMPARE(rootObject->property("ppp2").toInt(), 9); - QCOMPARE(rootObject->property("ppp3").toInt(), 12); + QTest::addRow("conversions2") << u"conversions2.qml"_s << true; + QTest::addRow("TestCase") << u"TestCase.qml"_s << true; + QTest::addRow("layouts") << u"layouts.qml"_s << true; + QTest::addRow("interactive") << u"interactive.qml"_s << true; + QTest::addRow("Panel") << u"Panel.qml"_s << true; + QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_s << true; + QTest::addRow("Root") << u"ProgressBar/Root.qml"_s << true; + QTest::addRow("noscope") << u"noscope.qml"_s << true; + QTest::addRow("dynamicscene") << u"dynamicscene.qml"_s << true; + QTest::addRow("curlygrouped") << u"curlygrouped.qml"_s << true; + QTest::addRow("cycleHead") << u"cycleHead.qml"_s << false; + QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_s << true; + QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_s << true; } -void tst_QmlCppCodegen::modulePrefix() +void tst_QmlCppCodegen::internalConversion() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/modulePrefix.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/internalConversion.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(!obj.isNull()); - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); + QObject *offset = obj->property("offset").value<QObject *>(); + QVERIFY(offset); - QCOMPARE(rootObject->property("foo").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); - QCOMPARE(rootObject->property("bar").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); - QCOMPARE(rootObject->property("baz").toString(), QStringLiteral("ItIsTheSingleton")); + QCOMPARE(offset->objectName(), "hello"_L1); + QCOMPARE(offset->property("mark").toString(), "hello"_L1); } -void tst_QmlCppCodegen::colorString() +void tst_QmlCppCodegen::invalidPropertyType() { + // Invisible on purpose + qmlRegisterType<MyCppType>("App", 1, 0, "MyCppType"); + QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorString.qml"_s)); + QQmlComponent okComponent(&engine, QUrl(u"qrc:/qt/qml/TestTypes/OkType.qml"_s)); + QVERIFY2(okComponent.isReady(), qPrintable(okComponent.errorString())); + QScopedPointer<QObject> picker(okComponent.create()); + QVERIFY2(!picker.isNull(), qPrintable(okComponent.errorString())); + QObject *inner = qmlContext(picker.data())->objectForName(u"inner"_s); + QVERIFY(inner); + MyCppType *myCppType = qobject_cast<MyCppType *>(inner); + QVERIFY(myCppType); + QVERIFY(!myCppType->useListDelegate()); - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BadType.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.createWithInitialProperties( + QVariantMap {{u"picker"_s, QVariant::fromValue(picker.data())}})); + QVERIFY2(!o.isNull(), qPrintable(c.errorString())); + QVERIFY(!myCppType->useListDelegate()); - QCOMPARE(qvariant_cast<QColor>(rootObject->property("c")), QColor::fromRgb(0xdd, 0xdd, 0xdd)); - QCOMPARE(qvariant_cast<QColor>(rootObject->property("d")), QColor::fromRgb(0xaa, 0xaa, 0xaa)); - QCOMPARE(qvariant_cast<QColor>(rootObject->property("e")), QColor::fromRgb(0x11, 0x22, 0x33)); + o->setProperty("useListDelegate", QVariant::fromValue<bool>(true)); + QVERIFY(myCppType->useListDelegate()); } -void tst_QmlCppCodegen::urlString() +void tst_QmlCppCodegen::invisibleBase() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/urlString.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleBase.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(qvariant_cast<QObject *>(o->property("n")), o.data()); +} - QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject); +void tst_QmlCppCodegen::invisibleListElementType() +{ + qmlRegisterType<InvisibleListElementType>("Invisible", 1, 0, "InvisibleListElement"); + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleListElementType.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s)); - QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s)); - QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s)); + QObject *a = o->property("a").value<QObject *>(); + QVERIFY(a); + + const QVariant x = a->property("x"); + QCOMPARE(x.metaType(), QMetaType::fromType<QQmlListReference>()); + const QQmlListReference ref = x.value<QQmlListReference>(); + QVERIFY(ref.isValid()); + QCOMPARE(ref.size(), 0); } +void tst_QmlCppCodegen::invisibleSingleton() +{ + // We may have seen Style.qml as singleton before, which would make the assignment pass. + // So let's flush the type registry. + qmlClearTypeRegistrations(); -void tst_QmlCppCodegen::callContextPropertyLookupResult() + QQmlEngine engine; + const QUrl copy(u"qrc:/qt/qml/HiddenTestTypes/hidden/Main.qml"_s); + QQmlComponent c(&engine, copy); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + "qrc:/qt/qml/HiddenTestTypes/hidden/Main.qml:4:5: " + "Unable to assign [undefined] to QColor"); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("c"), QVariant(QMetaType::fromName("QColor"))); +} + +void tst_QmlCppCodegen::invisibleTypes() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callContextPropertyLookupResult.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleTypes.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr); + QObject *singleton = qvariant_cast<QObject *>(o->property("singleton")); + QVERIFY(singleton != nullptr); + QCOMPARE(singleton->metaObject()->className(), "SingletonModel"); + + QObject *attached = qvariant_cast<QObject *>(o->property("attached")); + QVERIFY(attached != nullptr); + QCOMPARE(attached->metaObject()->className(), "AttachedAttached"); + +// TODO: This doesn't work in interpreted mode: +// const QMetaObject *meta = qvariant_cast<const QMetaObject *>(o->property("metaobject")); +// QVERIFY(meta != nullptr); +// QCOMPARE(meta->className(), "DerivedFromInvisible"); } -void tst_QmlCppCodegen::deadShoeSize() +void tst_QmlCppCodegen::iteration() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/deadShoeSize.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/iteration.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/deadShoeSize.qml:5: Error: ouch"); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("shoeSize").toInt(), 0); + QVERIFY(!o.isNull()); + + QCOMPARE(o->objectName(), "a345b345c345"_L1); } -void tst_QmlCppCodegen::listIndices() +void tst_QmlCppCodegen::javaScriptArgument() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listIndices.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/javaScriptArgument.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QVERIFY(!o.isNull()); - QQmlListReference list(o.data(), "items"); - QCOMPARE(list.count(), 3); - for (int i = 0; i < 3; ++i) - QCOMPARE(list.at(i), o.data()); - QCOMPARE(o->property("numItems").toInt(), 3); - QCOMPARE(qvariant_cast<QObject *>(o->property("fractional")), nullptr); - QCOMPARE(qvariant_cast<QObject *>(o->property("negativeZero")), o.data()); - QCOMPARE(qvariant_cast<QObject *>(o->property("infinity")), nullptr); - QCOMPARE(qvariant_cast<QObject *>(o->property("nan")), nullptr); + QCOMPARE(o->property("a").toDouble(), 4.0); + QCOMPARE(o->property("b").toDouble(), 9.0); + QCOMPARE(o->property("c").toString(), u"5t-1"_s); + QCOMPARE(o->property("d").toString(), u"9"_s); + QCOMPARE(o->property("e").toString(), u"10"_s); + QCOMPARE(o->property("f").toString(), u"-10"_s); + + const QStringList scales { + "0 ", "1 ", "10 ", "100 ", "1000 ", "9.77k", "97.7k", "977k", "9.54M", "95.4M", "954M", + "9.31G", "93.1G", "931G", "9.09T", "-1 ", "-10 ", "-100 ", "-1000 ", "-9.77k", "-97.7k", + "-977k", "-9.54M", "-95.4M", "-954M", "-9.31G", "-93.1G", "-931G", "-9.09T" + }; + + QCOMPARE(o->property("scales").value<QStringList>(), scales); + + double thing = 12.0; + QString result; + QMetaObject::invokeMethod( + o.data(), "forwardArg", Q_RETURN_ARG(QString, result), Q_ARG(double, thing)); + QCOMPARE(result, u"12 "); } -static const double numbers[] = { - qQNaN(), -qInf(), - std::numeric_limits<double>::min(), - std::numeric_limits<float>::min(), - std::numeric_limits<qint32>::min(), - -1000.2, -100, -2, -1.333, -1, -0.84, -0.5, +void tst_QmlCppCodegen::jsArrayMethods() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethods.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - // -0 and 0 are not different on the QML side. Therefore, don't keep them adjacent. - // Otherwise the bindings won't get re-evaluated. - std::copysign(0.0, -1), 1, 0.0, + QQmlComponent untyped(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsUntyped.qml"_s)); + QVERIFY2(untyped.isReady(), qPrintable(untyped.errorString())); + QScopedPointer<QObject> check(untyped.create()); + QVERIFY(!check.isNull()); - 0.5, 0.77, 1.4545, 2, 199, 2002.13, - std::numeric_limits<qint32>::max(), - std::numeric_limits<quint32>::max(), - std::numeric_limits<float>::max(), - std::numeric_limits<double>::max(), - qInf() -}; + check->setProperty("l1", object->property("l1")); + check->setProperty("l2", object->property("l2")); + check->setProperty("l3", object->property("l3")); + + QCOMPARE(object->property("listPropertyToString"), object->property("jsArrayToString")); + QCOMPARE(object->property("listPropertyToString"), check->property("jsArrayToString")); + + QCOMPARE(object->property("listPropertyIncludes"), object->property("jsArrayIncludes")); + QVERIFY(object->property("listPropertyIncludes").toBool()); + + QCOMPARE(object->property("listPropertyJoin"), object->property("jsArrayJoin")); + QCOMPARE(object->property("listPropertyJoin"), check->property("jsArrayJoin")); + QVERIFY(object->property("listPropertyJoin").toString().contains(QStringLiteral("klaus"))); + + QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf")); + QCOMPARE(object->property("listPropertyIndexOf").toInt(), 1); + + QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf")); + QCOMPARE(object->property("listPropertyLastIndexOf").toInt(), 5); +} + +void tst_QmlCppCodegen::jsArrayMethodsWithParams() +{ + QFETCH(int, i); + QFETCH(int, j); + QFETCH(int, k); + QQmlEngine engine; + QQmlComponent component + (&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsWithParams.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QQmlComponent untyped( + &engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsWithParamsUntyped.qml"_s)); + QVERIFY2(untyped.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.createWithInitialProperties({ + {QStringLiteral("i"), i}, + {QStringLiteral("j"), j}, + {QStringLiteral("k"), k} + })); + QVERIFY(!object.isNull()); + QScopedPointer<QObject> check(untyped.createWithInitialProperties({ + {QStringLiteral("i"), i}, + {QStringLiteral("j"), j}, + {QStringLiteral("k"), k} + })); + QVERIFY(!check.isNull()); + check->setProperty("l1", object->property("l1")); + check->setProperty("l2", object->property("l2")); + check->setProperty("l3", object->property("l3")); + + listsEqual(object.data(), object.data(), "Slice"); + listsEqual(object.data(), check.data(), "Slice"); + QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf")); + QCOMPARE(object->property("listPropertyIndexOf"), check->property("jsArrayIndexOf")); + QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf")); + QCOMPARE(object->property("listPropertyLastIndexOf"), check->property("jsArrayLastIndexOf")); +} + +void tst_QmlCppCodegen::jsArrayMethodsWithParams_data() +{ + QTest::addColumn<int>("i"); + QTest::addColumn<int>("j"); + QTest::addColumn<int>("k"); + + const int indices[] = { + std::numeric_limits<int>::min(), + -10, -3, -2, -1, 0, 1, 2, 3, 10, + std::numeric_limits<int>::max(), + }; + + // We cannot test the full cross product. So, take a random sample instead. + const qsizetype numIndices = sizeof(indices) / sizeof(int); + qsizetype seed = QRandomGenerator::global()->generate(); + const int numSamples = 4; + for (int i = 0; i < numSamples; ++i) { + seed = qHash(i, seed); + const int vi = indices[qAbs(seed) % numIndices]; + for (int j = 0; j < numSamples; ++j) { + seed = qHash(j, seed); + const int vj = indices[qAbs(seed) % numIndices]; + for (int k = 0; k < numSamples; ++k) { + seed = qHash(k, seed); + const int vk = indices[qAbs(seed) % numIndices]; + const QString tag = QLatin1String("%1/%2/%3").arg( + QString::number(vi), QString::number(vj), QString::number(vk)); + QTest::newRow(qPrintable(tag)) << vi << vj << vk; + + // output all the tags so that we can find out + // what combination caused a test to hang. + qDebug().noquote() << "scheduling" << tag; + } + } + } +} + +void tst_QmlCppCodegen::jsImport() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsimport.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("value").toInt(), 42); +} void tst_QmlCppCodegen::jsMathObject() { @@ -1707,708 +2876,1247 @@ void tst_QmlCppCodegen::jsMathObject() qDebug() << name << "failed."; } -void tst_QmlCppCodegen::intEnumCompare() +void tst_QmlCppCodegen::jsmoduleImport() { QQmlEngine engine; - { - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intEnumCompare.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("a").toBool(), true); - QCOMPARE(o->property("b").toBool(), false); - QCOMPARE(o->property("c").toBool(), true); - QCOMPARE(o->property("d").toBool(), false); - } - - { - // We cannot use Qt.red in QML because it's lower case. - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumInvalid.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("c").toBool(), true); - QCOMPARE(o->property("d").toBool(), false); - } + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsmoduleimport.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("ok").toBool(), true); + QVariant okFunc = object->property("okFunc"); + QCOMPARE(okFunc.metaType(), QMetaType::fromType<QJSValue>()); + QJSValue val = engine.toScriptValue(okFunc); + QJSValue result = val.call(); + QVERIFY(result.isBool()); + QVERIFY(result.toBool()); } -void tst_QmlCppCodegen::attachedSelf() +void tst_QmlCppCodegen::lengthAccessArraySequenceCompat() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/SelectionRectangle.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ArraySequenceLengthInterop.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("length").toInt(), 100); +} - QObject *handle = qvariant_cast<QObject *>(o->property("aa")); - QVERIFY(handle); - QVERIFY(qvariant_cast<QObject *>(handle->property("rect")) != nullptr); +void tst_QmlCppCodegen::letAndConst() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/letAndConst.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), u"ab"_s); } -void tst_QmlCppCodegen::functionReturningVoid() +void tst_QmlCppCodegen::listAsArgument() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionReturningVoid.qml"_s)); + + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listAsArgument.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QCOMPARE(o->property("i").toInt(), 4); + QCOMPARE(o->property("j").toInt(), 2); + QCOMPARE(o->property("i1").toInt(), 2); + QCOMPARE(o->property("i2").toInt(), 4); + QCOMPARE(o->property("d").value<QObject *>()->objectName(), u"this one"_s); - // It should be able to call the methods and wrap the void values into invalid QVariants, - // without crashing. - QVERIFY(o->metaObject()->indexOfProperty("aa") >= 0); - QVERIFY(o->metaObject()->indexOfProperty("bb") >= 0); - QVERIFY(!o->property("aa").isValid()); - QVERIFY(!o->property("bb").isValid()); + int singleInt = 0; + QList<int> moreInts; + QMetaObject::invokeMethod(o.data(), "returnInts1", Q_RETURN_ARG(QList<int>, moreInts)); + QCOMPARE(moreInts, QList<int>({5, 4, 3, 2, 1})); + QMetaObject::invokeMethod(o.data(), "selectSecondInt", Q_RETURN_ARG(int, singleInt), Q_ARG(QList<int>, moreInts)); + QCOMPARE(singleInt, 4); } -void tst_QmlCppCodegen::functionCallOnNamespaced() +void tst_QmlCppCodegen::listConversion() { - QQmlEngine engine; - { - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themergood.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("i").toInt(), 12); - } + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/listConversion.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - { - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themerbad.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(5.0, 10.0, 1.0, 1.0))); + QQmlListProperty<QObject> list = o->property("o").value<QQmlListProperty<QObject>>(); + QCOMPARE(list.count(&list), 3); + for (int i = 0; i < 3; ++i) { + QObject *entry = list.at(&list, i); + Person *person = qobject_cast<Person *>(entry); + QVERIFY(person); + QCOMPARE(person->name(), u"Horst %1"_s.arg(i + 1)); } + + QStringList strings = o->property("s").value<QStringList>(); + QCOMPARE(strings, QStringList({u"Horst 1"_s, u"Horst 2"_s, u"Horst 3"_s})); + + QVariantList vars = o->property("v").toList(); + QCOMPARE(vars, QVariantList({ + QString(), + QVariant::fromValue<qsizetype>(3), + QVariant::fromValue<Person *>(nullptr) + })); } -void tst_QmlCppCodegen::flushBeforeCapture() +void tst_QmlCppCodegen::listIndices() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noBindingLoop.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listIndices.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(o); - QCOMPARE(o->property("deviation").toDouble(), 9.0 / 3.3333); - QCOMPARE(o->property("samples").toInt(), 16); - QCOMPARE(o->property("radius").toDouble(), 8.0); + QQmlListReference list(o.data(), "items"); + QCOMPARE(list.count(), 3); + for (int i = 0; i < 3; ++i) + QCOMPARE(list.at(i), o.data()); + QCOMPARE(o->property("numItems").toInt(), 3); + QCOMPARE(qvariant_cast<QObject *>(o->property("fractional")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(o->property("negativeZero")), o.data()); + QCOMPARE(qvariant_cast<QObject *>(o->property("infinity")), nullptr); + QCOMPARE(qvariant_cast<QObject *>(o->property("nan")), nullptr); } -void tst_QmlCppCodegen::unknownAttached() +void tst_QmlCppCodegen::listLength() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownAttached.qml"_s)); - QVERIFY(c.isError()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listlength.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("l").toInt(), 2); } -void tst_QmlCppCodegen::variantlist() +void tst_QmlCppCodegen::listOfInvisible() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantlist.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - - const QVariantList things = qvariant_cast<QVariantList>(o->property("things")); - QCOMPARE(things.size(), 2); - QCOMPARE(things[0].toString(), u"thing"_s); - QCOMPARE(things[1].toInt(), 30); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listOfInvisible.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("width").toDouble(), 27.0); } -void tst_QmlCppCodegen::popContextAfterRet() +void tst_QmlCppCodegen::listPropertyAsModel() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/popContextAfterRet.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listPropertyAsModel.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->objectName(), QString()); - o->setProperty("stackViewDepth", 1); - QCOMPARE(o->objectName(), u"backgroundImage"_s); - o->setProperty("stackViewDepth", 2); - QCOMPARE(o->objectName(), u"backgroundBlur"_s); - o->setProperty("stackViewDepth", 1); - QCOMPARE(o->objectName(), u"backgroundImage"_s); + QQmlListReference children(o.data(), "children"); + QCOMPARE(children.count(), 5); } -void tst_QmlCppCodegen::revisions() +void tst_QmlCppCodegen::listToString() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/revisions.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listToString.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage(QtDebugMsg, "[one,two]"); + QTest::ignoreMessage(QtDebugMsg, "one,two"); + QTest::ignoreMessage(QtDebugMsg, "[1,2]"); + QTest::ignoreMessage(QtDebugMsg, "1,2"); + QTest::ignoreMessage( + QtDebugMsg, + QRegularExpression("\\[QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)," + "QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)\\]")); + QTest::ignoreMessage( + QtDebugMsg, + QRegularExpression("QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)," + "QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)")); + + QTest::ignoreMessage(QtDebugMsg, "[a,b]"); + QScopedPointer<QObject> o(c.create()); - QVERIFY(o); +} - QCOMPARE(o->property("delayed").toBool(), true); - QCOMPARE(o->property("gotten").toInt(), 5); +void tst_QmlCppCodegen::lotsOfRegisters() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/page.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + const auto compare = [&]() { + const qreal implicitBackgroundWidth = object->property("implicitBackgroundWidth").toDouble(); + const qreal leftInset = object->property("leftInset").toDouble(); + const qreal rightInset = object->property("rightInset").toDouble(); + const qreal contentWidth = object->property("contentWidth").toDouble(); + const qreal leftPadding = object->property("leftPadding").toDouble(); + const qreal rightPadding = object->property("rightPadding").toDouble(); + const qreal implicitFooterWidth = object->property("implicitFooterWidth").toDouble(); + const qreal implicitHeaderWidth = object->property("implicitHeaderWidth").toDouble(); + + const qreal implicitWidth = object->property("implicitWidth").toDouble(); + QCOMPARE(implicitWidth, qMax(qMax(implicitBackgroundWidth + leftInset + rightInset, + contentWidth + leftPadding + rightPadding), + qMax(implicitHeaderWidth, implicitFooterWidth))); + }; + + compare(); + + const QList<const char *> props = { + "leftInset", "rightInset", "contentWidth", "leftPadding", "rightPadding" + }; + + for (int i = 0; i < 100; ++i) { + QVERIFY(object->setProperty(props[i % props.size()], (i * 17) % 512)); + compare(); + } } -void tst_QmlCppCodegen::invisibleBase() +void tst_QmlCppCodegen::math() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleBase.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(qvariant_cast<QObject *>(o->property("n")), o.data()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/math.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("a").toInt(), 9); + QCOMPARE(object->property("b").toDouble(), 50.0 / 22.0); + QCOMPARE(object->property("c").toDouble(), std::atan(1.0) * 8.0); } -void tst_QmlCppCodegen::notEqualsInt() +void tst_QmlCppCodegen::mathMinMax() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notEqualsInt.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathMinMax.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + // Math.max() + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "2"); + QTest::ignoreMessage(QtDebugMsg, "2"); + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "2"); + QTest::ignoreMessage(QtDebugMsg, "2"); + QTest::ignoreMessage(QtDebugMsg, "9"); + + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "0.002"); + QTest::ignoreMessage(QtDebugMsg, "5.4"); + QTest::ignoreMessage(QtDebugMsg, "NaN"); + QTest::ignoreMessage(QtDebugMsg, "Infinity"); + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "0.08"); + QTest::ignoreMessage(QtDebugMsg, "Infinity"); + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "NaN"); + + // Math.min() + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "-2"); + QTest::ignoreMessage(QtDebugMsg, "-2"); + QTest::ignoreMessage(QtDebugMsg, "0"); + + QTest::ignoreMessage(QtDebugMsg, "0"); + QTest::ignoreMessage(QtDebugMsg, "-0.001"); + QTest::ignoreMessage(QtDebugMsg, "0.002"); + QTest::ignoreMessage(QtDebugMsg, "NaN"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + QTest::ignoreMessage(QtDebugMsg, "-1"); + QTest::ignoreMessage(QtDebugMsg, "-8"); + QTest::ignoreMessage(QtDebugMsg, "NaN"); + QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QObject *t = qmlContext(o.data())->objectForName(u"t"_s); - QVERIFY(t); - QCOMPARE(t->property("text").toString(), u"Foo"_s); - QMetaObject::invokeMethod(o.data(), "foo"); - QCOMPARE(t->property("text").toString(), u"Bar"_s); + QVERIFY(!o.isNull()); } -void tst_QmlCppCodegen::infinities() +void tst_QmlCppCodegen::mathOperations() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinities.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathOperations.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity()); - QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity()); + const QMetaObject *metaObject = o->metaObject(); - const double positiveZero = o->property("positiveZero").toDouble(); - QCOMPARE(positiveZero, 0.0); - QVERIFY(!std::signbit(positiveZero)); + char t1; + char t2; + QString name; + const auto guard = qScopeGuard([&]() { + if (QTest::currentTestFailed()) { + qDebug() << t1 << t2 << name << "failed on:"; + qDebug() << "doubles" << o->property("a").toDouble() << o->property("b").toDouble(); + qDebug() << "integers" << o->property("ia").toInt() << o->property("ib").toInt(); + qDebug() << "booleans" << o->property("ba").toBool() << o->property("bb").toBool(); + } + }); - const double negativeZero = o->property("negativeZero").toDouble(); - QCOMPARE(negativeZero, -0.0); - QVERIFY(std::signbit(negativeZero)); + for (double a : numbers) { + for (double b : numbers) { + o->setProperty("a", a); + o->setProperty("b", b); + for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) { + const QMetaProperty prop = metaObject->property(i); + const QByteArray propName = prop.name(); - QVERIFY(qIsNaN(o->property("naN").toDouble())); + if (propName.size() < 3 || propName == "objectName") + continue; + + t1 = propName[0]; + t2 = propName[1]; + name = QString::fromUtf8(propName.mid(2)); + + double expected; + + switch (t2) { + case 'd': + case '_': + switch (t1) { + case 'd': + expected = jsEval<double, double>(a, b, name, &engine); + break; + case 'i': + expected = jsEval<int, double>(a, b, name, &engine); + break; + case 'b': + expected = jsEval<bool, double>(a, b, name, &engine); + break; + } + break; + case 'i': + switch (t1) { + case 'd': + expected = jsEval<double, int>(a, b, name, &engine); + break; + case 'i': + expected = jsEval<int, int>(a, b, name, &engine); + break; + case 'b': + expected = jsEval<bool, int>(a, b, name, &engine); + break; + } + break; + case 'b': + switch (t1) { + case 'd': + expected = jsEval<double, bool>(a, b, name, &engine); + break; + case 'i': + expected = jsEval<int, bool>(a, b, name, &engine); + break; + case 'b': + expected = jsEval<bool, bool>(a, b, name, &engine); + break; + } + break; + } + + const double result = prop.read(o.data()).toDouble(); + QCOMPARE(result, expected); + } + } + } } -void tst_QmlCppCodegen::blockComments() +void tst_QmlCppCodegen::mathStaticProperties() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/blockComments.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(o->property("implicitHeight").toDouble(), 8.0); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathStaticProperties.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + // Approximate values based on + // https://262.ecma-international.org/14.0/#sec-value-properties-of-the-math-object + QCOMPARE(object->property("e").toDouble(), 2.7182818284590452354); + QCOMPARE(object->property("ln10").toDouble(), 2.302585092994046); + QCOMPARE(object->property("ln2").toDouble(), 0.6931471805599453); + QCOMPARE(object->property("log10e").toDouble(), 0.4342944819032518); + QCOMPARE(object->property("log2e").toDouble(), 1.4426950408889634); + QCOMPARE(object->property("pi").toDouble(), 3.1415926535897932); + QCOMPARE(object->property("sqrt1_2").toDouble(), 0.7071067811865476); + QCOMPARE(object->property("sqrt2").toDouble(), 1.4142135623730951); } -void tst_QmlCppCodegen::functionLookup() +void tst_QmlCppCodegen::mergedObjectReadWrite() +{ + QQmlEngine e; + { + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/mergedObjectRead.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtDebugMsg, "null"); + QTest::ignoreMessage( + QtWarningMsg, QRegularExpression("TypeError: Cannot read property 'x' of null")); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + } + + { + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/mergedObjectWrite.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression( + "TypeError: Value is null and could not be converted to an object")); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + } +} + +void tst_QmlCppCodegen::methodOnListLookup() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionLookup.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - const QVariant foo = o->property("bar"); - QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>()); - const QJSManagedValue method(engine.toScriptValue(foo), &engine); - QVERIFY(method.isFunction()); - const QJSValue result = method.call(); - QVERIFY(result.isString()); - QCOMPARE(result.toString(), QStringLiteral("a99")); + const QUrl url(u"qrc:/qt/qml/TestTypes/methodOnListLookup.qml"_s); + QQmlComponent component(&engine, url); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->objectName(), u"no one"); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + + ":14: TypeError: Cannot call method 'getName' of undefined"_L1)); + QMetaObject::invokeMethod(o.data(), "boom"); } -void tst_QmlCppCodegen::objectInVar() +void tst_QmlCppCodegen::methods() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectInVar.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); - QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/methods.qml"_s)); + QVERIFY(component.isReady()); - bool result = false; - QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result))); - QVERIFY(result); + QTest::ignoreMessage(QtDebugMsg, "The Bar"); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_s)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); + BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data())); - o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr)); - QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result))); - QVERIFY(!result); + QVERIFY(party && party->host()); + QCOMPARE(party->guestCount(), 5); + + bool foundGreen = false; + bool foundFoo = false; + for (int ii = 0; ii < party->guestCount(); ++ii) { + if (party->guest(ii)->name() == u"William Green"_s) + foundGreen = true; + if (party->guest(ii)->name() == u"The Foo"_s) + foundFoo = true; + } + + QVERIFY(foundGreen); + QVERIFY(foundFoo); + + QCOMPARE(obj->property("n1").toString(), u"onGurk"_s); + QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_s); + QCOMPARE(obj->property("n3"), QVariant()); + + { + QVariant ret; + obj->metaObject()->invokeMethod(obj.data(), "retrieveVar", Q_RETURN_ARG(QVariant, ret)); + QCOMPARE(ret.typeId(), QMetaType::QString); + QCOMPARE(ret.toString(), u"Jack Smith"_s); + } + + { + QString ret; + obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret)); + QCOMPARE(ret, u"Jack Smith"_s); + } + + QCOMPARE(party->host()->shoeSize(), 12); + obj->metaObject()->invokeMethod(obj.data(), "storeElement"); + QCOMPARE(party->host()->shoeSize(), 13); + QJSManagedValue v = engine.toManagedValue(obj->property("dresses")); + QVERIFY(v.isArray()); + + QJSManagedValue inner(v.property(2), &engine); + QVERIFY(inner.isArray()); + QCOMPARE(inner.property(0).toInt(), 1); + QCOMPARE(inner.property(1).toInt(), 2); + QCOMPARE(inner.property(2).toInt(), 3); + + QCOMPARE(obj->property("enumValue").toInt(), 2); } -void tst_QmlCppCodegen::functionTakingVar() +void tst_QmlCppCodegen::modulePrefix() { QQmlEngine engine; - const QUrl document(u"qrc:/qt/qml/TestTypes/functionTakingVar.qml"_s); - QQmlComponent c(&engine, document); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/modulePrefix.qml"_s)); - QVERIFY(!o->property("c").isValid()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); - int value = 11; - QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine); - void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) }; - QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() }; - e->executeRuntimeFunction(document, 0, o.data(), 1, args, types); + QCOMPARE(rootObject->property("foo").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); + QCOMPARE(rootObject->property("bar").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime())); + QCOMPARE(rootObject->property("baz").toString(), QStringLiteral("ItIsTheSingleton")); +} - QCOMPARE(o->property("c"), QVariant::fromValue<int>(11)); +void tst_QmlCppCodegen::multiDirectory_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addRow("from qt_add_qml_module") + << QUrl(u"qrc:/qt/qml/TestTypes/extra/extra.qml"_s); +#ifndef VERY_OLD_CMAKE + QTest::addRow("from qt_target_qml_sources") + << QUrl(u"qrc:/qt/qml/TestTypes/extra2/extra.qml"_s); +#endif } -void tst_QmlCppCodegen::testIsnan() +void tst_QmlCppCodegen::multiDirectory() { + QFETCH(QUrl, url); QQmlEngine engine; - const QUrl document(u"qrc:/qt/qml/TestTypes/isnan.qml"_s); - QQmlComponent c(&engine, document); + QQmlComponent component(&engine, url); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); + + QCOMPARE(rootObject->property("r").value<QRectF>(), QRectF(4, 6, 8, 10)); +} + +void tst_QmlCppCodegen::multiForeign() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiforeign.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), u"not here and not there"_s); +} - QCOMPARE(o->property("good").toDouble(), 10.1); - QVERIFY(qIsNaN(o->property("bad").toDouble())); +void tst_QmlCppCodegen::multiLookup() +{ + // Multiple lookups of singletons (Qt in this case) don't clash with one another. + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/immediateQuit.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); - const QVariant a = o->property("a"); - QCOMPARE(a.metaType(), QMetaType::fromType<bool>()); - QVERIFY(!a.toBool()); + const QByteArray message = QByteArray("End: ") + arg1(); + QTest::ignoreMessage(QtDebugMsg, message.constData()); - const QVariant b = o->property("b"); - QCOMPARE(b.metaType(), QMetaType::fromType<bool>()); - QVERIFY(b.toBool()); + QSignalSpy quitSpy(&engine, &QQmlEngine::quit); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(quitSpy.size(), 1); } -void tst_QmlCppCodegen::fallbackLookups() +void tst_QmlCppCodegen::multipleCtors() { QQmlEngine engine; - const QUrl document(u"qrc:/qt/qml/TestTypes/fallbacklookups.qml"_s); - QQmlComponent c(&engine, document); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multipleCtors.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("wr").value<ValueTypeWithLength>().length(), 3); + QCOMPARE(o->property("wp").value<ValueTypeWithLength>().length(), 11); + QCOMPARE(o->property("wi").value<ValueTypeWithLength>().length(), 17); +} - QCOMPARE(o->objectName(), QString()); - int result = 0; +void tst_QmlCppCodegen::namespaceWithEnum() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/namespaceWithEnum.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("i").toInt(), 2); +} - QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result)); - QCOMPARE(result, 16); - QCOMPARE(o->objectName(), QStringLiteral("aa93")); +void tst_QmlCppCodegen::noQQmlData() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noQQmlData.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result)); - QCOMPARE(result, 17); - QCOMPARE(o->objectName(), QStringLiteral("bb94")); + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: " + "Cannot read property 'name' of null"); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); - QObject *singleton = nullptr; - QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton)); - QVERIFY(singleton); + BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data()); + QVERIFY(party != nullptr); - QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result)); - QCOMPARE(result, 18); - QCOMPARE(singleton->objectName(), QStringLiteral("cc95")); + QCOMPARE(party->host(), nullptr); + QCOMPARE(party->property("n").toString(), QString()); - QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result)); - QCOMPARE(result, 19); - QCOMPARE(singleton->objectName(), QStringLiteral("dd96")); + Person *host1 = new Person(party); + party->setHost(host1); + QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s); + host1->setName(u"Marge"_s); + QCOMPARE(party->property("n").toString(), u"Marge in da house!"_s); + + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: " + "Cannot read property 'name' of null"); + + // Doesn't crash + party->setHost(nullptr); + + // Lookups are initialized now, and we introduce an object without QQmlData + Person *host2 = new Person(party); + party->setHost(host2); + QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s); + host2->setName(u"Homer"_s); + QCOMPARE(party->property("n").toString(), u"Homer in da house!"_s); + + QMetaObject::invokeMethod(party, "burn"); + engine.collectGarbage(); + + // Does not crash + party->setProperty("inDaHouse", u" burns!"_s); + + // Mr Burns may or may not burn, depending on whether we use lookups. + // If using lookups, the binding is aborted when we find the isQueuedForDeletion flag. + // If reading the property directly, we don't have to care about it. + QVERIFY(party->property("n").toString().startsWith(u"Mr Burns"_s)); } -void tst_QmlCppCodegen::typedArray() +void tst_QmlCppCodegen::nonNotifyable() { QQmlEngine engine; - const QUrl document(u"qrc:/qt/qml/TestTypes/typedArray.qml"_s); - QQmlComponent c(&engine, document); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nonNotifyable.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(qvariant_cast<QDateTime>(object->property("dayz")), + QDateTime(QDate(2121, 1, 12), QTime())); + QCOMPARE(qvariant_cast<QDateTime>(object->property("oParty")), + QDateTime(QDate(2111, 12, 11), QTime())); +} + +void tst_QmlCppCodegen::notEqualsInt() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notEqualsInt.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(o); - QDateTime date; - QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty()); - QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")), - QList<int>({1, 2, 3, 4})); - QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")), - QList<QDateTime>({date, date, date})); - QCOMPARE(qvariant_cast<QList<double>>(o->property("values5")), - QList<double>({1, 2, 3.4, 30, 0, 0})); - date = QDateTime::currentDateTime(); - o->setProperty("aDate", date); - QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")), - QList<QDateTime>({date, date, date})); + QObject *t = qmlContext(o.data())->objectForName(u"t"_s); + QVERIFY(t); + QCOMPARE(t->property("text").toString(), u"Foo"_s); + QMetaObject::invokeMethod(o.data(), "foo"); + QCOMPARE(t->property("text").toString(), u"Bar"_s); +} - QQmlListProperty<QObject> values6 - = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6")); - QCOMPARE(values6.count(&values6), 3); - for (int i = 0; i < 3; ++i) - QCOMPARE(values6.at(&values6, i), o.data()); +void tst_QmlCppCodegen::notNotString() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notNotString.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - QCOMPARE(o->property("inIntList").toInt(), 2); - QCOMPARE(qvariant_cast<QDateTime>(o->property("inDateList")), date); - QCOMPARE(o->property("inRealList").toDouble(), 30.0); - QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f")); + QCOMPARE(o->property("notNotString").value<bool>(), false); + o->setObjectName(u"a"_s); + QCOMPARE(o->property("notNotString").value<bool>(), true); +} - const QMetaObject *metaObject = o->metaObject(); - QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)")); - QVERIFY(method.isValid()); +void tst_QmlCppCodegen::nullAccess() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccess.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); - // If LoadElement threw an exception the function would certainly return neither 10 nor 20. - int result = 0; - method.invoke( - o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a"))); - QCOMPARE(result, 10); - method.invoke( - o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa"))); - QCOMPARE(result, 20); + QTest::ignoreMessage(QtWarningMsg, + "qrc:/qt/qml/TestTypes/nullAccess.qml:4:5: TypeError: " + "Cannot read property 'width' of null"); + QTest::ignoreMessage(QtWarningMsg, + "qrc:/qt/qml/TestTypes/nullAccess.qml:5:5: TypeError: " + "Cannot read property 'height' of null"); + QTest::ignoreMessage(QtWarningMsg, + "qrc:/qt/qml/TestTypes/nullAccess.qml:6: TypeError: Value is null and " + "could not be converted to an object"); + QScopedPointer<QObject> object(component.create()); + + QCOMPARE(object->property("width").toDouble(), 0.0); + QCOMPARE(object->property("height").toDouble(), 0.0); } -void tst_QmlCppCodegen::prefixedType() +void tst_QmlCppCodegen::nullAccessInsideSignalHandler() { QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccessInsideSignalHandler.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QTest::ignoreMessage(QtWarningMsg, + "qrc:/qt/qml/TestTypes/nullAccessInsideSignalHandler.qml:15: ReferenceError: " + "text is not defined"); + QScopedPointer<QObject> object(component.create()); + QSignalSpy spy(object.data(), SIGNAL(say_hello())); + QTRY_VERIFY(spy.size() > 0); +} - // We need to add an import path here because we cannot namespace the implicit import. - // The implicit import is what we use for all the other tests, even if we explicitly - // import TestTypes. That is because the TestTypes module is in a subdirectory "data". - engine.addImportPath(u":/"_s); - const QUrl document(u"qrc:/qt/qml/TestTypes/prefixedMetaType.qml"_s); - QQmlComponent c(&engine, document); +void tst_QmlCppCodegen::nullComparison() +{ + QQmlEngine engine; + + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullComparison.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + QVERIFY(!o.isNull()); - QCOMPARE(o->property("state").toInt(), 2); - QVERIFY(qvariant_cast<QObject *>(o->property("a")) != nullptr); - QVERIFY(qvariant_cast<QObject *>(o->property("b")) != nullptr); - QVERIFY(qvariant_cast<QObject *>(o->property("c")) == nullptr); + QCOMPARE(o->property("v").toInt(), 1); + QCOMPARE(o->property("w").toInt(), 3); + QCOMPARE(o->property("x").toInt(), 1); + QCOMPARE(o->property("y").toInt(), 5); + QCOMPARE(o->property("z").toInt(), 18); +} - QVERIFY(qvariant_cast<QObject *>(o->property("d")) != nullptr); - QVERIFY(qvariant_cast<QObject *>(o->property("e")) != nullptr); - QVERIFY(qvariant_cast<QObject *>(o->property("f")) == nullptr); +void tst_QmlCppCodegen::nullishCoalescing_data() +{ + QTest::addColumn<QString>("propertyName"); + QTest::addColumn<QVariant>("expected"); - QVERIFY(qvariant_cast<QObject *>(o->property("g")) != nullptr); - QVERIFY(qvariant_cast<QObject *>(o->property("h")) != nullptr); + const auto undefinedValue = QVariant(); + const auto nullValue = QVariant::fromMetaType(QMetaType::fromType<std::nullptr_t>(), nullptr); - QCOMPARE(o->property("countG").toInt(), 11); - QCOMPARE(o->property("countH").toInt(), 11); -} + QTest::addRow("trivial-good-int") << "p1" << QVariant(5); + QTest::addRow("trivial-good-string") << "p2" << QVariant("6"); -void tst_QmlCppCodegen::evadingAmbiguity() -{ - QQmlEngine engine; + QTest::addRow("trivial-bad-undefined-undefined") << "p3" << undefinedValue; + QTest::addRow("trivial-bad-undefined-null") << "p4" << nullValue; + QTest::addRow("trivial-bad-undefined-int") << "p5" << QVariant(-1); + QTest::addRow("trivial-bad-undefined-string") << "p6" << QVariant("-1"); - // We need to add an import path here because we cannot namespace the implicit import. - // The implicit import is what we use for all the other tests, even if we explicitly - // import TestTypes. That is because the TestTypes module is in a subdirectory "data". - engine.addImportPath(u":/"_s); + QTest::addRow("trivial-bad-null-undefined") << "p7" << undefinedValue; + QTest::addRow("trivial-bad-null-null") << "p8" << nullValue; + QTest::addRow("trivial-bad-null-int") << "p9" << QVariant(-1); + QTest::addRow("trivial-bad-null-string") << "p10" << QVariant("-1"); - QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous1/Ambiguous.qml"_s)); - QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - QScopedPointer<QObject> o1(c1.create()); - QCOMPARE(o1->objectName(), QStringLiteral("Ambiguous")); - QCOMPARE(o1->property("i").toString(), QStringLiteral("Ambiguous1")); + QTest::addRow("enum1") << "p11" << QVariant(1); - QQmlComponent c2(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous2/Ambiguous.qml"_s)); - QVERIFY2(c2.isReady(), qPrintable(c2.errorString())); - QScopedPointer<QObject> o2(c2.create()); - QCOMPARE(o2->objectName(), QStringLiteral("Ambiguous")); - QCOMPARE(o2->property("i").toString(), QStringLiteral("Ambiguous2")); + QTest::addRow("multiple ?? int") << "p12" << QVariant(1); + QTest::addRow("multiple ?? string") << "p13" << QVariant("1"); + QTest::addRow("multiple ?? mixed2") << "p14" << QVariant("2"); + QTest::addRow("multiple ?? mixed3") << "p15" << QVariant(1); + + QTest::addRow("optional + nullish bad") << "p16" << QVariant(-1); + QTest::addRow("optional + nullish good") << "p17" << QVariant(5); } -void tst_QmlCppCodegen::fromBoolValue() +void tst_QmlCppCodegen::nullishCoalescing() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fromBoolValue.qml"_s)); + const QUrl document(u"qrc:/qt/qml/TestTypes/nullishCoalescing.qml"_s); + QQmlComponent c(&engine, document); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QCOMPARE(o->property("a").toBool(), true); - o->setProperty("x", 100); - QCOMPARE(o->property("a").toBool(), false); - - QCOMPARE(o->property("width").toInt(), 100); - QCOMPARE(o->property("b").toBool(), false); + QVERIFY(o); - QScopedPointer<QObject> parent(c.create()); - o->setProperty("parent", QVariant::fromValue(parent.data())); - QCOMPARE(o->property("width").toInt(), 100); - QCOMPARE(o->property("b").toBool(), false); + QFETCH(QString, propertyName); + QFETCH(QVariant, expected); - o->setProperty("state", QVariant::fromValue(u"foo"_s)); - QCOMPARE(o->property("width").toInt(), 0); - QCOMPARE(o->property("b").toBool(), false); + QVariant actual = o->property(propertyName.toLocal8Bit()); + QCOMPARE(actual, expected); } -void tst_QmlCppCodegen::invisibleTypes() +void tst_QmlCppCodegen::numbersInJsPrimitive() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleTypes.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/numbersInJsPrimitive.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QObject *singleton = qvariant_cast<QObject *>(o->property("singleton")); - QVERIFY(singleton != nullptr); - QCOMPARE(singleton->metaObject()->className(), "SingletonModel"); + const QList<qint64> zeroes + = {0, 0, 0, 0, 0, 0, 0, 0}; + const QList<qint64> written + = {35, 36, 37, 38, 39, 40, 41, 42}; + const QList<qint64> writtenNegative + = {-35, 220, -37, 65498, -39, 4294967256, -41, 4294967254}; + const QList<QList<qint64>> writtenShuffled = { + { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 }, + { -37, 218, -39, 65496, -41, 4294967254, -36, 4294967259 }, + { -38, 217, -40, 65495, -42, 4294967260, -37, 4294967258 }, + { -39, 216, -41, 65494, -36, 4294967259, -38, 4294967257 }, + { -40, 215, -42, 65500, -37, 4294967258, -39, 4294967256 }, + { -41, 214, -36, 65499, -38, 4294967257, -40, 4294967255 }, + { -42, 220, -37, 65498, -39, 4294967256, -41, 4294967254 }, + { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 }, + }; - QObject *attached = qvariant_cast<QObject *>(o->property("attached")); - QVERIFY(attached != nullptr); - QCOMPARE(attached->metaObject()->className(), "AttachedAttached"); + const QList<qint64> stored + = {50, 51, 1332, 1333, 1334, 1335, 1336, 1337}; + const QList<qint64> storedNegative + = {-50, 205, -1332, 64203, -1334, 4294965961, -1336, 4294965959}; + const QList<QList<qint64>> storedShuffled = { + { -51, 204, -1333, 64202, -1335, 4294965960, -1337, 4294967245 }, + { -52, 203, -1334, 64201, -1336, 4294965959, -51, 4294967244 }, + { -53, 202, -1335, 64200, -1337, 4294967245, -52, 4294967243 }, + { -54, 201, -1336, 64199, -51, 4294967244, -53, 4294967242 }, + { -55, 200, -1337, 65485, -52, 4294967243, -54, 4294967241 }, + { -56, 199, -51, 65484, -53, 4294967242, -55, 4294967240 }, + { -57, 205, -52, 65483, -54, 4294967241, -56, 4294967239 }, + { -51, 204, -53, 65482, -55, 4294967240, -57, 4294967245 }, + }; -// TODO: This doesn't work in interpreted mode: -// const QMetaObject *meta = qvariant_cast<const QMetaObject *>(o->property("metaobject")); -// QVERIFY(meta != nullptr); -// QCOMPARE(meta->className(), "DerivedFromInvisible"); -} + QStringList asStrings(8); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(zeroes)); -class MyCppType : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool useListDelegate - READ useListDelegate - WRITE setUseListDelegate - NOTIFY useListDelegateChanged) -public: - explicit MyCppType(QObject * parent = nullptr) : QObject(parent) {} + QMetaObject::invokeMethod(o.data(), "writeValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(written)); - bool useListDelegate() const { return m_useListDelegate; } - void setUseListDelegate(bool useListDelegate) - { - if (useListDelegate != m_useListDelegate) { - m_useListDelegate = useListDelegate; - emit useListDelegateChanged(); + QMetaObject::invokeMethod(o.data(), "negateValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(writtenNegative)); + + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod(o.data(), "shuffleValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); } + QCOMPARE(asStrings, convertToStrings(writtenShuffled[i])); } -signals: - void useListDelegateChanged(); + QMetaObject::invokeMethod(o.data(), "storeValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(stored)); -private: - bool m_useListDelegate = false; -}; + QMetaObject::invokeMethod(o.data(), "negateValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(storedNegative)); + + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod(o.data(), "shuffleValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(storedShuffled[i])); + } +} -void tst_QmlCppCodegen::invalidPropertyType() +void tst_QmlCppCodegen::objectInVar() { - // Invisible on purpose - qmlRegisterType<MyCppType>("App", 1, 0, "MyCppType"); + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectInVar.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data()); + + bool result = false; + QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result))); + QVERIFY(result); + o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr)); + QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result))); + QVERIFY(!result); +} + +void tst_QmlCppCodegen::objectLookupOnListElement() +{ QQmlEngine engine; - QQmlComponent okComponent(&engine, QUrl(u"qrc:/qt/qml/TestTypes/OkType.qml"_s)); - QVERIFY2(okComponent.isReady(), qPrintable(okComponent.errorString())); - QScopedPointer<QObject> picker(okComponent.create()); - QVERIFY2(!picker.isNull(), qPrintable(okComponent.errorString())); - QObject *inner = qmlContext(picker.data())->objectForName(u"inner"_s); - QVERIFY(inner); - MyCppType *myCppType = qobject_cast<MyCppType *>(inner); - QVERIFY(myCppType); - QVERIFY(!myCppType->useListDelegate()); - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BadType.qml"_s)); + const QUrl url(u"qrc:/qt/qml/TestTypes/objectLookupOnListElement.qml"_s); + QQmlComponent c1(&engine, url); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); + + QScopedPointer<QObject> object(c1.create()); + QVERIFY(!object.isNull()); + + QList<int> zOrders; + QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders)); + QCOMPARE(zOrders, (QList<int>{1, 0, 0})); + object->setProperty("current", 1); + QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders)); + QCOMPARE(zOrders, (QList<int>{0, 1, 0})); + + QMetaObject::invokeMethod(object.data(), "clearChildren"); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + + u":21: TypeError: Cannot read property 'z' of undefined"_s)); + QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders)); + QCOMPARE(zOrders, (QList<int>())); +} + +void tst_QmlCppCodegen::objectToString() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/toString.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.createWithInitialProperties( - QVariantMap {{u"picker"_s, QVariant::fromValue(picker.data())}})); - QVERIFY2(!o.isNull(), qPrintable(c.errorString())); - QVERIFY(!myCppType->useListDelegate()); - o->setProperty("useListDelegate", QVariant::fromValue<bool>(true)); - QVERIFY(myCppType->useListDelegate()); + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/toString.qml:6: no"); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("yes").toString(), u"yes yes"_s); + QCOMPARE(o->property("no").toString(), u" no"_s); // throws, but that is ignored } -void tst_QmlCppCodegen::valueTypeLists() +void tst_QmlCppCodegen::objectWithStringListMethod() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeLists.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectWithStringListMethod.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtDebugMsg, "2"); QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} - QCOMPARE(qvariant_cast<QRectF>(o->property("rectInBounds")), QRectF(1, 2, 3, 4)); - QVERIFY(o->metaObject()->indexOfProperty("rectOutOfBounds") > 0); - QVERIFY(!o->property("rectOutOfBounds").isValid()); +void tst_QmlCppCodegen::onAssignment() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/pressAndHoldButton.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); - QCOMPARE(qvariant_cast<QString>(o->property("stringInBounds")), QStringLiteral("bbb")); - QVERIFY(o->metaObject()->indexOfProperty("stringOutOfBounds") > 0); - QVERIFY(!o->property("stringOutOfBounds").isValid()); + QCOMPARE(object->property("pressed").toBool(), false); + QCOMPARE(object->property("scale").toDouble(), 1.0); - QCOMPARE(qvariant_cast<int>(o->property("intInBounds")), 7); - QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0); - QVERIFY(!o->property("intOutOfBounds").isValid()); + object->metaObject()->invokeMethod(object.data(), "press"); + QTRY_COMPARE(object->property("pressed").toBool(), true); + QCOMPARE(object->property("scale").toDouble(), 0.9); - QCOMPARE(qvariant_cast<QString>(o->property("charInBounds")), QStringLiteral("d")); - QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0); - QVERIFY(!o->property("charOutOfBounds").isValid()); + object->metaObject()->invokeMethod(object.data(), "release"); + QCOMPARE(object->property("pressed").toBool(), false); + QCOMPARE(object->property("scale").toDouble(), 1.0); } -void tst_QmlCppCodegen::boundComponents() +void tst_QmlCppCodegen::optionalComparison() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/boundComponents.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/optionalComparison.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - QObject *c1o = o->property("o").value<QObject *>(); - QVERIFY(c1o != nullptr); - QCOMPARE(c1o->objectName(), u"bar"_s); + QCOMPARE(object->property("found").toInt(), 1); + QCOMPARE(object->property("foundStrict").toInt(), 1); + QCOMPARE(object->property("foundNot").toInt(), 2); + QCOMPARE(object->property("foundStrictNot").toInt(), 2); - QObject *c2o = c1o->property("o").value<QObject *>(); - QVERIFY(c2o != nullptr); - QCOMPARE(c2o->objectName(), u"bar12"_s); + // this === this, null === null (x4), undefined === undefined + QCOMPARE(object->property("undefinedEqualsUndefined").toInt(), 6); + + QCOMPARE(object->property("optionalNull").toBool(), true); + object->setObjectName("foo"_L1); + QCOMPARE(object->property("optionalNull").toBool(), false); } -class InvisibleListElementType : public QObject +void tst_QmlCppCodegen::outOfBoundsArray() { - Q_OBJECT -public: - InvisibleListElementType(QObject *parent = nullptr) : QObject(parent) {} -}; + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/outOfBounds.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); -void tst_QmlCppCodegen::invisibleListElementType() + QTest::ignoreMessage(QtDebugMsg, "oob undefined"); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QVERIFY(object->metaObject()->indexOfProperty("oob") > 0); + QVERIFY(!object->property("oob").isValid()); + const QVariant oob2 = object->property("oob2"); + QCOMPARE(oob2.metaType(), QMetaType::fromType<QObject *>()); + QCOMPARE(oob2.value<QObject *>(), nullptr); +} + +void tst_QmlCppCodegen::overriddenProperty() { - qmlRegisterType<InvisibleListElementType>("Invisible", 1, 0, "InvisibleListElement"); QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleListElementType.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/childobject.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - QObject *a = o->property("a").value<QObject *>(); - QVERIFY(a); + QObject *child = object->property("child").value<QObject *>(); + QVERIFY(child); - const QVariant x = a->property("x"); - QCOMPARE(x.metaType(), QMetaType::fromType<QQmlListReference>()); - const QQmlListReference ref = x.value<QQmlListReference>(); - QVERIFY(ref.isValid()); - QCOMPARE(ref.size(), 0); + QCOMPARE(object->objectName(), u"kraut"_s); + QCOMPARE(object->property("doneThing").toInt(), 5); + QCOMPARE(object->property("usingFinal").toInt(), 5); + + auto checkAssignment = [&]() { + const QString newName = u"worscht"_s; + QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName)); + QCOMPARE(object->objectName(), newName); + }; + checkAssignment(); + + QMetaObject::invokeMethod(child, "doString"); + QCOMPARE(child->objectName(), u"string"_s); + QMetaObject::invokeMethod(child, "doNumber"); + QCOMPARE(child->objectName(), u"double"_s); + QMetaObject::invokeMethod(child, "doArray"); + QCOMPARE(child->objectName(), u"javaScript"_s); + + QMetaObject::invokeMethod(child, "doString2"); + QCOMPARE(child->objectName(), u"string"_s); + QMetaObject::invokeMethod(child, "doNumber2"); + QCOMPARE(child->objectName(), u"double"_s); + QMetaObject::invokeMethod(child, "doArray2"); + QCOMPARE(child->objectName(), u"javaScript"_s); + + QMetaObject::invokeMethod(child, "doFoo"); + QCOMPARE(child->objectName(), u"ObjectWithMethod"_s); + + ObjectWithMethod *benign = new ObjectWithMethod(object.data()); + benign->theThing = 10; + benign->setObjectName(u"cabbage"_s); + object->setProperty("child", QVariant::fromValue(benign)); + QCOMPARE(object->objectName(), u"cabbage"_s); + checkAssignment(); + QCOMPARE(object->property("doneThing").toInt(), 10); + QCOMPARE(object->property("usingFinal").toInt(), 10); + + OverriddenObjectName *evil = new OverriddenObjectName(object.data()); + QTest::ignoreMessage(QtWarningMsg, + "Final member fff is overridden in class OverriddenObjectName. " + "The override won't be used."); + object->setProperty("child", QVariant::fromValue(evil)); + + QCOMPARE(object->objectName(), u"borschtsch"_s); + + checkAssignment(); + QCOMPARE(object->property("doneThing").toInt(), 7); + QCOMPARE(object->property("usingFinal").toInt(), 5); } -void tst_QmlCppCodegen::typePropertyClash() +void tst_QmlCppCodegen::ownPropertiesNonShadowable() { QQmlEngine engine; - engine.rootContext()->setContextProperty(u"size"_s, 5); - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropertyClash.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QCOMPARE(o->objectName(), u"Size: 5"_s); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/overriddenMember.qml"_s)); + + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); + + QCOMPARE(rootObject->property("ppp").toInt(), 16); + QCOMPARE(rootObject->property("ppp2").toInt(), 9); + QCOMPARE(rootObject->property("ppp3").toInt(), 12); } -void tst_QmlCppCodegen::objectToString() +void tst_QmlCppCodegen::parentProperty() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/toString.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/parentProp.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("c").toInt(), 11); + QCOMPARE(object->property("i").toInt(), 22); + object->setProperty("a", QVariant::fromValue(22)); + QCOMPARE(object->property("c").toInt(), 28); + object->setProperty("implicitWidth", QVariant::fromValue(14)); + QCOMPARE(object->property("i").toInt(), 26); - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/toString.qml:6: no"); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); + QObject *child = qmlContext(object.data())->objectForName(u"child"_s); + QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_s); + QObject *evil = qmlContext(object.data())->objectForName(u"evil"_s); - QCOMPARE(o->property("yes").toString(), u"yes yes"_s); - QCOMPARE(o->property("no").toString(), u" no"_s); // throws, but that is ignored + child->setProperty("parent", QVariant::fromValue(sibling)); + + QCOMPARE(child->property("b").toInt(), 0); + QCOMPARE(child->property("i").toInt(), 28); + QCOMPARE(object->property("i").toInt(), 56); + + child->setProperty("parent", QVariant::fromValue(evil)); + + QCOMPARE(child->property("b").toInt(), 5994); + QCOMPARE(object->property("c").toInt(), 5996); + + QCOMPARE(child->property("i").toInt(), 443); + QCOMPARE(object->property("i").toInt(), 886); + + { + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/specificParent.qml"_s)); + + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); + + QCOMPARE(rootObject->property("a").toReal(), 77.0); + } } -void tst_QmlCppCodegen::throwObjectName() +void tst_QmlCppCodegen::popContextAfterRet() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/throwObjectName.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/popContextAfterRet.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - - QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/throwObjectName.qml:5:5: ouch"); QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QVERIFY(o->objectName().isEmpty()); + QVERIFY(o); + + QCOMPARE(o->objectName(), QString()); + o->setProperty("stackViewDepth", 1); + QCOMPARE(o->objectName(), u"backgroundImage"_s); + o->setProperty("stackViewDepth", 2); + QCOMPARE(o->objectName(), u"backgroundBlur"_s); + o->setProperty("stackViewDepth", 1); + QCOMPARE(o->objectName(), u"backgroundImage"_s); } -void tst_QmlCppCodegen::javaScriptArgument() +void tst_QmlCppCodegen::prefixedType() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/javaScriptArgument.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); + // We need to add an import path here because we cannot namespace the implicit import. + // The implicit import is what we use for all the other tests, even if we explicitly + // import TestTypes. That is because the TestTypes module is in a subdirectory "data". + engine.addImportPath(u":/"_s); + + const QUrl document(u"qrc:/qt/qml/TestTypes/prefixedMetaType.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); + QVERIFY(o); - QCOMPARE(o->property("a").toDouble(), 4.0); - QCOMPARE(o->property("b").toDouble(), 9.0); - QCOMPARE(o->property("c").toString(), u"5t-1"_s); - QCOMPARE(o->property("d").toString(), u"9"_s); - QCOMPARE(o->property("e").toString(), u"10"_s); - QCOMPARE(o->property("f").toString(), u"-10"_s); + QCOMPARE(o->property("state").toInt(), 2); + QVERIFY(qvariant_cast<QObject *>(o->property("a")) != nullptr); + QVERIFY(qvariant_cast<QObject *>(o->property("b")) != nullptr); + QVERIFY(qvariant_cast<QObject *>(o->property("c")) == nullptr); - const QStringList scales { - "0 ", "1 ", "10 ", "100 ", "1000 ", "9.77k", "97.7k", "977k", "9.54M", "95.4M", "954M", - "9.31G", "93.1G", "931G", "9.09T", "-1 ", "-10 ", "-100 ", "-1000 ", "-9.77k", "-97.7k", - "-977k", "-9.54M", "-95.4M", "-954M", "-9.31G", "-93.1G", "-931G", "-9.09T" - }; + QVERIFY(qvariant_cast<QObject *>(o->property("d")) != nullptr); + QVERIFY(qvariant_cast<QObject *>(o->property("e")) != nullptr); + QVERIFY(qvariant_cast<QObject *>(o->property("f")) == nullptr); - QCOMPARE(o->property("scales").value<QStringList>(), scales); + QVERIFY(qvariant_cast<QObject *>(o->property("g")) != nullptr); + QVERIFY(qvariant_cast<QObject *>(o->property("h")) != nullptr); - double thing = 12.0; - QString result; - QMetaObject::invokeMethod( - o.data(), "forwardArg", Q_RETURN_ARG(QString, result), Q_ARG(double, thing)); - QCOMPARE(result, u"12 "); + QCOMPARE(o->property("countG").toInt(), 11); + QCOMPARE(o->property("countH").toInt(), 11); } -void tst_QmlCppCodegen::translation() +void tst_QmlCppCodegen::propertyOfParent() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/translation.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/RootWithoutId.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); - QCOMPARE(o->property("translate2"), u"s"_s); - QCOMPARE(o->property("translate3"), u"s"_s); - QCOMPARE(o->property("translate4"), u"s"_s); + QObject *child = qmlContext(object.data())->objectForName(u"item"_s); - QCOMPARE(o->property("translateNoop2"), u"s"_s); - QCOMPARE(o->property("translateNoop3"), u"s"_s); + bool expected = false; - QCOMPARE(o->property("tr1"), u"s"_s); - QCOMPARE(o->property("tr2"), u"s"_s); - QCOMPARE(o->property("tr3"), u"s"_s); + for (int i = 0; i < 3; ++i) { + const QVariant foo = object->property("foo"); + QCOMPARE(foo.metaType(), QMetaType::fromType<bool>()); + QCOMPARE(foo.toBool(), expected); - QCOMPARE(o->property("trNoop1"), u"s"_s); - QCOMPARE(o->property("trNoop2"), u"s"_s); + const QVariant bar = object->property("bar"); + QCOMPARE(bar.metaType(), QMetaType::fromType<bool>()); + QCOMPARE(bar.toBool(), expected); - QCOMPARE(o->property("trId1"), u"s"_s); - QCOMPARE(o->property("trId2"), u"s"_s); + const QVariant visible = child->property("visible"); + QCOMPARE(visible.metaType(), QMetaType::fromType<bool>()); + QCOMPARE(visible.toBool(), expected); - QCOMPARE(o->property("trIdNoop1"), u"s"_s); + expected = !expected; + object->setProperty("foo", expected); + } } -void tst_QmlCppCodegen::stringArg() +void tst_QmlCppCodegen::reduceWithNullThis() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringArg.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/reduceWithNullThis.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - QCOMPARE(o->property("stringArg"), u"a foozly thing"_s); - QCOMPARE(o->property("falseArg"), u"a 0 thing"_s); - QCOMPARE(o->property("trueArg"), u"a 1 thing"_s); - QCOMPARE(o->property("zeroArg"), u"a 0 thing"_s); - QCOMPARE(o->property("intArg"), u"a 11 thing"_s); - QCOMPARE(o->property("realArg"), u"a 12.25 thing"_s); + QCOMPARE(object->property("preferredHeight").toDouble(), 28.0); + QCOMPARE(object->property("preferredHeight2").toDouble(), 28.0); } -void tst_QmlCppCodegen::conversionDecrement() +void tst_QmlCppCodegen::readEnumFromInstance() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionDecrement.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); + const QString url = u"qrc:/qt/qml/TestTypes/readEnumFromInstance.qml"_s; - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); + QQmlComponent component(&engine, QUrl(url)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); - QCOMPARE(o->property("currentPageIndex").toInt(), 0); - o->setProperty("pages", 5); - QCOMPARE(o->property("currentPageIndex").toInt(), 3); - o->setProperty("pages", 4); - QCOMPARE(o->property("currentPageIndex").toInt(), 0); - o->setProperty("pages", 6); - QCOMPARE(o->property("currentPageIndex").toInt(), 4); - o->setProperty("pages", 60); - QCOMPARE(o->property("currentPageIndex").toInt(), 3); + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + ":7:5: Unable to assign [undefined] to int"_L1)); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("priority"), QVariant::fromValue<int>(0)); + QCOMPARE(object->property("prop2"), QVariant::fromValue<int>(1)); + QCOMPARE(object->property("priorityIsVeryHigh"), QVariant::fromValue<bool>(false)); + + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + ":13: Error: Cannot assign [undefined] to int"_L1)); + + int result = 0; + QMetaObject::invokeMethod(object.data(), "cyclePriority", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 0); } -void tst_QmlCppCodegen::unstoredUndefined() +void tst_QmlCppCodegen::readonlyListProperty() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unstoredUndefined.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QCOMPARE(o->objectName(), u"NaN"_s); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/readonlyListProperty.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("l").toInt(), 4); +} + +void tst_QmlCppCodegen::registerElimination() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerelimination.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + // Increment of 23 hits both 0 and 460 + for (int input = -23; input < 700; input += 23) { + object->setProperty("input", input); + if (input <= 0 || input >= 460) + QCOMPARE(object->property("output").toInt(), 459); + else + QCOMPARE(object->property("output").toInt(), input); + } } void tst_QmlCppCodegen::registerPropagation() @@ -2431,784 +4139,989 @@ void tst_QmlCppCodegen::registerPropagation() QCOMPARE(undefined, u"undefined"_s); } -void tst_QmlCppCodegen::argumentConversion() +void tst_QmlCppCodegen::renameAdjust() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/argumentConversion.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/renameAdjust.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - auto checkNaN = [&](const char *propName) { - const QVariant prop = o->property(propName); - QCOMPARE(prop.metaType(), QMetaType::fromType<double>()); - QVERIFY(qIsNaN(prop.toDouble())); - }; + QTest::ignoreMessage(QtDebugMsg, "success"); + QTest::ignoreMessage(QtCriticalMsg, "failed 10 11"); - checkNaN("a"); - checkNaN("b"); - checkNaN("e"); - - QCOMPARE(o->property("c").toDouble(), 3.0); - QCOMPARE(o->property("d").toDouble(), -1.0); - QCOMPARE(o->property("f").toDouble(), 10.0); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); } -void tst_QmlCppCodegen::badSequence() +void tst_QmlCppCodegen::resettableProperty() { + QFETCH(QString, url); + QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/badSequence.qml"_s)); + QQmlComponent c(&engine, QUrl(url)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - - Person *self = qobject_cast<Person *>(o.data()); - QVERIFY(self); - QVERIFY(self->barzles().isEmpty()); - QVERIFY(self->cousins().isEmpty()); - - Person *other = o->property("other").value<Person *>(); - QVERIFY(other); - QVERIFY(other->barzles().isEmpty()); - QVERIFY(other->cousins().isEmpty()); + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":10:5: Unable to assign [undefined] to double"_s)); - Barzle f1; - Barzle f2; - const QList<Barzle *> barzles { &f1, &f2 }; - const QList<Person *> cousins { self, other }; + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - other->setBarzles(barzles); - QCOMPARE(self->barzles(), barzles); - QCOMPARE(self->property("l").toInt(), 2); + QCOMPARE(o->property("value").toDouble(), 999); + QMetaObject::invokeMethod(o.data(), "doReset"); + QCOMPARE(o->property("value").toDouble(), 0); - other->setCousins(cousins); - QCOMPARE(self->cousins(), cousins); - QCOMPARE(self->property("m").toInt(), 2); + o->setProperty("value", double(82)); + QCOMPARE(o->property("value").toDouble(), 82); + QMetaObject::invokeMethod(o.data(), "doReset2"); + QCOMPARE(o->property("value").toDouble(), 0); - QQmlListProperty<Person> others - = self->property("others").value<QQmlListProperty<Person>>(); - QCOMPARE(others.count(&others), 2); - QCOMPARE(others.at(&others, 0), cousins[0]); - QCOMPARE(others.at(&others, 1), cousins[1]); + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":18: Error: Cannot assign [undefined] to double"_s)); + QCOMPARE(o->property("notResettable").toDouble(), 10); + QMetaObject::invokeMethod(o.data(), "doNotReset"); + QCOMPARE(o->property("notResettable").toDouble(), 10); + QCOMPARE(o->property("notResettable2").toDouble(), 0); // not NaN - QQmlListProperty<Person> momsCousins - = self->property("momsCousins").value<QQmlListProperty<Person>>(); - QCOMPARE(momsCousins.count(&momsCousins), 2); - QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]); - QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]); + o->setObjectName(u"namename"_s); + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":22: Error: Cannot assign [undefined] to QString"_s)); + QMetaObject::invokeMethod(o.data(), "aaa"); + QCOMPARE(o->objectName(), u"namename"_s); +} - QQmlListProperty<Person> dadsCousins - = self->property("dadsCousins").value<QQmlListProperty<Person>>(); - QCOMPARE(dadsCousins.count(&dadsCousins), 1); - QCOMPARE(dadsCousins.at(&dadsCousins, 0), other); +void tst_QmlCppCodegen::resettableProperty_data() +{ + QTest::addColumn<QString>("url"); + QTest::addRow("object lookups") << u"qrc:/qt/qml/TestTypes/resettable.qml"_s; + QTest::addRow("fallback lookups") << u"qrc:/qt/qml/TestTypes/fallbackresettable.qml"_s; } -void tst_QmlCppCodegen::enumLookup() +void tst_QmlCppCodegen::returnAfterReject() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumLookup.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/returnAfterReject.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - - QCOMPARE(o->property("ready").toBool(), true); + QVERIFY(o); + QCOMPARE(o->property("bar").toInt(), 123); } -void tst_QmlCppCodegen::trivialSignalHandler() +void tst_QmlCppCodegen::revisions() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/trivialSignalHandler.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/revisions.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(o->property("a").toString(), u"no"_s); - QCOMPARE(o->property("b").toInt(), -1); - QCOMPARE(o->property("b").toDouble(), -1.0); + QCOMPARE(o->property("delayed").toBool(), true); + QCOMPARE(o->property("gotten").toInt(), 5); +} - o->setObjectName(u"yes"_s); - QCOMPARE(o->property("a").toString(), u"yes"_s); - QCOMPARE(o->property("b").toInt(), 5); - QCOMPARE(o->property("c").toDouble(), 2.5); +void tst_QmlCppCodegen::scopeIdLookup() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeIdLookup.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("objectName").toString(), u"outer"_s); } -void tst_QmlCppCodegen::stringToByteArray() +void tst_QmlCppCodegen::scopeObjectDestruction() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringToByteArray.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fileDialog.qml"_s)); - Person *person = qobject_cast<Person *>(o.data()); - QVERIFY(person); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); - QCOMPARE(person->dataBindable().value(), QByteArray("some data")); - QCOMPARE(person->name(), u"some data"_s); + QObject *dialog = rootObject->property("dialog").value<QObject *>(); + QVERIFY(dialog); + + // We cannot check the warning messages. The AOT compiled code complains about reading the + // "parent" property of an object scheduled for deletion. The runtime silently returns undefined + // at that point and then complains about not being able to read a property on undefined. + + // Doesn't crash, even though it triggers bindings on scope objects scheduled for deletion. + QMetaObject::invokeMethod(dialog, "open"); } -void tst_QmlCppCodegen::listPropertyAsModel() +void tst_QmlCppCodegen::scopeVsObject() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listPropertyAsModel.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - - QQmlListReference children(o.data(), "children"); - QCOMPARE(children.count(), 5); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeVsObject.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("objectName").toString(), u"foobar"_s); } -void tst_QmlCppCodegen::notNotString() +void tst_QmlCppCodegen::scopedEnum() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notNotString.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); + const QString url = u"qrc:/qt/qml/TestTypes/scopedEnum.qml"_s; + QQmlComponent component(&engine, QUrl(url)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); - QCOMPARE(o->property("notNotString").value<bool>(), false); - o->setObjectName(u"a"_s); - QCOMPARE(o->property("notNotString").value<bool>(), true); -} + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url + u":6:5: Unable to assign [undefined] to int"_s)); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url + u":8: TypeError: Cannot read property 'C' of undefined"_s)); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url + u":14: TypeError: Cannot read property 'C' of undefined"_s)); -template<typename T> -QString toOperand(double arg); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("good").toInt(), 27); + QCOMPARE(object->property("bad").toInt(), 0); + QCOMPARE(object->property("wrong").toInt(), 0); + QCOMPARE(object->property("right").toInt(), 7); -template<> -QString toOperand<double>(double arg) -{ - if (qIsNull(arg)) - return std::signbit(arg) ? QStringLiteral("(-0)") : QStringLiteral("(0)"); + QCOMPARE(object->property("notgood").toInt(), 26); + QCOMPARE(object->property("notbad").toInt(), 26); + QCOMPARE(object->property("notwrong").toInt(), 0); + QCOMPARE(object->property("notright").toInt(), 6); - return u'(' + QJSPrimitiveValue(arg).toString() + u')'; + QCOMPARE(object->property("passable").toInt(), 2); + QCOMPARE(object->property("wild").toInt(), 1); } -template<> -QString toOperand<int>(double arg) +void tst_QmlCppCodegen::sequenceToIterable() { - const int iArg = QJSPrimitiveValue(arg).toInteger(); - return u'(' + QJSPrimitiveValue(iArg).toString() + u')'; + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/sequenceToIterable.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("c").toInt(), 11); + + QQmlListReference children(object.data(), "children"); + QCOMPARE(children.count(), 11); + static const QRegularExpression name("Entry\\(0x[0-9a-f]+, \"Item ([0-9])\"\\): ([0-9])"); + for (int i = 0; i < 10; ++i) { + const auto match = name.match(children.at(i)->objectName()); + QVERIFY(match.hasMatch()); + QCOMPARE(match.captured(1), QString::number(i)); + } } -template<> -QString toOperand<bool>(double arg) +void tst_QmlCppCodegen::setLookupConversion() { - const bool bArg = QJSPrimitiveValue(arg).toBoolean(); - return u'(' + QJSPrimitiveValue(bArg).toString() + u')'; + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/setLookupConversion.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QVERIFY(o->objectName().isEmpty()); + QMetaObject::invokeMethod(o.data(), "t"); + QCOMPARE(o->objectName(), u"a"_s); + QCOMPARE(o->property("value").toInt(), 9); } -template<typename T1, typename T2> -double jsEval(double arg1, double arg2, const QString &op, QJSEngine *engine) +void tst_QmlCppCodegen::setLookupOriginalScope() { - auto evalBinary = [&](const QString &jsOp) { - return engine->evaluate(toOperand<T1>(arg1) + jsOp + toOperand<T2>(arg2)).toNumber(); - }; + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/setLookupOriginalScope.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - auto evalBinaryConst = [&](const QString &jsOp) { - return engine->evaluate(toOperand<T1>(arg1) + jsOp + u'9').toNumber(); - }; + QObject *v = o->property("variable").value<QObject *>(); + QCOMPARE(v->property("value").toInt(), 0); - auto evalUnary = [&](const QString &jsOp) { - return engine->evaluate(jsOp + toOperand<T1>(arg1)).toNumber(); - }; + QMetaObject::invokeMethod(o.data(), "trigger"); + QObject *edit = o->property("edit").value<QObject *>(); + QVERIFY(edit); + QCOMPARE(edit->property("myOutput").value<QObject *>(), v); + QCOMPARE(v->property("value").toInt(), 55); +} - auto evalInPlace = [&](const QString &jsOp) { - return engine->evaluate( - u"(function() {var a = "_s + toOperand<T1>(arg1)+ u"; return "_s - + jsOp + u"a;})()"_s).toNumber(); - }; +void tst_QmlCppCodegen::shadowedAsCasts() +{ + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedAsCasts.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(!obj.isNull()); + + QObject *shadowed1 = obj->property("shadowed1").value<QObject *>(); + QVERIFY(shadowed1); + QVERIFY(shadowed1->objectName().isEmpty()); + const QVariant name1 = shadowed1->property("objectName"); + QCOMPARE(name1.metaType(), QMetaType::fromType<int>()); + QCOMPARE(name1.toInt(), 43); + + QObject *shadowed2 = obj->property("shadowed2").value<QObject *>(); + QVERIFY(shadowed2); + QVERIFY(shadowed2->objectName().isEmpty()); + const QVariant name2 = shadowed2->property("objectName"); + QCOMPARE(name2.metaType(), QMetaType::fromType<int>()); + QCOMPARE(name2.toInt(), 42); + + QObject *shadowed3 = obj->property("shadowed3").value<QObject *>(); + QVERIFY(shadowed3); + QVERIFY(shadowed3->objectName().isEmpty()); + const QVariant name3 = shadowed3->property("objectName"); + QCOMPARE(name3.metaType(), QMetaType::fromType<double>()); + QCOMPARE(name3.toDouble(), 41.0); +} + +void tst_QmlCppCodegen::shadowedMethod() +{ + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedMethod.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("athing"), QVariant::fromValue<bool>(false)); + QCOMPARE(o->property("bthing"), QVariant::fromValue(u"b"_s)); + QCOMPARE(o->property("cthing"), QVariant::fromValue(u"c"_s)); +} - if (op == u"unot") - return evalUnary(u"!"_s); - if (op == u"uplus") - return evalUnary(u"+"_s); - if (op == u"uminus") - return evalUnary(u"-"_s); - if (op == u"ucompl") - return evalUnary(u"~"_s); +void tst_QmlCppCodegen::shadowedPrimitiveCmpEqNull() +{ + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedPrimitiveCmpEqNull.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} - if (op == u"increment") - return evalInPlace(u"++"_s); - if (op == u"decrement") - return evalInPlace(u"--"_s); +void tst_QmlCppCodegen::shifts() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/shifts.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - if (op == u"add") - return evalBinary(u"+"_s); - if (op == u"sub") - return evalBinary(u"-"_s); - if (op == u"mul") - return evalBinary(u"*"_s); - if (op == u"div") - return evalBinary(u"/"_s); - if (op == u"exp") - return evalBinary(u"**"_s); - if (op == u"mod") - return evalBinary(u"%"_s); + QCOMPARE(object->property("a").toInt(), 9728); + QCOMPARE(object->property("b").toInt(), 4864); + QCOMPARE(object->property("c").toInt(), 19448); + QCOMPARE(object->property("d").toInt(), 9731); + QCOMPARE(object->property("e").toInt(), 0); +} - if (op == u"bitAnd") - return evalBinary(u"&"_s); - if (op == u"bitOr") - return evalBinary(u"|"_s); - if (op == u"bitXor") - return evalBinary(u"^"_s); +void tst_QmlCppCodegen::signalHandler() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signal.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->objectName(), QString()); + QCOMPARE(object->property("ff").toInt(), 4); - if (op == u"bitAndConst") - return evalBinaryConst(u"&"_s); - if (op == u"bitOrConst") - return evalBinaryConst(u"|"_s); - if (op == u"bitXorConst") - return evalBinaryConst(u"^"_s); + object->setObjectName(u"foo"_s); + QCOMPARE(object->property("ff").toInt(), 12); +} - if (op == u"ushr") - return evalBinary(u">>>"_s); - if (op == u"shr") - return evalBinary(u">>"_s); - if (op == u"shl") - return evalBinary(u"<<"_s); +void tst_QmlCppCodegen::signalIndexMismatch() +{ + QQmlEngine engine; - if (op == u"ushrConst") - return evalBinaryConst(u">>>"_s); - if (op == u"shrConst") - return evalBinaryConst(u">>"_s); - if (op == u"shlConst") - return evalBinaryConst(u"<<"_s); + QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalIndexMismatch.qml"_s)); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - qDebug() << op; - Q_UNREACHABLE_RETURN(0); + QScopedPointer<QObject> item(c1.create()); + const auto visualIndexBeforeMoveList = item->property("visualIndexBeforeMove").toList(); + const auto visualIndexAfterMoveList = item->property("visualIndexAfterMove").toList(); + + QCOMPARE(visualIndexBeforeMoveList, QList<QVariant>({ 0, 1, 2 })); + QCOMPARE(visualIndexAfterMoveList, QList<QVariant>({ 0, 1, 2 })); } -void tst_QmlCppCodegen::mathOperations() +void tst_QmlCppCodegen::signalsWithLists() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathOperations.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalsWithLists.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - const QMetaObject *metaObject = o->metaObject(); + QVariantList varlist = o->property("varlist").toList(); + QCOMPARE(varlist.size(), 5); + QCOMPARE(varlist[0], QVariant::fromValue(1)); + QCOMPARE(varlist[1], QVariant::fromValue(u"foo"_s)); + QCOMPARE(varlist[2], QVariant::fromValue(o.data())); + QCOMPARE(varlist[3], QVariant()); + QCOMPARE(varlist[4], QVariant::fromValue(true)); - char t1; - char t2; - QString name; - const auto guard = qScopeGuard([&]() { - if (QTest::currentTestFailed()) { - qDebug() << t1 << t2 << name << "failed on:"; - qDebug() << "doubles" << o->property("a").toDouble() << o->property("b").toDouble(); - qDebug() << "integers" << o->property("ia").toInt() << o->property("ib").toInt(); - qDebug() << "booleans" << o->property("ba").toBool() << o->property("bb").toBool(); - } - }); + QQmlListProperty<QObject> objlist = o->property("objlist").value<QQmlListProperty<QObject>>(); + QCOMPARE(objlist.count(&objlist), 3); + QCOMPARE(objlist.at(&objlist, 0), o.data()); + QCOMPARE(objlist.at(&objlist, 1), nullptr); + QCOMPARE(objlist.at(&objlist, 2), o.data()); - for (double a : numbers) { - for (double b : numbers) { - o->setProperty("a", a); - o->setProperty("b", b); - for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) { - const QMetaProperty prop = metaObject->property(i); - const QByteArray propName = prop.name(); + QCOMPARE(o->property("happening").toInt(), 0); + o->metaObject()->invokeMethod(o.data(), "sendSignals"); + QCOMPARE(o->property("happening").toInt(), 8); +} - if (propName.size() < 3 || propName == "objectName") - continue; +void tst_QmlCppCodegen::signatureIgnored() +{ + QQmlEngine engine; - t1 = propName[0]; - t2 = propName[1]; - name = QString::fromUtf8(propName.mid(2)); + QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signatureIgnored.qml"_s)); + QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - double expected; + QScopedPointer<QObject> ignored(c1.create()); + QCOMPARE(ignored->property("l").toInt(), 5); + QCOMPARE(ignored->property("m").toInt(), 77); + QCOMPARE(ignored->property("n").toInt(), 67); +} - switch (t2) { - case 'd': - case '_': - switch (t1) { - case 'd': - expected = jsEval<double, double>(a, b, name, &engine); - break; - case 'i': - expected = jsEval<int, double>(a, b, name, &engine); - break; - case 'b': - expected = jsEval<bool, double>(a, b, name, &engine); - break; - } - break; - case 'i': - switch (t1) { - case 'd': - expected = jsEval<double, int>(a, b, name, &engine); - break; - case 'i': - expected = jsEval<int, int>(a, b, name, &engine); - break; - case 'b': - expected = jsEval<bool, int>(a, b, name, &engine); - break; - } - break; - case 'b': - switch (t1) { - case 'd': - expected = jsEval<double, bool>(a, b, name, &engine); - break; - case 'i': - expected = jsEval<int, bool>(a, b, name, &engine); - break; - case 'b': - expected = jsEval<bool, bool>(a, b, name, &engine); - break; - } - break; - } +void tst_QmlCppCodegen::simpleBinding() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s)); + QScopedPointer<QObject> object(component.create()); + QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData()); + QCOMPARE(object->property("foo").toInt(), int(3)); - const double result = prop.read(o.data()).toDouble(); - QCOMPARE(result, expected); - } - } + { + CppBaseClass *base = qobject_cast<CppBaseClass *>(object.data()); + Q_ASSERT(base); + QVERIFY(!base->cppProp.hasBinding()); + QCOMPARE(base->cppProp.value(), 7); + QVERIFY(base->cppProp2.hasBinding()); + QCOMPARE(base->cppProp2.value(), 14); + base->cppProp.setValue(9); + QCOMPARE(base->cppProp.value(), 9); + QCOMPARE(base->cppProp2.value(), 18); } } -void tst_QmlCppCodegen::inaccessibleProperty() +void tst_QmlCppCodegen::storeElementSideEffects() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/versionmismatch.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/storeElementSideEffects.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(o->property("c").toInt(), 5); + const QJSValue prop = o->property("myItem").value<QJSValue>(); + QVERIFY(prop.isArray()); + QCOMPARE(prop.property(0).toInt(), 10); } -void tst_QmlCppCodegen::typePropagationLoop() +void tst_QmlCppCodegen::storeMetaEnum() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropagationLoop.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/StoreMetaEnum.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(o->property("j").toInt(), 3); + QCOMPARE(o->property("bar").toInt(), 0); + QCOMPARE(o->property("baz").toInt(), 1); } -void tst_QmlCppCodegen::signatureIgnored() +void tst_QmlCppCodegen::stringArg() { QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringArg.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signatureIgnored.qml"_s)); - QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - - QScopedPointer<QObject> ignored(c1.create()); - QCOMPARE(ignored->property("l").toInt(), 5); - QCOMPARE(ignored->property("m").toInt(), 77); - QCOMPARE(ignored->property("n").toInt(), 67); + QCOMPARE(o->property("stringArg"), u"a foozly thing"_s); + QCOMPARE(o->property("falseArg"), u"a 0 thing"_s); + QCOMPARE(o->property("trueArg"), u"a 1 thing"_s); + QCOMPARE(o->property("zeroArg"), u"a 0 thing"_s); + QCOMPARE(o->property("intArg"), u"a 11 thing"_s); + QCOMPARE(o->property("realArg"), u"a 12.25 thing"_s); } -void tst_QmlCppCodegen::listAsArgument() +void tst_QmlCppCodegen::stringLength() { QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringLength.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("stringLength").toInt(), 8); +} - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listAsArgument.qml"_s)); +void tst_QmlCppCodegen::stringToByteArray() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringToByteArray.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QCOMPARE(o->property("i").toInt(), 4); - QCOMPARE(o->property("j").toInt(), 2); - QCOMPARE(o->property("i1").toInt(), 2); - QCOMPARE(o->property("i2").toInt(), 4); - QCOMPARE(o->property("d").value<QObject *>()->objectName(), u"this one"_s); - int singleInt = 0; - QList<int> moreInts; - QMetaObject::invokeMethod(o.data(), "returnInts1", Q_RETURN_ARG(QList<int>, moreInts)); - QCOMPARE(moreInts, QList<int>({5, 4, 3, 2, 1})); - QMetaObject::invokeMethod(o.data(), "selectSecondInt", Q_RETURN_ARG(int, singleInt), Q_ARG(QList<int>, moreInts)); - QCOMPARE(singleInt, 4); + Person *person = qobject_cast<Person *>(o.data()); + QVERIFY(person); + + QCOMPARE(person->dataBindable().value(), QByteArray("some data")); + QCOMPARE(person->name(), u"some data"_s); } -void tst_QmlCppCodegen::letAndConst() +void tst_QmlCppCodegen::structuredValueType() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/letAndConst.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/structuredValueType.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(!o.isNull()); - QCOMPARE(o->objectName(), u"ab"_s); + + QCOMPARE(o->property("r").value<QRectF>(), QRectF(1, 2, 3, 4)); + QCOMPARE(o->property("r2").value<QRectF>(), QRectF(42, 0, 0, 0)); + + WeatherModelUrl w; + w.setStrings(QStringList({"one", "two", "three"})); + + QCOMPARE(o->property("w").value<WeatherModelUrl>(), w); } -void tst_QmlCppCodegen::signalIndexMismatch() +void tst_QmlCppCodegen::testIsnan() { QQmlEngine engine; + const QUrl document(u"qrc:/qt/qml/TestTypes/isnan.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalIndexMismatch.qml"_s)); - QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); + QCOMPARE(o->property("good").toDouble(), 10.1); + QVERIFY(qIsNaN(o->property("bad").toDouble())); - QScopedPointer<QObject> item(c1.create()); - const auto visualIndexBeforeMoveList = item->property("visualIndexBeforeMove").toList(); - const auto visualIndexAfterMoveList = item->property("visualIndexAfterMove").toList(); + const QVariant a = o->property("a"); + QCOMPARE(a.metaType(), QMetaType::fromType<bool>()); + QVERIFY(!a.toBool()); - QCOMPARE(visualIndexBeforeMoveList, QList<QVariant>({ 0, 1, 2 })); - QCOMPARE(visualIndexAfterMoveList, QList<QVariant>({ 0, 1, 2 })); + const QVariant b = o->property("b"); + QCOMPARE(b.metaType(), QMetaType::fromType<bool>()); + QVERIFY(b.toBool()); } -void tst_QmlCppCodegen::callWithSpread() +void tst_QmlCppCodegen::thisObject() { - QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callWithSpread.qml"_s)); + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/thisObject.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QTest::ignoreMessage(QtCriticalMsg, "That is great!"); QScopedPointer<QObject> o(c.create()); QVERIFY(!o.isNull()); + QCOMPARE(o->property("warned").value<QObject *>(), o.data()); } -void tst_QmlCppCodegen::nullComparison() +void tst_QmlCppCodegen::throwObjectName() { QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/throwObjectName.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullComparison.qml"_s)); + QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/throwObjectName.qml:5:5: ouch"); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QVERIFY(o->objectName().isEmpty()); +} + +void tst_QmlCppCodegen::topLevelComponent() +{ + // TODO: Once we stop accepting top level Component elements, this test can be removed. + + QQmlEngine e; + + const QUrl url(u"qrc:/qt/qml/TestTypes/topLevelComponent.qml"_s); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + u":4:1: Using a Component as the root of a QML document " + "is deprecated: types defined in qml documents are " + "automatically wrapped into Components when needed."_s)); + + QQmlComponent c(&e, url); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(!o.isNull()); - QCOMPARE(o->property("v").toInt(), 1); - QCOMPARE(o->property("w").toInt(), 3); - QCOMPARE(o->property("x").toInt(), 1); - QCOMPARE(o->property("y").toInt(), 5); + QQmlComponent *inner = qobject_cast<QQmlComponent *>(o.data()); + QVERIFY(inner); + + QScopedPointer<QObject> o2(inner->create()); + QCOMPARE(o2->objectName(), u"foo"_s); } -void tst_QmlCppCodegen::consoleObject() +void tst_QmlCppCodegen::translation() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleObject.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/translation.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - QTest::ignoreMessage(QtDebugMsg, "b 4.55"); - QTest::ignoreMessage(QtDebugMsg, "b 4.55"); - QTest::ignoreMessage(QtInfoMsg, "b 4.55"); - QTest::ignoreMessage(QtWarningMsg, "b 4.55"); - QTest::ignoreMessage(QtCriticalMsg, "b 4.55"); + QCOMPARE(o->property("translate2"), u"s"_s); + QCOMPARE(o->property("translate3"), u"s"_s); + QCOMPARE(o->property("translate4"), u"s"_s); - // Unfortunately we cannot check the logging category with QTest::ignoreMessage - QTest::ignoreMessage(QtDebugMsg, "b 4.55"); - QTest::ignoreMessage(QtDebugMsg, "b 4.55"); - QTest::ignoreMessage(QtInfoMsg, "b 4.55"); - QTest::ignoreMessage(QtWarningMsg, "b 4.55"); - QTest::ignoreMessage(QtCriticalMsg, "b 4.55"); + QCOMPARE(o->property("translateNoop2"), u"s"_s); + QCOMPARE(o->property("translateNoop3"), u"s"_s); - const QRegularExpression re(u"QQmlComponentAttached\\(0x[0-9a-f]+\\) b 4\\.55"_s); - QTest::ignoreMessage(QtDebugMsg, re); - QTest::ignoreMessage(QtDebugMsg, re); - QTest::ignoreMessage(QtInfoMsg, re); - QTest::ignoreMessage(QtWarningMsg, re); - QTest::ignoreMessage(QtCriticalMsg, re); + QCOMPARE(o->property("tr1"), u"s"_s); + QCOMPARE(o->property("tr2"), u"s"_s); + QCOMPARE(o->property("tr3"), u"s"_s); - QTest::ignoreMessage(QtDebugMsg, "a undefined b false null 7"); - QTest::ignoreMessage(QtDebugMsg, ""); - QTest::ignoreMessage(QtDebugMsg, "4"); - QTest::ignoreMessage(QtDebugMsg, ""); + QCOMPARE(o->property("trNoop1"), u"s"_s); + QCOMPARE(o->property("trNoop2"), u"s"_s); - const QRegularExpression re2(u"QQmlComponentAttached\\(0x[0-9a-f]+\\)"_s); - QTest::ignoreMessage(QtDebugMsg, re2); + QCOMPARE(o->property("trId1"), u"s"_s); + QCOMPARE(o->property("trId2"), u"s"_s); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); + QCOMPARE(o->property("trIdNoop1"), u"s"_s); } -void tst_QmlCppCodegen::multiForeign() +void tst_QmlCppCodegen::trigraphs() { - QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiforeign.qml"_s)); + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/trigraphs.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QCOMPARE(o->objectName(), u"not here and not there"_s); + QCOMPARE(o->objectName(), u"?""?= ?""?/ ?""?' ?""?( ?""?) ?""?! ?""?< ?""?> ?""?-"_s); } -void tst_QmlCppCodegen::namespaceWithEnum() +void tst_QmlCppCodegen::trivialSignalHandler() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/namespaceWithEnum.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/trivialSignalHandler.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QCOMPARE(o->property("i").toInt(), 2); + + QCOMPARE(o->property("a").toString(), u"no"_s); + QCOMPARE(o->property("b").toInt(), -1); + QCOMPARE(o->property("b").toDouble(), -1.0); + + o->setObjectName(u"yes"_s); + QCOMPARE(o->property("a").toString(), u"yes"_s); + QCOMPARE(o->property("b").toInt(), 5); + QCOMPARE(o->property("c").toDouble(), 2.5); } -void tst_QmlCppCodegen::enumProblems() +void tst_QmlCppCodegen::typePropagationLoop() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumProblems.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> outer(c.create()); - QVERIFY(!outer.isNull()); - QObject *inner = outer->property("o").value<QObject *>(); - QVERIFY(inner); - Foo *bar = inner->property("bar").value<Foo *>(); - QVERIFY(bar); - QCOMPARE(bar->type(), Foo::Component); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropagationLoop.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); - Foo *fighter = inner->property("fighter").value<Foo *>(); - QVERIFY(fighter); - QCOMPARE(fighter->type(), Foo::Fighter); + QCOMPARE(o->property("j").toInt(), 3); } -void tst_QmlCppCodegen::enumConversion() +void tst_QmlCppCodegen::typePropertyClash() { QQmlEngine engine; - - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumConversion.qml"_s)); + engine.rootContext()->setContextProperty(u"size"_s, 5); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropertyClash.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), u"Size: 5"_s); +} +void tst_QmlCppCodegen::typedArray() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/qt/qml/TestTypes/typedArray.qml"_s); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(o); - QCOMPARE(o->property("test").toInt(), 0x04); - QCOMPARE(o->property("test_1").toBool(), true); + QDateTime date; + QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty()); + QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")), + QList<int>({1, 2, 3, 4})); + QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")), + QList<QDateTime>({date, date, date})); + { + const QList<double> actual + = qvariant_cast<QList<double>>(o->property("values5")); + const QList<double> expected + = QList<double>({1, 2, 3.4, 30, std::numeric_limits<double>::quiet_NaN(), 0}); + QCOMPARE(actual.size(), expected.size()); + for (qsizetype i = 0, end = actual.size(); i != end; ++i) { + if (std::isnan(expected[i])) + QVERIFY(std::isnan(actual[i])); + else + QCOMPARE(actual[i], expected[i]); + } + } + date = QDateTime::currentDateTime(); + o->setProperty("aDate", date); + QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")), + QList<QDateTime>({date, date, date})); + + QQmlListProperty<QObject> values6 + = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6")); + QCOMPARE(values6.count(&values6), 3); + for (int i = 0; i < 3; ++i) + QCOMPARE(values6.at(&values6, i), o.data()); + + QCOMPARE(o->property("inIntList").toInt(), 2); + QCOMPARE(qvariant_cast<QDateTime>(o->property("inDateList")), date); + QCOMPARE(o->property("inRealList").toDouble(), 30.0); + QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f")); + + const QMetaObject *metaObject = o->metaObject(); + QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)")); + QVERIFY(method.isValid()); + + // If LoadElement threw an exception the function would certainly return neither 10 nor 20. + int result = 0; + method.invoke( + o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a"))); + QCOMPARE(result, 10); + method.invoke( + o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa"))); + QCOMPARE(result, 20); } -void tst_QmlCppCodegen::storeElementSideEffects() +void tst_QmlCppCodegen::undefinedResets() { QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedResets.qml"_s)); - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/storeElementSideEffects.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); - QScopedPointer<QObject> o(c.create()); - QVERIFY(o); + Person *person = qobject_cast<Person *>(rootObject.data()); + QVERIFY(person); + QCOMPARE(person->shoeSize(), 0); + QCOMPARE(person->name(), u"Marge"_s); - const QJSValue prop = o->property("myItem").value<QJSValue>(); - QVERIFY(prop.isArray()); - QCOMPARE(prop.property(0).toInt(), 10); -}; + person->setShoeSize(11); -void tst_QmlCppCodegen::ambiguousSignals() + QCOMPARE(person->shoeSize(), 11); + QCOMPARE(person->name(), u"Bart"_s); + + person->setShoeSize(10); + QCOMPARE(person->shoeSize(), 10); + QCOMPARE(person->name(), u"Marge"_s); + + person->setName(u"no one"_s); + QCOMPARE(person->name(), u"no one"_s); + + person->setObjectName(u"the one"_s); + QCOMPARE(person->name(), u"Bart"_s); +} + +void tst_QmlCppCodegen::undefinedToDouble() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguousSignals.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedToDouble.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(!o.isNull()); - QCOMPARE(o->objectName(), u"tomorrow"_s); - Person *p = qobject_cast<Person *>(o.data()); - QVERIFY(p); - emit p->ambiguous(12); - QCOMPARE(o->objectName(), u"12foo"_s); - emit p->ambiguous(); - QCOMPARE(o->objectName(), u"9foo"_s); + const QVariant d = o->property("d"); + QCOMPARE(d.metaType(), QMetaType::fromType<double>()); + QVERIFY(std::isnan(d.toDouble())); } -void tst_QmlCppCodegen::fileImportsContainCxxTypes() +void tst_QmlCppCodegen::unknownAttached() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/usingCxxTypesFromFileImports.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QCOMPARE(o->objectName(), u"horst guenther"_s); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownAttached.qml"_s)); + QVERIFY(c.isError()); } -void tst_QmlCppCodegen::lengthAccessArraySequenceCompat() +void tst_QmlCppCodegen::unknownParameter() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ArraySequenceLengthInterop.qml"_s)); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownParameter.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("cppProp").toInt(), 18); +} + +void tst_QmlCppCodegen::unstoredUndefined() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unstoredUndefined.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QCOMPARE(o->property("length").toInt(), 100); + QCOMPARE(o->objectName(), u"NaN"_s); } -static QList<QString> convertToStrings(const QList<int> &ints) +void tst_QmlCppCodegen::unusedAttached() { - QList<QString> strings; - for (int i : ints) - strings.append(QString::number(i)); - return strings; + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unusedAttached.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + + const auto func = qmlAttachedPropertiesFunction( + object.data(), QMetaType::fromName("QQuickKeyNavigationAttached*").metaObject()); + QObject *attached = qmlAttachedPropertiesObject(object.data(), func); + const QVariant prop = attached->property("priority"); + QVERIFY(prop.isValid()); + QCOMPARE(QByteArray(prop.metaType().name()), "QQuickKeyNavigationAttached::Priority"); + bool ok = false; + QCOMPARE(prop.toInt(&ok), 0); + QVERIFY(ok); } -void tst_QmlCppCodegen::numbersInJsPrimitive() +void tst_QmlCppCodegen::urlString() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/numbersInJsPrimitive.qml"_s)); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/urlString.qml"_s)); - const QList<int> zeroes = {0, 0, 0, 0}; - const QList<int> written = {39, 40, 41, 42}; - const QList<int> stored = {1334, 1335, 1336, 1337}; - QStringList asStrings(4); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> rootObject(component.create()); + QVERIFY(rootObject); - for (int i = 0; i < 4; ++i) { - QMetaObject::invokeMethod( - o.data(), "readValueAsString", - Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s)); + QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s)); + QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s)); + QCOMPARE(rootObject->objectName(), QLatin1String("http://dddddd.com")); +} + +void tst_QmlCppCodegen::valueTypeBehavior() +{ + QQmlEngine engine; + + { + const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeCopy.qml"_s); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e')); + QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f')); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("e").toDouble(), 45.0); + QCOMPARE(o->property("f").toDouble(), 1.0); } - QCOMPARE(asStrings, convertToStrings(zeroes)); - QMetaObject::invokeMethod(o.data(), "writeValues"); - for (int i = 0; i < 4; ++i) { - QMetaObject::invokeMethod( - o.data(), "readValueAsString", - Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + { + const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeReference.qml"_s); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e')); + QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f')); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QVERIFY(qIsNaN(o->property("e").toDouble())); + QCOMPARE(o->property("f").toDouble(), 5.0); } - QCOMPARE(asStrings, convertToStrings(written)); - QMetaObject::invokeMethod(o.data(), "storeValues"); - for (int i = 0; i < 4; ++i) { - QMetaObject::invokeMethod( - o.data(), "readValueAsString", - Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + { + const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeDefault.qml"_s); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e')); + QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f')); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QVERIFY(qIsNaN(o->property("e").toDouble())); + QCOMPARE(o->property("f").toDouble(), 5.0); + } + + { + const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeCast.qml"_s); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("x"), 10); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + + u":8: TypeError: Cannot read property 'x' of undefined"_s)); + o->setProperty("v", QLatin1String("not a rect")); + + // If the binding throws an exception, the value doesn't change. + QCOMPARE(o->property("x"), 10); + + QCOMPARE(o->property("tv3"), 5); + QCOMPARE(o->property("tc3"), 5); + QCOMPARE(o->property("tc6"), QVariant()); + QCOMPARE(o->property("tc7"), QVariant()); + QCOMPARE(o->property("tc8"), 2); + + // The default greeting is never applied because undefined can be coerced to string + QCOMPARE(o->property("greeting1"), QLatin1String("undefined")); + QCOMPARE(o->property("greeting2"), QLatin1String("Custom Greeting")); } - QCOMPARE(asStrings, convertToStrings(stored)); } -void tst_QmlCppCodegen::infinitiesToInt() +void tst_QmlCppCodegen::valueTypeLists() { QQmlEngine engine; - - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinitiesToInt.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeLists.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - const char *props[] = {"a", "b", "c"}; - for (const char *prop : props) { - const QVariant i = o->property(prop); - QCOMPARE(i.metaType(), QMetaType::fromType<int>()); - bool ok = false; - QCOMPARE(i.toInt(&ok), 0); - QVERIFY(ok); - } + QCOMPARE(qvariant_cast<QRectF>(o->property("rectInBounds")), QRectF(1, 2, 3, 4)); + QVERIFY(o->metaObject()->indexOfProperty("rectOutOfBounds") > 0); + QVERIFY(!o->property("rectOutOfBounds").isValid()); + + QCOMPARE(qvariant_cast<QString>(o->property("stringInBounds")), QStringLiteral("bbb")); + QVERIFY(o->metaObject()->indexOfProperty("stringOutOfBounds") > 0); + QVERIFY(!o->property("stringOutOfBounds").isValid()); + + QCOMPARE(qvariant_cast<int>(o->property("intInBounds")), 7); + QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0); + QVERIFY(!o->property("intOutOfBounds").isValid()); + + QCOMPARE(qvariant_cast<QString>(o->property("charInBounds")), QStringLiteral("d")); + QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0); + QVERIFY(!o->property("charOutOfBounds").isValid()); } -void tst_QmlCppCodegen::equalityVarAndNonStorable() +void tst_QmlCppCodegen::valueTypeProperty() { QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeProperty.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); - QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityVarAndNonStorable.qml"_s)); - QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); + QFont font = qvariant_cast<QFont>(object->property("font")); + QCOMPARE(object->property("foo").toString(), font.family()); + font.setFamily(u"Bar"_s); + object->setProperty("font", QVariant::fromValue(font)); + QCOMPARE(object->property("foo").toString(), u"Bar"_s); +} - QScopedPointer<QObject> object(c1.create()); - QVERIFY(!object.isNull() && !c1.isError()); - QVERIFY(!object->property("aIsNull").toBool()); - QVERIFY(object->property("aIsNotNull").toBool()); - QVERIFY(object->property("aIsNotUndefined").toBool()); - QVERIFY(object->property("objectIsNotNull").toBool()); - QVERIFY(!object->property("typedArrayIsNull").toBool()); - QVERIFY(object->property("isUndefined").toBool()); - QVERIFY(!object->property("derivedIsNull").toBool()); +void tst_QmlCppCodegen::variantMapLookup() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantMapLookup.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("i"), 42); +} - QVERIFY(object->property("primitiveIsNull").toBool()); - QVERIFY(object->property("primitiveIsDefined").toBool()); - QVERIFY(object->property("primitiveIsUndefined").toBool()); +void tst_QmlCppCodegen::variantReturn() +{ + QQmlEngine e; + QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/variantReturn.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QVERIFY(object->property("jsValueIsNull").toBool()); - QVERIFY(object->property("jsValueIsDefined").toBool()); - QVERIFY(object->property("jsValueIsUndefined").toBool()); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); - QVERIFY(object->property("nullVarIsUndefined").toBool()); - QVERIFY(object->property("nullIsUndefined").toBool()); - QVERIFY(object->property("nullVarIsNull").toBool()); - QVERIFY(object->property("nullIsNotUndefined").toBool()); -}; + QObject *a = o->property("a").value<QObject *>(); + QVERIFY(a); + const QVariant x = a->property("x"); + const QMetaObject *meta = x.metaType().metaObject(); + QVERIFY(meta); + const QMetaProperty property = meta->property(meta->indexOfProperty("timeIndex")); + QVERIFY(property.isValid()); + const QVariant timeIndex = property.readOnGadget(x.data()); + QCOMPARE(timeIndex.metaType(), QMetaType::fromType<qsizetype>()); + QCOMPARE(timeIndex.value<qsizetype>(), qsizetype(1)); -void tst_QmlCppCodegen::equalityQObjects() + QObject *b = o->property("b").value<QObject *>(); + QVERIFY(b); + QCOMPARE(b->property("z").toInt(), 2); +} + +void tst_QmlCppCodegen::variantlist() { QQmlEngine engine; - QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQObjects.qml"_s)); - QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - QScopedPointer<QObject> object(c1.create()); - QVERIFY(!object.isNull() && !c1.isError()); - - QVERIFY(object->property("derivedIsNotNull").toBool()); - QVERIFY(object->property("nullObjectIsNull").toBool()); - QVERIFY(object->property("nonNullObjectIsNotNull").toBool()); - QVERIFY(object->property("compareSameObjects").toBool()); - QVERIFY(object->property("compareDifferentObjects").toBool()); - QVERIFY(object->property("compareObjectWithNullObject").toBool()); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantlist.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QVERIFY(object->property("nonStrict_derivedIsNotNull").toBool()); - QVERIFY(object->property("nonStrict_nullObjectIsNull").toBool()); - QVERIFY(object->property("nonStrict_nonNullObjectIsNotNull").toBool()); - QVERIFY(object->property("nonStrict_compareSameObjects").toBool()); - QVERIFY(object->property("nonStrict_compareDifferentObjects").toBool()); - QVERIFY(object->property("nonStrict_compareObjectWithNullObject").toBool()); + const QVariantList things = qvariant_cast<QVariantList>(o->property("things")); + QCOMPARE(things.size(), 2); + QCOMPARE(things[0].toString(), u"thing"_s); + QCOMPARE(things[1].toInt(), 30); } -void tst_QmlCppCodegen::dateConversions() +void tst_QmlCppCodegen::variantMap() { QQmlEngine engine; - QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s)); + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantMap.qml"_s)); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug"); - - const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate()); - const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime()); - - QCOMPARE(o->property("date").value<QDateTime>(), refDate); - QCOMPARE(o->property("time").value<QDateTime>(), refTime); - - QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDateTime, QString>(refDate))); - QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QDateTime, QString>(refTime))); + QCOMPARE(o->objectName(), "a b"_L1); + QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(12, 13, 14, 15))); - QMetaObject::invokeMethod(o.data(), "shuffle"); + const QVariantMap expected = QVariantMap { + { u"1"_s, QVariant::fromValue<std::nullptr_t>(nullptr) }, + { u"19"_s, QVariant::fromValue(u"19"_s) }, + { u"25"_s, QVariant() } + }; - QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate))); - QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime))); + QCOMPARE(o->property("v").toMap(), expected); +} - const QDate date = ref->myDate(); - const QTime time = ref->myTime(); +void tst_QmlCppCodegen::voidConversion() +{ + QQmlEngine engine; + const QUrl url(u"qrc:/qt/qml/TestTypes/voidConversion.qml"_s); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date))); - QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time))); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + u":8: Error: Cannot assign [undefined] to QPointF"_s)); - QMetaObject::invokeMethod(o.data(), "fool"); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); - QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time))); - QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date))); + QCOMPARE(o->property("p"), QPointF(20, 10)); } -static QRegularExpression bindingLoopMessage(const QUrl &url, char var) +void tst_QmlCppCodegen::voidFunction() { - // The actual string depends on how many times QObject* was registered with what parameters. - return QRegularExpression( - "%1:4:1: QML [^:]+: Binding loop detected for property \"%2\""_L1 - .arg(url.toString()).arg(QLatin1Char(var))); + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/voidfunction.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QVERIFY(object->objectName().isEmpty()); + object->metaObject()->invokeMethod(object.data(), "doesNotReturnValue"); + QCOMPARE(object->objectName(), u"barbar"_s); } -void tst_QmlCppCodegen::valueTypeBehavior() +void tst_QmlCppCodegen::writeBack() { QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/writeback.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); - const QUrl copy(u"qrc:/qt/qml/TestTypes/valueTypeCopy.qml"_s); + Person *person = qobject_cast<Person *>(object.data()); + QVERIFY(person); + QCOMPARE(person->area(), QRectF(4, 5, 16, 17)); + QCOMPARE(person->property("inner").toInt(), 99); - QQmlComponent c1(&engine, copy); - QVERIFY2(c1.isReady(), qPrintable(c1.errorString())); - QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(copy, 'e')); - QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(copy, 'f')); - QScopedPointer<QObject> o1(c1.create()); - QVERIFY(!o1.isNull()); - QCOMPARE(o1->property("e").toDouble(), 45.0); - QCOMPARE(o1->property("f").toDouble(), 1.0); + Person *shadowable = person->property("shadowable").value<Person *>(); + QVERIFY(shadowable); + QCOMPARE(shadowable->area(), QRectF(40, 50, 16, 17)); - const QUrl reference(u"qrc:/qt/qml/TestTypes/valueTypeReference.qml"_s); - QQmlComponent c2(&engine, reference); - QVERIFY2(c2.isReady(), qPrintable(c2.errorString())); - QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(reference, 'e')); - QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(reference, 'f')); - QScopedPointer<QObject> o2(c2.create()); - QVERIFY(!o2.isNull()); - QVERIFY(qIsNaN(o2->property("e").toDouble())); - QCOMPARE(o2->property("f").toDouble(), 5.0); + QCOMPARE(person->property("ints"), QVariant::fromValue(QList<int>({12, 22, 2, 1, 0, 0, 33}))); } -void tst_QmlCppCodegen::invisibleSingleton() +void tst_QmlCppCodegen::writeVariantMap() { QQmlEngine engine; - const QUrl copy(u"qrc:/qt/qml/TestTypes/hidden/Main.qml"_s); - QQmlComponent c(&engine, copy); - QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/StringBuilderTestTypes/writeVariantMap.qml"_s)); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + const QVariantMap v = object->property("data").toMap(); + QCOMPARE(v.size(), 1); + const QVariant textPlain = v[u"text/plain"_s]; + QCOMPARE(textPlain.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(textPlain.toString(), u"%Drag Me%"_s); - QTest::ignoreMessage( - QtWarningMsg, - "qrc:/qt/qml/TestTypes/hidden/Main.qml:4:5: " - "Unable to assign [undefined] to QColor"); - QScopedPointer<QObject> o(c.create()); - QVERIFY(!o.isNull()); - QCOMPARE(o->property("c"), QVariant(QMetaType::fromName("QColor"))); } QTEST_MAIN(tst_QmlCppCodegen) diff --git a/tests/auto/qml/qmldiskcache/CMakeLists.txt b/tests/auto/qml/qmldiskcache/CMakeLists.txt index b6fd92d653..e47d2a7f75 100644 --- a/tests/auto/qml/qmldiskcache/CMakeLists.txt +++ b/tests/auto/qml/qmldiskcache/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmldiskcache Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmldiskcache LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmldiskcache SOURCES tst_qmldiskcache.cpp diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 19a6731ff7..810fdecafd 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> @@ -34,12 +34,18 @@ private slots: void recompileAfterDirectoryChange(); void fileSelectors(); void localAliases(); + void aliasToAlias(); void cacheResources(); void stableOrderOfDependentCompositeTypes(); void singletonDependency(); void cppRegisteredSingletonDependency(); void cacheModuleScripts(); void reuseStaticMappings(); + void invalidateSaveLoadCache(); + void duplicateIdsInInlineComponents(); + + void inlineComponentDoesNotCauseConstantInvalidation_data(); + void inlineComponentDoesNotCauseConstantInvalidation(); private: QDir m_qmlCacheDirectory; @@ -98,7 +104,7 @@ struct TestCompiler { closeMapping(); testFilePath = baseDirectory + QStringLiteral("/test.qml"); - cacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + cacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(testFilePath)); mappedFile.setFileName(cacheFilePath); } @@ -170,7 +176,7 @@ struct TestCompiler return false; } - const QString targetCacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + const QString targetCacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(targetTestFilePath)); QFile source(cacheFilePath); @@ -197,16 +203,14 @@ struct TestCompiler { const QString path = fileName.isEmpty() ? testFilePath : tempDir.path() + "/" + fileName; - QQmlRefPointer<QV4::ExecutableCompilationUnit> unit - = QV4::ExecutableCompilationUnit::create(); + auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); return unit->loadFromDisk(QUrl::fromLocalFile(path), QFileInfo(path).lastModified(), &lastErrorString); } quintptr unitData() { - QQmlRefPointer<QV4::ExecutableCompilationUnit> unit - = QV4::ExecutableCompilationUnit::create(); + auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString) ? quintptr(unit->unitData()) @@ -289,12 +293,12 @@ void tst_qmldiskcache::loadLocalAsFallback() f.write(reinterpret_cast<const char *>(&unit), sizeof(unit)); } - QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); + auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), QFileInfo(testCompiler.testFilePath).lastModified(), &testCompiler.lastErrorString); QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString)); - QCOMPARE(unit->objectCount(), 1); + QCOMPARE(unit->qmlData->nObjects, 1u); } void tst_qmldiskcache::regenerateAfterChange() @@ -606,7 +610,7 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); - QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath( + QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(testFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } @@ -622,7 +626,7 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 100); - QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath( + QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(selectedTestFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } @@ -671,6 +675,55 @@ void tst_qmldiskcache::localAliases() } } +void tst_qmldiskcache::aliasToAlias() +{ + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral(R"( + import QML + QtObject { + id: foo + readonly property alias myAlias: bar.prop + + property QtObject o: QtObject { + id: bar + + property QtObject o: QtObject { + id: baz + readonly property int value: 100 + } + + readonly property alias prop: baz.value + } + } + )"); + + { + 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("myAlias").toInt(), 100); + } + + engine.clearComponentCache(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("myAlias").toInt(), 100); + } +} + static QSet<QString> entrySet(const QDir &dir) { const auto &list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); @@ -782,7 +835,7 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes() QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData()); QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData()); - const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -861,7 +914,7 @@ void tst_qmldiskcache::singletonDependency() QCOMPARE(obj->property("value").toInt(), 42); } - const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -919,7 +972,7 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency() QCOMPARE(value.toInt(), 42); } - const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath( QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -961,7 +1014,8 @@ void tst_qmldiskcache::cacheModuleScripts() auto componentPrivate = QQmlComponentPrivate::get(&component); QVERIFY(componentPrivate); - auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit(); + auto compilationUnit = componentPrivate->compilationUnit->dependentScriptsPtr() + ->first()->compilationUnit(); QVERIFY(compilationUnit); auto unitData = compilationUnit->unitData(); QVERIFY(unitData); @@ -1007,6 +1061,326 @@ void tst_qmldiskcache::reuseStaticMappings() QCOMPARE(testCompiler.unitData(), data1); } +class AParent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int x MEMBER x) +public: + AParent(QObject *parent = nullptr) : QObject(parent) {} + int x = 25; +}; + +class BParent : public QObject +{ + Q_OBJECT + + // Insert y before x, to change the property index of x + Q_PROPERTY(int y MEMBER y) + + Q_PROPERTY(int x MEMBER x) +public: + BParent(QObject *parent = nullptr) : QObject(parent) {} + int y = 13; + int x = 25; +}; + +static QString writeTempFile( + const QTemporaryDir &tempDir, const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); +}; + +void tst_qmldiskcache::invalidateSaveLoadCache() +{ + qmlRegisterType<AParent>("Base", 1, 0, "Parent"); + std::unique_ptr<QQmlEngine> e = std::make_unique<QQmlEngine>(); + + // If you store a CU to a .qmlc file at run time, the .qmlc file will contain + // alias entries with the encodedMetaPropertyIndex pre-resolved. That's in + // contrast to .qmlc files generated ahead of time. Exploit that to cause + // a need to recompile the file. + + QTemporaryDir tempDir; + writeTempFile( + tempDir, QLatin1String("B.qml"), + R"( + import QML + QtObject { + component C: QtObject {} + } + )"); + + const QString fileName = writeTempFile( + tempDir, QLatin1String("a.qml"), + R"( + import Base + Parent { + id: self + property alias z: self.x + component C: Parent {} + property C c: C {} + property B.C d: B.C {} + } + )"); + const QUrl url = QUrl::fromLocalFile(fileName); + waitForFileSystem(); + + { + QQmlComponent a(e.get(), url); + QVERIFY2(a.isReady(), qPrintable(a.errorString())); + QScopedPointer<QObject> ao(a.create()); + QVERIFY(!ao.isNull()); + AParent *ap = qobject_cast<AParent *>(ao.data()); + QCOMPARE(ap->property("z").toInt(), ap->x); + } + + QString errorString; + auto oldUnit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); + QVERIFY2(oldUnit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString)); + + // Produce a checksum mismatch. + e->clearComponentCache(); + qmlClearTypeRegistrations(); + qmlRegisterType<BParent>("Base", 1, 0, "Parent"); + e = std::make_unique<QQmlEngine>(); + + { + QQmlComponent b(e.get(), url); + QVERIFY2(b.isReady(), qPrintable(b.errorString())); + QScopedPointer<QObject> bo(b.create()); + QVERIFY(!bo.isNull()); + BParent *bp = qobject_cast<BParent *>(bo.data()); + QCOMPARE(bp->property("z").toInt(), bp->x); + } + + // Make it recompile again. If we ever get rid of the metaobject indices in compilation units, + // the above test will not test the save/load cache anymore. Therefore, in order to make really + // sure that we get a new CU that invalidates the save/load cache, modify the file in place. + + e->clearComponentCache(); + { + QFile file(fileName); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Append)); + file.write(" "); + } + waitForFileSystem(); + + { + QQmlComponent b(e.get(), url); + QVERIFY2(b.isReady(), qPrintable(b.errorString())); + QScopedPointer<QObject> bo(b.create()); + QVERIFY(!bo.isNull()); + BParent *bp = qobject_cast<BParent *>(bo.data()); + QCOMPARE(bp->property("z").toInt(), bp->x); + } + + // Verify that the mapped unit data is actually different now. + // The cache should have been invalidated after all. + // So, now we should be able to load a freshly written CU. + + auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); + QVERIFY2(unit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString)); + + QVERIFY(unit->unitData() != oldUnit->unitData()); +} + +void tst_qmldiskcache::duplicateIdsInInlineComponents() +{ + // Exercise the case of loading strange generalized group properties from .qmlc. + + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral(R"( + import QtQml + QtObject { + component First : QtObject { + property QtObject aa: QtObject { + id: a + } + property Binding bb: Binding { + a.objectName: "test1" + } + } + + component Second : QtObject { + property QtObject aa: QtObject { + id: a + } + property Binding bb: Binding { + a.objectName: "test2" + } + + property Component cc: QtObject { + property QtObject aa: QtObject { + id: a + } + property Binding bb: Binding { + a.objectName: "test3" + } + } + } + + property First first: First {} + property Second second: Second {} + property QtObject third: second.cc.createObject(); + + objectName: first.aa.objectName + second.aa.objectName + third.aa.objectName; + } + )"); + + { + 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->objectName(), "test1test2test3"); + } + + engine.clearComponentCache(); + + { + CleanlyLoadingComponent component(&engine, testCompiler.testFilePath); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->objectName(), "test1test2test3"); + } +} + +void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation_data() +{ + QTest::addColumn<QByteArray>("code"); + + QTest::addRow("simple") << QByteArray(R"( + import QtQml + QtObject { + component Test: QtObject { + property int i: 28 + } + property Test test: Test { + objectName: "foobar" + } + property int k: test.i + } + )"); + + QTest::addRow("with function") << QByteArray(R"( + import QtQml + QtObject { + component Test : QtObject { + id: self + property int i: 2 + property alias j: self.i + } + property Test test: Test { + function updateValue() {} + objectName: 'foobar' + j: 28 + } + property int k: test.j + } + )"); + + QTest::addRow("in nested") << QByteArray(R"( + import QtQuick + Item { + Item { + component Line: Item { + property alias endY: pathLine.y + Item { + Item { + id: pathLine + } + } + } + } + Line { + id: primaryLine + endY: 28 + } + property int k: primaryLine.endY + } + )"); + + QTest::addRow("with revision") << QByteArray(R"( + import QtQuick + ListView { + Item { + id: scrollBar + } + delegate: Image { + mipmap: true + } + Item { + id: refreshNodesIndicator + } + property int k: delegate.createObject().mipmap ? 28 : 4 + } + )"); +} + +void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation() +{ + QFETCH(QByteArray, code); + + QQmlEngine engine; + + TestCompiler testCompiler(&engine); + QVERIFY(testCompiler.tempDir.isValid()); + + auto check = [&](){ + QQmlComponent c(&engine, QUrl::fromLocalFile(testCompiler.testFilePath)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("k"), QVariant::fromValue<int>(28)); + }; + + testCompiler.reset(); + QVERIFY(testCompiler.writeTestFile(code)); + + QVERIFY(testCompiler.loadTestFile()); + + const quintptr data1 = testCompiler.unitData(); + QVERIFY(data1 != 0); + QCOMPARE(testCompiler.unitData(), data1); + check(); + + engine.clearComponentCache(); + + // inline component does not invalidate cache + QVERIFY(testCompiler.loadTestFile()); + QCOMPARE(testCompiler.unitData(), data1); + check(); + + testCompiler.reset(); + QVERIFY(testCompiler.writeTestFile(R"( + import QtQml + QtObject { + component Test : QtObject { + property double d: 2 + } + property Test test: Test { + objectName: 'foobar' + } + })")); + QVERIFY(testCompiler.loadTestFile()); + const quintptr data2 = testCompiler.unitData(); + QVERIFY(data2); + QVERIFY(data1 != data2); +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" diff --git a/tests/auto/qml/qmlformat/CMakeLists.txt b/tests/auto/qml/qmlformat/CMakeLists.txt index 2c908a9190..76923d66de 100644 --- a/tests/auto/qml/qmlformat/CMakeLists.txt +++ b/tests/auto/qml/qmlformat/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmlformat Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlformat LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -24,8 +30,11 @@ qt_internal_add_test(tst_qmlformat Qt::TestPrivate Qt::QuickTestUtilsPrivate TESTDATA ${test_data} + TIMEOUT 3000 ) +add_dependencies(tst_qmlformat Qt::qmlformat) + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml index 92cbeb55f7..019b7f0141 100644 --- a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml +++ b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml @@ -10,17 +10,14 @@ import QtCharts 2.0 @Pippo { atg1: 3 } -@Annotation2 { -} +@Annotation2 {} Item { - @Annotate { - } + @Annotate {} anchors.fill: parent @AnnotateMore { property int x: 5 } - @AnnotateALot { - } + @AnnotateALot {} property variant othersSlice: 0 //![1] @@ -79,19 +76,17 @@ Item { @BindingAnn { bType: 2 } - val2: Item { - } + val2: Item {} @BindingAnn { bType: 3 } val3: [ - Item { - } + Item {} ] @BindingAnn { bType: 4 } - Animation on val { + Animation on val { duration: 34 } } diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml index 8b181f607e..b1662e8898 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml @@ -43,7 +43,7 @@ Item { property bool some_bool: false // This comment is related to the property animation - PropertyAnimation on x { + PropertyAnimation on x { id: foo2 x: 3 y: x + 3 @@ -54,6 +54,7 @@ Item { // Another orphan // More orphans + property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] property bool something_computed: function (x) { const PI = 3, DAYS_PER_YEAR = 365.25; @@ -124,6 +125,7 @@ Item { } // Another orphan inside something_computed + return "foobar"; }() @@ -133,11 +135,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // some_read_only_bool diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.qml index cb8865d94e..c4aef78924 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.qml @@ -43,7 +43,7 @@ Item { property bool some_bool: false // This comment is related to the property animation - PropertyAnimation on x { + PropertyAnimation on x { id: foo2 x: 3 y: x + 3 @@ -54,6 +54,7 @@ Item { // Another orphan // More orphans + property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] property bool something_computed: function (x) { const PI = 3, DAYS_PER_YEAR = 365.25; @@ -124,6 +125,7 @@ Item { } // Another orphan inside something_computed + return "foobar"; }() @@ -133,11 +135,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // some_read_only_bool diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml index ef964f6f60..7cea50213a 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml @@ -43,7 +43,7 @@ Item { property bool some_bool: false // This comment is related to the property animation - PropertyAnimation on x { + PropertyAnimation on x { id: foo2 x: 3 y: x + 3 @@ -54,6 +54,7 @@ Item { // Another orphan // More orphans + property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] property bool something_computed: function (x) { const PI = 3, DAYS_PER_YEAR = 365.25; @@ -124,6 +125,7 @@ Item { } // Another orphan inside something_computed + return "foobar"; }() @@ -133,11 +135,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // some_read_only_bool diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted2.qml b/tests/auto/qml/qmlformat/data/Example1.formatted2.qml index aa662a3d5f..7049686900 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted2.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted2.qml @@ -39,6 +39,7 @@ Item { // Another orphan // More orphans + property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] property bool some_bool: false default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool @@ -114,6 +115,7 @@ Item { } // Another orphan inside something_computed + return "foobar"; }() @@ -140,14 +142,12 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // This comment is related to the property animation - PropertyAnimation on x { + PropertyAnimation on x { id: foo2 x: 3 y: x + 3 diff --git a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml index 7dfe435ac0..95b4fcf4a3 100644 --- a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml +++ b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml @@ -1,4 +1,3 @@ // This comment should be directly above Item after formatting -Item { -} +Item {} diff --git a/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml b/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml new file mode 100644 index 0000000000..8ae4dd7c88 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml @@ -0,0 +1,4 @@ +Item { + // should keep its comma + property var some_array_literal: [30, 20, 0.3,] +} diff --git a/tests/auto/qml/qmlformat/data/arrayEndComma.qml b/tests/auto/qml/qmlformat/data/arrayEndComma.qml new file mode 100644 index 0000000000..1aec09515c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrayEndComma.qml @@ -0,0 +1,8 @@ +Item { + // should keep its comma + property var some_array_literal: [ + 30, + 20, + 0.3, + ] +} diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml new file mode 100644 index 0000000000..ac4cf97881 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml @@ -0,0 +1,10 @@ +import QtQuick + +Item { + Component.onCompleted: { + let f = ([]) => {}; + let g = ([a]) => {}; + let h = ([a, b]) => {}; + let i = ([a, ...b]) => {}; + } +} diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml new file mode 100644 index 0000000000..2a47b2f152 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml @@ -0,0 +1,8 @@ +import QtQuick + +Item { Component.onCompleted: { let f = ([]) => {}; + let g = ([a]) => {}; + let h = ([a, b]) => {}; + let i = ([a, ...b]) => {}; + } +} diff --git a/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml new file mode 100644 index 0000000000..071e3bc69f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml @@ -0,0 +1,12 @@ +/* comment with spaces */ + +/* + another comment + */ +// + +// another comment /*test */ + +import QtQml + +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml new file mode 100644 index 0000000000..c99415aeed --- /dev/null +++ b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml @@ -0,0 +1,15 @@ +/* comment with spaces */ + +/* + another comment + */ +// + + +// another comment /*test */ + + + +import QtQml + +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/class.formatted.js b/tests/auto/qml/qmlformat/data/class.formatted.js new file mode 100644 index 0000000000..eeb1faf64e --- /dev/null +++ b/tests/auto/qml/qmlformat/data/class.formatted.js @@ -0,0 +1,5 @@ +class Person { + constructor(name) { + this._name = name; + } +} diff --git a/tests/auto/qml/qmlformat/data/class.js b/tests/auto/qml/qmlformat/data/class.js new file mode 100644 index 0000000000..116061d515 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/class.js @@ -0,0 +1 @@ +class Person{constructor(name){this._name = name;}} diff --git a/tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml b/tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml new file mode 100644 index 0000000000..583c315c4b --- /dev/null +++ b/tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml @@ -0,0 +1,11 @@ +import QtQml + +QtObject { + // This to enum + enum Foo { + A = 3, // This is A + B, // This is B + C = 4, // This is C + D // This is D + } +} diff --git a/tests/auto/qml/qmlformat/data/commentInEnum.qml b/tests/auto/qml/qmlformat/data/commentInEnum.qml new file mode 100644 index 0000000000..3a1b15d278 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/commentInEnum.qml @@ -0,0 +1,12 @@ +import QtQml + +QtObject { + // This to enum + enum Foo { + A = 3, // This is A + B, // This is B + C = 4, // This is C + D // This is D + } + +} diff --git a/tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml b/tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml new file mode 100644 index 0000000000..749dc65b7c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml @@ -0,0 +1,4 @@ +import QtQml + +// hello world +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/commentInQmlObject.qml b/tests/auto/qml/qmlformat/data/commentInQmlObject.qml new file mode 100644 index 0000000000..9aca637e4f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/commentInQmlObject.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + // hello world +} diff --git a/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml new file mode 100644 index 0000000000..0bfcc58179 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml @@ -0,0 +1,24 @@ +import QtQml + +QtObject { + + function evil({ + hello = "world", + x = 42 + }, [n = 42, m = 43, o = 44], { + destructuring, + is = { + a, + lot, + of + }, + fun = 42 + } = { + destructuring: 123, + is: { + x: 123 + }, + fun: 456 + }) { + } +} diff --git a/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml new file mode 100644 index 0000000000..f41661ed67 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml @@ -0,0 +1,9 @@ +import QtQml + +QtObject { + + function evil({ hello = "world", x = 42 }, + [n = 42, m = 43, o = 44], + { destructuring, is = {a, lot, of}, fun = 42 } = {destructuring : 123, is : {x : 123}, fun : 456}) { + } +} diff --git a/tests/auto/qml/qmlformat/data/directives.formatted.js b/tests/auto/qml/qmlformat/data/directives.formatted.js new file mode 100644 index 0000000000..3d0a4db544 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/directives.formatted.js @@ -0,0 +1,8 @@ +.pragma library +.import "fun.js" as Fun +.import Qt.test 1.0 as JsQtTest +.import Qt.Quick as Test + +Fun.begin(); +Test.do_stuff(); +JsQtTest.do_stuff(); diff --git a/tests/auto/qml/qmlformat/data/directives.js b/tests/auto/qml/qmlformat/data/directives.js new file mode 100644 index 0000000000..b8e1b785ac --- /dev/null +++ b/tests/auto/qml/qmlformat/data/directives.js @@ -0,0 +1,9 @@ + +.pragma library + +.import "fun.js" as Fun +.import Qt.test 1.0 as JsQtTest + + +.import Qt.Quick as Test +Fun.begin();Test.do_stuff();JsQtTest.do_stuff(); diff --git a/tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js b/tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js new file mode 100644 index 0000000000..c130f72453 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js @@ -0,0 +1,6 @@ +.pragma library // use as library +/* + Fun is necessary + */ +.import "fun.js" as Fun +.import Qt.test 1.0 as JsQtTest // yet another comment not to be lost diff --git a/tests/auto/qml/qmlformat/data/directivesWithComments.js b/tests/auto/qml/qmlformat/data/directivesWithComments.js new file mode 100644 index 0000000000..7206bb27ed --- /dev/null +++ b/tests/auto/qml/qmlformat/data/directivesWithComments.js @@ -0,0 +1,8 @@ + +.pragma library // use as library + +/* + Fun is necessary + */ +.import "fun.js" as Fun +.import Qt.test 1.0 as JsQtTest // yet another comment not to be lost diff --git a/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml b/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml index 0c7a2829c9..8eaac71178 100644 --- a/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml +++ b/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml @@ -1,13 +1,15 @@ Item { - property var test: [{ + property var test: [ + { // Testing "foo": "bar" - }] + } + ] onTestChanged: { fooBar(test, { - // Testing - "foo": "bar" - }); + // Testing + "foo": "bar" + }); } } diff --git a/tests/auto/qml/qmlformat/data/dontRemoveComments.qml b/tests/auto/qml/qmlformat/data/dontRemoveComments.qml index 1797834879..2d2b4b6705 100644 --- a/tests/auto/qml/qmlformat/data/dontRemoveComments.qml +++ b/tests/auto/qml/qmlformat/data/dontRemoveComments.qml @@ -1,8 +1,10 @@ Item { - property var test: [{ -// Testing + property var test: [ + { + // Testing "foo": "bar" - }] + } + ] onTestChanged: { fooBar(test, { diff --git a/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml index edbb12c6e6..de6a23f9c7 100644 --- a/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml +++ b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml @@ -4,11 +4,13 @@ Item { function f() { var count = 0; + class Person { constructor(name){ this._name = name; } } + class Employee extends Person { constructor(name, age){ super(name); diff --git a/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml new file mode 100644 index 0000000000..f41942e054 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml @@ -0,0 +1,13 @@ +import QtQml + +QtObject { + + function patron(a, ...b) { + } + + function patron1(a, ...[b, ...args]) { + } + + function patron2(...{}) { + } +} diff --git a/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml new file mode 100644 index 0000000000..28364f29c9 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml @@ -0,0 +1,11 @@ +import QtQml + +QtObject { + + function patron(a, ...b) { + } + + function patron1(a, ...[b, ...args]) {} + + function patron2(...{}) {} +}
\ No newline at end of file diff --git a/tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml b/tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml new file mode 100644 index 0000000000..bbf978936f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/enumWithValues.formatted.qml @@ -0,0 +1,12 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Rectangle { + enum AxisAlignment { + Bottom = 0, + Left = 1, + Right = 2 + } +} diff --git a/tests/auto/qml/qmlformat/data/enumWithValues.qml b/tests/auto/qml/qmlformat/data/enumWithValues.qml new file mode 100644 index 0000000000..2dbe7fbac5 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/enumWithValues.qml @@ -0,0 +1,12 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Rectangle{ +enum AxisAlignment{ +Bottom = 0, +Left = 1, +Right = 2 +} +} diff --git a/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml b/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml new file mode 100644 index 0000000000..3f6807834f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml @@ -0,0 +1,16 @@ +import QtQuick + +Item { + x: { + const s = "\""; + let a = { + "\"": "\\" + }; + + let patron = { + "\\\"\n\n": "\?\?\\\"", + "": "", + "\'\"\n": 1 + }; + } +} diff --git a/tests/auto/qml/qmlformat/data/escapeChars.qml b/tests/auto/qml/qmlformat/data/escapeChars.qml new file mode 100644 index 0000000000..c82ff3119e --- /dev/null +++ b/tests/auto/qml/qmlformat/data/escapeChars.qml @@ -0,0 +1,16 @@ +import QtQuick + +Item { + x: { + const s = "\"" + let a = { + "\"": "\\" + }; + + let patron = { + "\\\"\n\n" : "\?\?\\\"","": "", "\'\"\n":1 + }; + + + } +} diff --git a/tests/auto/qml/qmlformat/data/esm.formatted.mjs b/tests/auto/qml/qmlformat/data/esm.formatted.mjs new file mode 100644 index 0000000000..f0b3fd7753 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/esm.formatted.mjs @@ -0,0 +1,44 @@ +//Imports +import defaultExport from "module-name"; +import * as name from "module-name"; +import { export1 } from "module-name"; +import { export1 as alias1 } from "module-name"; +import { default as alias } from "module-name"; +import { export1, export2 } from "module-name"; +import { export1, export2 as alias2 } from "module-name"; +import defaultExport, { export1, a } from "module-name"; +import defaultExport, * as name from "module-name"; +import "module-name"; + +// Exporting declarations +export let name1, name2; // also var +export const name1 = 1, name2 = 2; // also var, let +export function functionName() {} +export class ClassName { + constructor(h){ + this.h = h; + } +} +export function* generatorFunctionName() {} +export const { + name1, + name2: bar +} = o; +export const [name1, name2] = array; + +// Export list +export { name1, nameN }; +export { variable1 as name1, variable2 as name2, nameN }; +export { name1 as default }; + +// Default exports +export default function* generatorFunctionName() { + return 1; +} + +// Aggregating modules +export * from "module-name"; +export { name1, nameN } from "module-name"; +export { import1 as name1, import2 as name2, nameN } from "module-name"; +export { default } from "module-name"; +export { default as name1 } from "module-name"; diff --git a/tests/auto/qml/qmlformat/data/esm.mjs b/tests/auto/qml/qmlformat/data/esm.mjs new file mode 100644 index 0000000000..83e22fdedd --- /dev/null +++ b/tests/auto/qml/qmlformat/data/esm.mjs @@ -0,0 +1,33 @@ +//Imports +import defaultExport from "module-name"; +import * as name from "module-name" +import {export1} from "module-name"; +import { export1 as alias1 } from "module-name"; +import { default as alias } from "module-name" +import { export1, export2 } from "module-name"; +import {export1,export2 as alias2} from "module-name"; +import defaultExport,{export1,a} from "module-name" +import defaultExport, * as name from "module-name"; +import "module-name"; + + +// Exporting declarations +export let name1,name2; // also var +export const name1=1,name2=2 // also var, let +export function functionName() {} +export class ClassName{constructor(h){this.h=h;}} +export function* generatorFunctionName() {} +export const {name1, name2: bar}=o;export const [name1,name2]=array + +// Export list +export {name1,nameN};export {variable1 as name1,variable2 as name2,nameN } +export {name1 as default}; + +// Default exports +export default function* generatorFunctionName() {return 1;} + +// Aggregating modules +export * from "module-name"; +export { name1,nameN} from "module-name" +export { import1 as name1, import2 as name2,nameN } from "module-name";export { default, } from "module-name"; +export { default as name1 } from "module-name"; diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml b/tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml new file mode 100644 index 0000000000..a995f0479f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml @@ -0,0 +1,10 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { + property int a + function aa() { + } +} diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid1.qml b/tests/auto/qml/qmlformat/data/filesOption/valid1.qml new file mode 100644 index 0000000000..8aef366bd9 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/filesOption/valid1.qml @@ -0,0 +1,9 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { +property int a +function aa(){} +}
\ No newline at end of file diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml b/tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml new file mode 100644 index 0000000000..a995f0479f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml @@ -0,0 +1,10 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { + property int a + function aa() { + } +} diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid2.qml b/tests/auto/qml/qmlformat/data/filesOption/valid2.qml new file mode 100644 index 0000000000..f2cb636c00 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/filesOption/valid2.qml @@ -0,0 +1,9 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { + property int a + function aa() {} +} diff --git a/tests/auto/qml/qmlformat/data/forOf.formatted.qml b/tests/auto/qml/qmlformat/data/forOf.formatted.qml index fa9ff9c631..0cc4f9fecd 100644 --- a/tests/auto/qml/qmlformat/data/forOf.formatted.qml +++ b/tests/auto/qml/qmlformat/data/forOf.formatted.qml @@ -3,6 +3,7 @@ import QtQml 2.0 QtObject { Component.onCompleted: { var list = [[1, 2], [3, 4], [5, 6]]; + for (const [x, y] of list) console.log("X: " + x + "; Y: " + y); for (let [x, y] of list) diff --git a/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml b/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml index d452ba2b8c..91f520b5fc 100644 --- a/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml +++ b/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml @@ -6,8 +6,7 @@ Item { } function test2() { } - Button { - } + Button {} function test4() { } diff --git a/tests/auto/qml/qmlformat/data/importStatements.formatted.qml b/tests/auto/qml/qmlformat/data/importStatements.formatted.qml new file mode 100644 index 0000000000..6613becaca --- /dev/null +++ b/tests/auto/qml/qmlformat/data/importStatements.formatted.qml @@ -0,0 +1,8 @@ +import QtQml + +import QtQuick +import QtQuick.Controls + +import org.test.module + +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/importStatements.qml b/tests/auto/qml/qmlformat/data/importStatements.qml new file mode 100644 index 0000000000..efe1872e93 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/importStatements.qml @@ -0,0 +1,12 @@ +import QtQml + + +import QtQuick +import QtQuick.Controls + +import org.test.module + + + +QtObject { +} diff --git a/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml b/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml new file mode 100644 index 0000000000..09ab9454e1 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml @@ -0,0 +1,6 @@ +Item { + block1: { + console.log("Hello, world!"); + } + emptyBlock: {} +} diff --git a/tests/auto/qml/qmlformat/data/javascriptBlock.qml b/tests/auto/qml/qmlformat/data/javascriptBlock.qml new file mode 100644 index 0000000000..1665c81f42 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/javascriptBlock.qml @@ -0,0 +1,5 @@ +Item { + block1: {console.log("Hello, world!");} + emptyBlock: { + } +} diff --git a/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js new file mode 100644 index 0000000000..62a5e36f5e --- /dev/null +++ b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js @@ -0,0 +1,6 @@ +var b = function () { + var a = 1; + for (var ii = 1; ii < 10; ++ii) { + a = a * ii; + } +}; diff --git a/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js new file mode 100644 index 0000000000..0155cff7f9 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js @@ -0,0 +1 @@ +var b=function(){var a=1;for(var ii=1;ii<10;++ii){a=a*ii;}} diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js new file mode 100644 index 0000000000..3bc7f683c4 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js @@ -0,0 +1,7 @@ +var f = function () { + if (true) { + console.log("true"); + } else { + console.log("false"); + } +}; diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElse.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.js new file mode 100644 index 0000000000..aaf731e5f0 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.js @@ -0,0 +1 @@ +var f = function(){if(true){ console.log("true");} else {console.log("false");}} diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js new file mode 100644 index 0000000000..99db6fb372 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js @@ -0,0 +1,9 @@ +var l = function () { + var nl = function () { + if (true) { + console.log("true"); + } else { + console.log("false"); + } + }; +}; diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js new file mode 100644 index 0000000000..23ae087b04 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js @@ -0,0 +1 @@ +var l = function(){var nl = function(){if(true){console.log("true");}else{console.log("false");}};}; diff --git a/tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js b/tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js new file mode 100644 index 0000000000..cd7d75314c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js @@ -0,0 +1,4 @@ +if (((typeof ezJsu !== 'undefined') && ezJsu === true) || ((typeof _ez_sa !== 'undefined') && _ez_sa === true)) { + a.defaultStoreUrl = '//g.ezoic.net/ezoic/imp.gif'; + a.defaultStoreA = '//g.ezoic.net/ezoic/i.gif';// Single Line Comments here +} diff --git a/tests/auto/qml/qmlformat/data/messyIfStatement.js b/tests/auto/qml/qmlformat/data/messyIfStatement.js new file mode 100644 index 0000000000..cdd4c7bd9d --- /dev/null +++ b/tests/auto/qml/qmlformat/data/messyIfStatement.js @@ -0,0 +1,2 @@ +if (((typeof ezJsu!=='undefined')&&ezJsu===true)||((typeof _ez_sa !=='undefined')&&_ez_sa === true)) {a.defaultStoreUrl='//g.ezoic.net/ezoic/imp.gif';a.defaultStoreA ='//g.ezoic.net/ezoic/i.gif';// Single Line Comments here +} diff --git a/tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs b/tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs new file mode 100644 index 0000000000..37f8ccb698 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs @@ -0,0 +1,20 @@ +//Imports +import defaultExport from "module-name"; +import "module-name"; +export class ClassName { + constructor(h){ + this.h = h; + } +} +export const { + n1, + n3, + n4, + name2: bar +} = o; +export const [name1, name2] = array; +function stuff() { + var l = () => { + 1 + 1; + }; +} diff --git a/tests/auto/qml/qmlformat/data/mini_esm.mjs b/tests/auto/qml/qmlformat/data/mini_esm.mjs new file mode 100644 index 0000000000..9d6c2406dc --- /dev/null +++ b/tests/auto/qml/qmlformat/data/mini_esm.mjs @@ -0,0 +1,3 @@ +//Imports +import defaultExport from "module-name";import "module-name";export class ClassName{constructor(h){this.h=h;}} +export const {n1,n3,n4,name2:bar}=o;export const [name1,name2]=array;function stuff(){var l=()=>{1+1};}; diff --git a/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml b/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml index 45d04c5887..46e3e963ea 100644 --- a/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml +++ b/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml @@ -1,11 +1,9 @@ Item { - Item { - } + Item {} /* This is a multiline comment. it should stay attached to Commented instead of getting orphaned. */ // This should also stick to Commented - Commented { - } + Commented {} } diff --git a/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml b/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml index 5536ecf513..fc1915f647 100644 --- a/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml +++ b/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml @@ -1,11 +1,13 @@ Item { function a() { function nested() {} + foo(); } function b() { function nested() {} + bar(); } } diff --git a/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml b/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml index 4ff5a40a23..ebb125f36d 100644 --- a/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml +++ b/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml @@ -26,6 +26,7 @@ Item { x(); } } + if (x && y) if (x < y) return 0; diff --git a/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml b/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml new file mode 100644 index 0000000000..94e97076b1 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml @@ -0,0 +1,137 @@ +import QtQml + +QtObject { + + function set1() { + const array = [1, 2, 3, 4]; + const [a, b] = [1, 2]; + const [aa, , bb] = array; + const [aaa = 23, bbb] = array; + const [a1, b1, ...rest1] = array; + const [a2, , b2, ...rest2] = array; + const [a3, b3, ...{ + pop, + push + }] = array; + const [a4, b4, ...[c, d]] = array; + + const obj = { + _a: 1, + _b: 2 + }; + const { + a5, + b5 + } = obj; + const { + a6: a_, + b6: b1_ + } = obj; + const { + a7: a11 = 4, + b11 = 34, + c1: b111, + d1 + } = obj; + let key = a; + const { + [key]: a___ + } = obj; + } + + function set2() { + // declare first + let a, b, a1, b1, c, d, rest, pop, push; + const array = [1, 2, 3, 4]; + [a, b] = array; + [a, , b] = array; + [a = aDefault, b] = array; + [a, b, ...rest] = array; + [a, , b, ...rest] = array; + [a, b, ...{ + pop, + push + }] = array; + [a, b, ...[c, d]] = array; + + const obj = { + _a: 1, + _b: 2 + }; + ({ + a, + b + } = obj); // brackets are required + ({ + a: a1, + b: b1 + } = obj); + + const complicatedObject = { + a: 1, + b: { + c: 2, + d: { + e: 3, + f: [4, 5, 6] + } + }, + g: [7, 8, 9] + }; + + const { + patron, + b: { + mafik, + d: { + e, + f: [, secondF, ...restF] + } + }, + g: [firstG, ...restG] + } = complicatedObject; + } + + Component.onCompleted: { + const myFunction = myLambda => { + const myObject = { + a: 1, + b: { + c: 2, + d: [3, 4, 5] + }, + e: { + f: 6, + g: { + h: 7, + i: [8, 9, 10] + } + } + }; + + myLambda(myObject); + }; + + myFunction(({ + a, + b: { + c, + d: [firstD] + }, + e: { + f, + g: { + h, + i: [, secondI] + } + } + }) => { + console.log(a); // 1 + console.log(c); // 2 + console.log(firstD); // 3 + console.log(f); // 6 + console.log(h); // 7 + console.log(secondI); // 9 + }); + } +} diff --git a/tests/auto/qml/qmlformat/data/objectDestructuring.qml b/tests/auto/qml/qmlformat/data/objectDestructuring.qml new file mode 100644 index 0000000000..0487153125 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/objectDestructuring.qml @@ -0,0 +1,77 @@ +import QtQml + +QtObject { + + function set1() { + const array = [1,2,3,4]; + const [a, b] = [1,2]; + const [aa, , bb] = array; + const [aaa = 23, bbb] = array; + const [a1, b1, ...rest1] = array; + const [a2, , b2, ...rest2] = array; + const [a3, b3, ...{ pop, push }] = array; + const [a4, b4, ...[c, d]] = array; + + const obj = {_a:1,_b:2}; + const { a5, b5 } = obj; + const { a6: a_, b6: b1_ } = obj; + const { a7: a11 = 4, b11 = 34, c1: b111, d1 } = obj; + let key = a; + const { [key]: a___ } = obj; + } + + function set2() { + // declare first + let a, b, a1, b1, c, d, rest, pop, push; + const array = [1,2,3,4]; + [a, b] = array; + [a, , b] = array; + [a = aDefault, b] = array; + [a, b, ...rest] = array; + [a, , b, ...rest] = array; + [a, b, ...{ pop, push }] = array; + [a, b, ...[c, d]] = array; + + const obj = {_a:1,_b:2}; + ({ a, b } = obj); // brackets are required + ({ a: a1, b: b1 } = obj); + + const complicatedObject = { + a: 1, + b: { + c: 2, + d: { + e: 3, + f: [4, 5, 6] + } + }, + g: [7, 8, 9] + }; + + const { patron, b: { mafik, d: { e, f: [ , secondF, ...restF ] } }, g: [ firstG, ...restG ] } = complicatedObject; + } + + Component.onCompleted: { + const myFunction = (myLambda) => { + const myObject = { + a: 1, + b: {c: 2, d: [3, 4, 5] }, e: { + f: 6, + g: { h: 7, i: [8, 9, 10] + } + } + }; + + myLambda(myObject); + }; + + myFunction(({ a, b: { c, d: [ firstD ] }, e: { f, g: { h, i: [ , secondI ] } } }) => { + console.log(a); // 1 + console.log(c); // 2 + console.log(firstD); // 3 + console.log(f); // 6 + console.log(h); // 7 + console.log(secondI); // 9 + }); + } +} diff --git a/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml b/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml index bd0406e595..df26a9b599 100644 --- a/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml +++ b/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml @@ -1,6 +1,9 @@ Item { + Button {} + Button { + id: foo } height: 360 diff --git a/tests/auto/qml/qmlformat/data/objectsSpacing.qml b/tests/auto/qml/qmlformat/data/objectsSpacing.qml index 6adc89778c..0239b05145 100644 --- a/tests/auto/qml/qmlformat/data/objectsSpacing.qml +++ b/tests/auto/qml/qmlformat/data/objectsSpacing.qml @@ -3,6 +3,10 @@ Item { Button { } + Button { + id: foo + } + height: 360 width: 360 diff --git a/tests/auto/qml/qmlformat/data/pragma.formatted.js b/tests/auto/qml/qmlformat/data/pragma.formatted.js new file mode 100644 index 0000000000..3f57e537ac --- /dev/null +++ b/tests/auto/qml/qmlformat/data/pragma.formatted.js @@ -0,0 +1,4 @@ +"use strict"; +{ + function f() {} +} diff --git a/tests/auto/qml/qmlformat/data/pragma.formatted.qml b/tests/auto/qml/qmlformat/data/pragma.formatted.qml new file mode 100644 index 0000000000..143db39888 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/pragma.formatted.qml @@ -0,0 +1,8 @@ +pragma Singleton +pragma ComponentBehavior: Bound +pragma FunctionSignatureBehavior: Enforced +pragma ValueTypeBehavior: Copy, Addressable + +import QtQml + +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/pragma.js b/tests/auto/qml/qmlformat/data/pragma.js new file mode 100644 index 0000000000..b81d52d853 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/pragma.js @@ -0,0 +1 @@ +"use strict";{function f(){}} diff --git a/tests/auto/qml/qmlformat/data/pragma.qml b/tests/auto/qml/qmlformat/data/pragma.qml new file mode 100644 index 0000000000..7469277395 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/pragma.qml @@ -0,0 +1,8 @@ +pragma Singleton + pragma ComponentBehavior: Bound + pragma FunctionSignatureBehavior: Enforced +pragma ValueTypeBehavior: Copy, Addressable + +import QtQml + +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml b/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml index 94b5877957..9214014889 100644 --- a/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml +++ b/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml @@ -3,11 +3,11 @@ Item { var copiedItem = "copied value"; var computedItem = "computedName"; var obj = { - "identifierName": "identifier value", + identifierName: "identifier value", "string name": "string value", - "Infinity": "numeric value", + Infinity: "numeric value", [computedItem]: "computed value", - "copiedItem": copiedItem + copiedItem }; } } diff --git a/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml b/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml index b9bcc34e8c..767bfe7e61 100644 --- a/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml +++ b/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml @@ -1 +1 @@ -/* This file is licensed under the not a license license
1. You may not comply
2. Goodbye
*/
// Importing this is very important
import QtQuick 5.15
// Muddling the waters!
import QtQuick.Models 3.14 as muddle
// Importing that is important too
import Z
import That
import This // THIS IS VERY IMPORTANT!
import Y
import X.Z
import X.Y
import A.LLOHA
import A.B.B.A
// This comment is related to Item
Item {
// This to id
// Also id. (line 2)
// This is the third id
// fourth id comment
id: foo
// This to enum
enum Foo {
A = 3, // This is A
B, // This is B
C = 4, // This is C
D // This is D
}
// Orphan comment
// Another orphan
// More orphans
property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
property bool some_bool: false
default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool
// some_read_only_bool
readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0
property bool something_computed: function (x) {
const PI = 3, DAYS_PER_YEAR = 365.25;
var x = 3 + 2;
x["bla"] = 50;
// This is an orphan inside something_computed
// Are these getting duplicated?
// This one to var few!
var few = new WhatEver();
x += Math.sin(3);
x--;
--x;
x++;
++x;
for (var x = 0; x < 100; x++) {
x++;
console.log("Foo");
}
for (var x in [3, 2, 1]) {
y++;
console.log("Bar");
}
while (true) {
console.log("Wee");
}
with (foo) {
bar;
x += 5;
} // This is related to with!
x3: do {
console.log("Hello");
} while (3 == 0)
try {
dangerous();
} catch (e) {
console.log(e);
} finally {
console.log("What else?");
}
switch (x) {
case 0:
x = 1;
break;
case 1:
x = 5;
break;
case 4:
x = 100;
break;
}
if (x == 50) {
console.log("true");
} else if (x == 50) {
console.log("other thing");
} else {
console.log("false");
}
if (x == 50) {
console.log("true");
} else if (x == 50) {
console.log("other thing");
x--;
} else {
console.log("false");
}
// Another orphan inside something_computed
return "foobar";
}()
signal say(string name, bool caps)
// This one to aFunc()
function aFunc() {
var x = 3;
return x;
}
x: 3 // Very cool
myFavouriteThings: [
// This is an orphan
// This is a cool text
Text {
},
// This is a cool rectangle
Rectangle {
}
]
// This comment is related to the property animation
PropertyAnimation on x {
id: foo2
x: 3
y: x + 3
}
Component.onCompleted: console.log("Foo!")
Text {
required property string batman
signal boo(int count, int times, real duration)
text: "Bla"
}
}
\ No newline at end of file +/* This file is licensed under the not a license license
1. You may not comply
2. Goodbye
*/
// Importing this is very important
import QtQuick 5.15
// Muddling the waters!
import QtQuick.Models 3.14 as muddle
// Importing that is important too
import Z
import That
import This // THIS IS VERY IMPORTANT!
import Y
import X.Z
import X.Y
import A.LLOHA
import A.B.B.A
// This comment is related to Item
Item {
// This to id
// Also id. (line 2)
// This is the third id
// fourth id comment
id: foo
// This to enum
enum Foo {
A = 3, // This is A
B, // This is B
C = 4, // This is C
D // This is D
}
// Orphan comment
// Another orphan
// More orphans
property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
property bool some_bool: false
default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool
// some_read_only_bool
readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0
property bool something_computed: function (x) {
const PI = 3, DAYS_PER_YEAR = 365.25;
var x = 3 + 2;
x["bla"] = 50;
// This is an orphan inside something_computed
// Are these getting duplicated?
// This one to var few!
var few = new WhatEver();
x += Math.sin(3);
x--;
--x;
x++;
++x;
for (var x = 0; x < 100; x++) {
x++;
console.log("Foo");
}
for (var x in [3, 2, 1]) {
y++;
console.log("Bar");
}
while (true) {
console.log("Wee");
}
with (foo) {
bar;
x += 5;
} // This is related to with!
x3: do {
console.log("Hello");
} while (3 == 0)
try {
dangerous();
} catch (e) {
console.log(e);
} finally {
console.log("What else?");
}
switch (x) {
case 0:
x = 1;
break;
case 1:
x = 5;
break;
case 4:
x = 100;
break;
}
if (x == 50) {
console.log("true");
} else if (x == 50) {
console.log("other thing");
} else {
console.log("false");
}
if (x == 50) {
console.log("true");
} else if (x == 50) {
console.log("other thing");
x--;
} else {
console.log("false");
}
// Another orphan inside something_computed
return "foobar";
}()
signal say(string name, bool caps)
// This one to aFunc()
function aFunc() {
var x = 3;
return x;
}
x: 3 // Very cool
myFavouriteThings: [
// This is an orphan
// This is a cool text
Text {
},
// This is a cool rectangle
Rectangle {
}
]
// This comment is related to the property animation
PropertyAnimation on x {
id: foo2
x: 3
y: x + 3
}
Component.onCompleted: console.log("Foo!")
Text {
required property string batman
signal boo(int count, int times, real duration)
text: "Bla"
}
}
\ No newline at end of file diff --git a/tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js b/tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js new file mode 100644 index 0000000000..fbdec1038c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js @@ -0,0 +1 @@ +let v = 1; diff --git a/tests/auto/qml/qmlformat/data/simpleJSStatement.js b/tests/auto/qml/qmlformat/data/simpleJSStatement.js new file mode 100644 index 0000000000..d954a87fc0 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/simpleJSStatement.js @@ -0,0 +1 @@ +let v=1; diff --git a/tests/auto/qml/qmlformat/data/simpleLoop.formatted.js b/tests/auto/qml/qmlformat/data/simpleLoop.formatted.js new file mode 100644 index 0000000000..435fc145bf --- /dev/null +++ b/tests/auto/qml/qmlformat/data/simpleLoop.formatted.js @@ -0,0 +1,4 @@ +var a = 1; +for (var ii = 1; ii < 10; ++ii) { + a = a * ii; +} diff --git a/tests/auto/qml/qmlformat/data/simpleLoop.js b/tests/auto/qml/qmlformat/data/simpleLoop.js new file mode 100644 index 0000000000..08b172991f --- /dev/null +++ b/tests/auto/qml/qmlformat/data/simpleLoop.js @@ -0,0 +1 @@ +var a=1;for(var ii=1;ii<10;++ii){a=a*ii;} diff --git a/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js new file mode 100644 index 0000000000..d5b34291e6 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js @@ -0,0 +1,4 @@ +function f() { + let a = 0; + return a; +} diff --git a/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js new file mode 100644 index 0000000000..c49be5e2bd --- /dev/null +++ b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js @@ -0,0 +1 @@ +function f(){let a=0;return a; } diff --git a/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml b/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml index 40cf5068da..923f0642d7 100644 --- a/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml +++ b/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml @@ -2,15 +2,12 @@ QtObject { id: foo states: [ - State { - } + State {} ] transitions: [ - Transition { - } + Transition {} ] // This needs to be *before* states and transitions after formatting - Item { - } + Item {} } diff --git a/tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js b/tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js new file mode 100644 index 0000000000..592db7a02b --- /dev/null +++ b/tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js @@ -0,0 +1,11 @@ +function one() { + var a=1; +} + +function two(a, b) { + console.log(a,b); +} + +function three(c) { + var a=c; +} diff --git a/tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js b/tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js new file mode 100644 index 0000000000..6575a9fbd8 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js @@ -0,0 +1,9 @@ +function one() { + var a = 1; +} +function two(a, b) { + console.log(a, b); +} +function three(c) { + var a = c; +} diff --git a/tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js b/tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js new file mode 100644 index 0000000000..afcf8acf02 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js @@ -0,0 +1,9 @@ +function one() { + var a = 1; +} +function two(a, b) { + console.log(a, b); +} +function three(c) { + var a = c; +} diff --git a/tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js b/tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js new file mode 100644 index 0000000000..648591f221 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js @@ -0,0 +1 @@ +function one() {var a=1;}function two(a, b) {console.log(a,b);}function three(c){var a=c;} diff --git a/tests/auto/qml/qmlformat/data/twoFunctions.formatted.js b/tests/auto/qml/qmlformat/data/twoFunctions.formatted.js new file mode 100644 index 0000000000..b7414de053 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/twoFunctions.formatted.js @@ -0,0 +1,12 @@ +function one() { + var a = 1; + if (a === true) { + a = 5; + } +} + +function two(a, b) { + for (; b < 5; ++b) { + a = a * b; + } +} diff --git a/tests/auto/qml/qmlformat/data/twoFunctions.js b/tests/auto/qml/qmlformat/data/twoFunctions.js new file mode 100644 index 0000000000..b410ebd167 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/twoFunctions.js @@ -0,0 +1,3 @@ +function one(){var a=1;if (a===true){a=5;}} + +function two(a,b){for(;b<5;++b){a=a*b;}} diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp index c14dd794fc..11d4c9d2c7 100644 --- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp +++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QDir> @@ -16,6 +16,25 @@ using namespace QQmlJS::Dom; +// TODO refactor extension helpers +const QString QML_EXT = ".qml"; +const QString JS_EXT = ".js"; +const QString MJS_EXT = ".mjs"; + +static QStringView fileExt(QStringView filename) +{ + if (filename.endsWith(QML_EXT)) { + return QML_EXT; + } + if (filename.endsWith(JS_EXT)) { + return JS_EXT; + } + if (filename.endsWith(MJS_EXT)) { + return MJS_EXT; + } + Q_UNREACHABLE(); +}; + class TestQmlformat: public QQmlDataTest { Q_OBJECT @@ -27,6 +46,7 @@ public: private Q_SLOTS: void initTestCase() override; + //actually testFormat tests CLI of qmlformat void testFormat(); void testFormat_data(); @@ -40,10 +60,19 @@ private Q_SLOTS: void testBackupFileLimit(); + void testFilesOption_data(); + void testFilesOption(); + + void plainJS_data(); + void plainJS(); + + void ecmascriptModule(); + private: QString readTestFile(const QString &path); + //TODO(QTBUG-117849) refactor this helper function QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true, - RunOption rOption = RunOption::OnCopy); + RunOption rOption = RunOption::OnCopy, QStringView ext = QML_EXT); QString formatInMemory(const QString &fileToFormat, bool *didSucceed = nullptr, LineWriterOptions options = LineWriterOptions(), WriteOutChecks extraChecks = WriteOutCheck::ReparseCompare, @@ -52,9 +81,11 @@ private: QString m_qmlformatPath; QStringList m_excludedDirs; QStringList m_invalidFiles; + QStringList m_ignoreFiles; QStringList findFiles(const QDir &); bool isInvalidFile(const QFileInfo &fileName) const; + bool isIgnoredFile(const QFileInfo &fileName) const; }; // Don't fail on warnings because we read a lot of QML files that might intentionally be malformed. @@ -85,6 +116,7 @@ void TestQmlformat::initTestCase() m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding"; m_excludedDirs << "doc/src/snippets/qtquick1/imports"; m_excludedDirs << "tests/manual/v4"; + m_excludedDirs << "tests/manual/qmllsformatter"; m_excludedDirs << "tests/auto/qml/ecmascripttests"; m_excludedDirs << "tests/auto/qml/qmllint"; @@ -126,6 +158,15 @@ void TestQmlformat::initTestCase() m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml"; m_invalidFiles << "tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml"; m_invalidFiles << "tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/emptyFile.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/completions/afterDots.qml"; + m_invalidFiles << "tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml"; + m_invalidFiles << "tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml"; + m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.qml"; // Files that get changed: // rewrite of import "bla/bla/.." to import "bla" @@ -145,6 +186,10 @@ void TestQmlformat::initTestCase() m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml"; m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml"; + + // These files are too big + m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/longQmlFile.qml"; + m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/deeplyNested.qml"; } QStringList TestQmlformat::findFiles(const QDir &d) @@ -157,15 +202,17 @@ QStringList TestQmlformat::findFiles(const QDir &d) QStringList rv; - QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), - QDir::Files); - foreach (const QString &file, files) { - rv << d.absoluteFilePath(file); + const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), + QDir::Files); + for (const QString &file: files) { + QString absoluteFilePath = d.absoluteFilePath(file); + if (!isIgnoredFile(QFileInfo(absoluteFilePath))) + rv << absoluteFilePath; } - QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | - QDir::NoSymLinks); - foreach (const QString &dir, dirs) { + const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + for (const QString &dir: dirs) { QDir sub = d; sub.cd(dir); rv << findFiles(sub); @@ -183,6 +230,15 @@ bool TestQmlformat::isInvalidFile(const QFileInfo &fileName) const return false; } +bool TestQmlformat::isIgnoredFile(const QFileInfo &fileName) const +{ + for (const QString &file : m_ignoreFiles) { + if (fileName.absoluteFilePath().endsWith(file)) + return true; + } + return false; +} + QString TestQmlformat::readTestFile(const QString &path) { QFile file(testFile(path)); @@ -304,6 +360,60 @@ void TestQmlformat::testFormat_data() QTest::newRow("ecmaScriptClassInQml") << "ecmaScriptClassInQml.qml" << "ecmaScriptClassInQml.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("arrowFunctionWithBinding") + << "arrowFunctionWithBinding.qml" + << "arrowFunctionWithBinding.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("blanklinesAfterComment") + << "blanklinesAfterComment.qml" + << "blanklinesAfterComment.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("pragmaValueList") + << "pragma.qml" + << "pragma.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("objectDestructuring") + << "objectDestructuring.qml" + << "objectDestructuring.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("destructuringFunctionParameter") + << "destructuringFunctionParameter.qml" + << "destructuringFunctionParameter.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("ellipsisFunctionArgument") + << "ellipsisFunctionArgument.qml" + << "ellipsisFunctionArgument.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("importStatements") + << "importStatements.qml" + << "importStatements.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("arrayEndComma") + << "arrayEndComma.qml" + << "arrayEndComma.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("escapeChars") + << "escapeChars.qml" + << "escapeChars.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("javascriptBlock") + << "javascriptBlock.qml" + << "javascriptBlock.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("enumWithValues") + << "enumWithValues.qml" + << "enumWithValues.formatted.qml" << QStringList{} << RunOption::OnCopy; + //plainJS + QTest::newRow("nestedLambdaWithIfElse") + << "lambdaWithIfElseInsideLambda.js" + << "lambdaWithIfElseInsideLambda.formatted.js" << QStringList{} << RunOption::OnCopy; + + QTest::newRow("indentEquals2") + << "threeFunctionsOneLine.js" + << "threeFunctions.formattedW2.js" << QStringList{"-w=2"} << RunOption::OnCopy; + + QTest::newRow("tabIndents") + << "threeFunctionsOneLine.js" + << "threeFunctions.formattedTabs.js" << QStringList{"-t"} << RunOption::OnCopy; + + QTest::newRow("normalizedFunctionSpacing") + << "threeFunctionsOneLine.js" + << "threeFunctions.formattedFuncSpacing.js" + << QStringList{ "-n", "--functions-spacing" } << RunOption::OnCopy; + + QTest::newRow("esm_tabIndents") + << "mini_esm.mjs" + << "mini_esm.formattedTabs.mjs" << QStringList{ "-t" } << RunOption::OnCopy; } void TestQmlformat::testFormat() @@ -313,7 +423,82 @@ void TestQmlformat::testFormat() QFETCH(QStringList, args); QFETCH(RunOption, runOption); - QCOMPARE(runQmlformat(testFile(file), args, true, runOption), readTestFile(fileFormatted)); + auto formatted = runQmlformat(testFile(file), args, true, runOption, fileExt(file)); + QEXPECT_FAIL("normalizedFunctionSpacing", + "Normalize && function spacing are not yet supported for JS", Abort); + auto exp = readTestFile(fileFormatted); + QCOMPARE(formatted, exp); +} + +void TestQmlformat::plainJS_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QString>("fileFormatted"); + + QTest::newRow("simpleStatement") << "simpleJSStatement.js" + << "simpleJSStatement.formatted.js"; + QTest::newRow("simpleFunction") << "simpleOnelinerJSFunc.js" + << "simpleOnelinerJSFunc.formatted.js"; + QTest::newRow("simpleLoop") << "simpleLoop.js" + << "simpleLoop.formatted.js"; + QTest::newRow("messyIfStatement") << "messyIfStatement.js" + << "messyIfStatement.formatted.js"; + QTest::newRow("lambdaFunctionWithLoop") << "lambdaFunctionWithLoop.js" + << "lambdaFunctionWithLoop.formatted.js"; + QTest::newRow("lambdaWithIfElse") << "lambdaWithIfElse.js" + << "lambdaWithIfElse.formatted.js"; + QTest::newRow("nestedLambdaWithIfElse") << "lambdaWithIfElseInsideLambda.js" + << "lambdaWithIfElseInsideLambda.formatted.js"; + QTest::newRow("twoFunctions") << "twoFunctions.js" + << "twoFunctions.formatted.js"; + QTest::newRow("pragma") << "pragma.js" + << "pragma.formatted.js"; + QTest::newRow("classConstructor") << "class.js" + << "class.formatted.js"; + QTest::newRow("legacyDirectives") << "directives.js" + << "directives.formatted.js"; + QTest::newRow("legacyDirectivesWithComments") << "directivesWithComments.js" + << "directivesWithComments.formatted.js"; +} + +void TestQmlformat::plainJS() +{ + QFETCH(QString, file); + QFETCH(QString, fileFormatted); + + bool wasSuccessful; + LineWriterOptions opts; +#ifdef Q_OS_WIN + opts.lineEndings = QQmlJS::Dom::LineWriterOptions::LineEndings::Windows; +#endif + QString output = formatInMemory(testFile(file), &wasSuccessful, opts, WriteOutCheck::None); + + QVERIFY(wasSuccessful && !output.isEmpty()); + + // TODO(QTBUG-119404) + QEXPECT_FAIL("classConstructor", "see QTBUG-119404", Abort); + // TODO(QTBUG-119770) + QEXPECT_FAIL("legacyDirectivesWithComments", "see QTBUG-119770", Abort); + auto exp = readTestFile(fileFormatted); + QCOMPARE(output, exp); +} + +void TestQmlformat::ecmascriptModule() +{ + QString file("esm.mjs"); + QString formattedFile("esm.formatted.mjs"); + + bool wasSuccessful; + LineWriterOptions opts; +#ifdef Q_OS_WIN + opts.lineEndings = QQmlJS::Dom::LineWriterOptions::LineEndings::Windows; +#endif + QString output = formatInMemory(testFile(file), &wasSuccessful, opts, WriteOutCheck::None); + + QVERIFY(wasSuccessful && !output.isEmpty()); + + auto exp = readTestFile(formattedFile); + QCOMPARE(output, readTestFile(formattedFile)); } #if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled @@ -326,9 +511,23 @@ void TestQmlformat::testExample_data() QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; QString tests = QLatin1String(SRCDIR) + "/../../../../tests/"; + QStringList exampleFiles; + QStringList testFiles; QStringList files; - files << findFiles(QDir(examples)); - files << findFiles(QDir(tests)); + exampleFiles << findFiles(QDir(examples)); + testFiles << findFiles(QDir(tests)); + + // Actually this test is an e2e test and not the unit test. + // At the moment of writing, CI lacks providing instruments for the automated tests + // which might be time-consuming, as for example this one. + // Therefore as part of QTBUG-122990 this test was copied to the /manual/e2e/qml/qmlformat + // however very small fraction of the test data is still preserved here for the sake of + // testing automatically at least a small part of the examples + const int nBatch = 10; + files << exampleFiles.mid(0, nBatch) << exampleFiles.mid(exampleFiles.size() / 2, nBatch) + << exampleFiles.mid(exampleFiles.size() - nBatch, nBatch); + files << testFiles.mid(0, nBatch) << testFiles.mid(exampleFiles.size() / 2, nBatch) + << testFiles.mid(exampleFiles.size() - nBatch, nBatch); for (const QString &file : files) QTest::newRow(qPrintable(file)) << file; @@ -417,12 +616,79 @@ void TestQmlformat::testBackupFileLimit() }; } +void TestQmlformat::testFilesOption_data() +{ + QTest::addColumn<QString>("containerFile"); + QTest::addColumn<QStringList>("individualFiles"); + + QTest::newRow("initial") << "fileListToFormat" + << QStringList{"valid1.qml", "invalidEntry:cannot be parsed", "valid2.qml"}; +} + +void TestQmlformat::testFilesOption() +{ + QFETCH(QString, containerFile); + QFETCH(QStringList, individualFiles); + + // Create a temporary directory + QTemporaryDir tempDir; + tempDir.setAutoRemove(false); + QStringList actualFormattedFilesPath; + + // Iterate through files in the source directory and copy them to the temporary directory + const auto sourceDir = dataDirectory() + QDir::separator() + "filesOption"; + + // Create a file that contains the list of files to be formatted + const QString tempFilePath = tempDir.path() + QDir::separator() + containerFile; + QFile container(tempFilePath); + if (container.open(QIODevice::Text | QIODevice::WriteOnly)) { + QTextStream out(&container); + + for (const auto &file : individualFiles) { + QString destinationFilePath = tempDir.path() + QDir::separator() + file; + if (QFile::copy(sourceDir + QDir::separator() + file, destinationFilePath)) + actualFormattedFilesPath << destinationFilePath; + out << destinationFilePath << "\n"; + } + + container.close(); + } else { + QFAIL("Cannot create temp test file\n"); + return; + } + + { + QProcess process; + process.start(m_qmlformatPath, QStringList{"-F", tempFilePath}); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + } + + const auto readFile = [](const QString &filePath){ + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "Error on opening the file " << filePath; + return QByteArray{}; + } + + return file.readAll(); + }; + + for (const auto &filePath : actualFormattedFilesPath) { + auto expectedFormattedFile = QFileInfo(filePath).fileName(); + const auto expectedFormattedFilePath = sourceDir + QDir::separator() + + expectedFormattedFile.replace(".qml", ".formatted.qml"); + + QCOMPARE(readFile(filePath), readFile(expectedFormattedFilePath)); + } +} + QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList args, - bool shouldSucceed, RunOption rOptions) + bool shouldSucceed, RunOption rOptions, QStringView ext) { // Copy test file to temporary location QTemporaryDir tempDir; - const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml"; + const QString tempFile = (tempDir.path() + QDir::separator() + "to_format") % ext; if (rOptions == RunOption::OnCopy) { QFile::copy(fileToFormat, tempFile); @@ -446,7 +712,8 @@ QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList arg QFile temp(tempFile); - temp.open(QIODevice::ReadOnly); + if (!temp.open(QIODevice::ReadOnly)) + qFatal("Could not open %s", qPrintable(tempFile)); QString formatted = QString::fromUtf8(temp.readAll()); return formatted; @@ -456,19 +723,17 @@ QString TestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucc LineWriterOptions options, WriteOutChecks extraChecks, WriteOutChecks largeChecks) { - DomItem env = DomEnvironment::create( + auto env = DomEnvironment::create( QStringList(), // as we load no dependencies we do not need any paths QQmlJS::Dom::DomEnvironment::Option::SingleThreaded | QQmlJS::Dom::DomEnvironment::Option::NoDependencies); DomItem tFile; - env.loadFile( - fileToFormat, QString(), - [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; }, - LoadOption::DefaultLoad); - env.loadPendingDependencies(); + env->loadFile(FileToLoad::fromFileSystem(env, fileToFormat), + [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; }); + env->loadPendingDependencies(); MutableDomItem myFile = tFile.field(Fields::currentItem); - DomItem writtenOut; + bool writtenOut; QString resultStr; if (myFile.field(Fields::isValid).value().toBool()) { WriteOutChecks checks = extraChecks; @@ -486,7 +751,7 @@ QString TestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucc res.flush(); } if (didSucceed) - *didSucceed = bool(writtenOut); + *didSucceed = writtenOut; return resultStr; } diff --git a/tests/auto/qml/qmlimportscanner/CMakeLists.txt b/tests/auto/qml/qmlimportscanner/CMakeLists.txt index 042e8fd3d5..a1a22d7309 100644 --- a/tests/auto/qml/qmlimportscanner/CMakeLists.txt +++ b/tests/auto/qml/qmlimportscanner/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmlimportscanner Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlimportscanner LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json index a688511ed0..8860b629d1 100644 --- a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json +++ b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json @@ -10,29 +10,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -40,9 +36,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -50,8 +46,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json index 084e4354b3..085d0bf6d4 100644 --- a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json +++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json @@ -5,24 +5,20 @@ "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -30,9 +26,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -40,8 +36,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json index c50fe5ab51..c0cad1e3a1 100644 --- a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json +++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json @@ -10,29 +10,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -40,9 +36,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -50,8 +46,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json b/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json index 447c664785..3abcd58b1c 100644 --- a/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json @@ -1,23 +1,19 @@ [ { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -25,9 +21,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -35,8 +31,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/Imports.json b/tests/auto/qml/qmlimportscanner/data/Imports.json index 5e23e7b1ad..ee6903b1da 100644 --- a/tests/auto/qml/qmlimportscanner/data/Imports.json +++ b/tests/auto/qml/qmlimportscanner/data/Imports.json @@ -10,29 +10,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -40,9 +36,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -50,8 +46,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json index 06df9295fe..0e733b551f 100644 --- a/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json @@ -1,39 +1,29 @@ [ { "classname": "QtQuick2Plugin", - "name": "QtQuick", "linkTarget": "Qt6::qtquick2plugin", + "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" - }, - { - "name": "Things", - "plugin": "doesNotExistPlugin", - "relativePath": "Things", "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -41,9 +31,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -51,8 +41,14 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" + }, + { + "name": "Things", + "plugin": "doesNotExistPlugin", + "relativePath": "Things", + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json index 213798d04d..d0d427f595 100644 --- a/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json +++ b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json @@ -5,29 +5,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -35,9 +31,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -45,8 +41,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/Simple.qml.json b/tests/auto/qml/qmlimportscanner/data/Simple.qml.json index 213798d04d..d0d427f595 100644 --- a/tests/auto/qml/qmlimportscanner/data/Simple.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/Simple.qml.json @@ -5,29 +5,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -35,9 +31,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -45,8 +41,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/Singleton.json b/tests/auto/qml/qmlimportscanner/data/Singleton.json index cb24e57f60..1813d01efe 100644 --- a/tests/auto/qml/qmlimportscanner/data/Singleton.json +++ b/tests/auto/qml/qmlimportscanner/data/Singleton.json @@ -10,29 +10,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -40,9 +36,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -50,8 +46,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/Things.json b/tests/auto/qml/qmlimportscanner/data/Things.json index ee5caecfda..a6081c89d8 100644 --- a/tests/auto/qml/qmlimportscanner/data/Things.json +++ b/tests/auto/qml/qmlimportscanner/data/Things.json @@ -11,29 +11,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -41,9 +37,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -51,8 +47,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json index 2d2b602fb1..7094d1c8e3 100644 --- a/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json @@ -1,27 +1,19 @@ [ { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", - "name": "QtQml", - "plugin": "qmlmetaplugin", - "pluginIsOptional": true, - "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" - }, - { "classname": "QtQmlPlugin", "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", + "name": "QtQml", "plugin": "qmlplugin", "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "prefer": ":/qt-project.org/imports/QtQml/", + "relativePath": "QtQml", + "type": "module" }, { - "name": "Methods.js", - "type": "javascript" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -29,9 +21,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -39,8 +31,12 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" + }, + { + "name": "Methods.js", + "type": "javascript" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/localImport.qml.json b/tests/auto/qml/qmlimportscanner/data/localImport.qml.json index ee5caecfda..a6081c89d8 100644 --- a/tests/auto/qml/qmlimportscanner/data/localImport.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/localImport.qml.json @@ -11,29 +11,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -41,9 +37,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -51,8 +47,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json index 213798d04d..d0d427f595 100644 --- a/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json @@ -5,29 +5,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -35,9 +31,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -45,8 +41,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json index ee5caecfda..a6081c89d8 100644 --- a/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json @@ -11,29 +11,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -41,9 +37,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -51,8 +47,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json index 447c664785..3abcd58b1c 100644 --- a/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json +++ b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json @@ -1,23 +1,19 @@ [ { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -25,9 +21,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -35,8 +31,8 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/data/rootPath.json b/tests/auto/qml/qmlimportscanner/data/rootPath.json index 942c8d6000..41ce0f9a45 100644 --- a/tests/auto/qml/qmlimportscanner/data/rootPath.json +++ b/tests/auto/qml/qmlimportscanner/data/rootPath.json @@ -5,29 +5,25 @@ "name": "QtQuick", "plugin": "qtquick2plugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQuick/", "relativePath": "QtQuick", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQuick/" + "type": "module" }, { - "classname": "QtQmlMetaPlugin", - "linkTarget": "Qt6::QmlMeta", + "classname": "QtQmlPlugin", + "linkTarget": "Qt6::qmlplugin", "name": "QtQml", - "plugin": "qmlmetaplugin", + "plugin": "qmlplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/", "relativePath": "QtQml", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/" + "type": "module" }, { - "classname": "QtQmlPlugin", - "linkTarget": "Qt6::qmlplugin", - "name": "QtQml.Base", - "plugin": "qmlplugin", - "pluginIsOptional": true, - "relativePath": "QtQml/Base", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Base/" + "name": "QML", + "prefer": ":/qt-project.org/imports/QML/", + "relativePath": "QML", + "type": "module" }, { "classname": "QtQmlModelsPlugin", @@ -35,9 +31,9 @@ "name": "QtQml.Models", "plugin": "modelsplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/Models/", "relativePath": "QtQml/Models", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/Models/" + "type": "module" }, { "classname": "QtQmlWorkerScriptPlugin", @@ -45,9 +41,9 @@ "name": "QtQml.WorkerScript", "plugin": "workerscriptplugin", "pluginIsOptional": true, + "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/", "relativePath": "QtQml/WorkerScript", - "type": "module", - "prefer": ":/qt-project.org/imports/QtQml/WorkerScript/" + "type": "module" }, { "name": "QTBUG-45916.js", @@ -68,12 +64,12 @@ "type": "module" }, { - "name": "Imports", - "relativePath": "Imports", + "name": "Module", "type": "module" }, { - "name": "Module", + "name": "Imports", + "relativePath": "Imports", "type": "module" } ] diff --git a/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp b/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp index 02cebc349c..49b3418401 100644 --- a/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp +++ b/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QProcess> @@ -149,7 +149,7 @@ void TestQmlimportscanner::runQmlimportscanner(const QString &mode, const QStrin QVERIFY(generated.isArray()); QFile imports(resultFile); - imports.open(QIODevice::ReadOnly); + QVERIFY(imports.open(QIODevice::ReadOnly)); QJsonDocument expected = QJsonDocument::fromJson(imports.readAll(), &error); QCOMPARE(error.error, QJsonParseError::NoError); QVERIFY(expected.isArray()); diff --git a/tests/auto/qml/qmllint/CMakeLists.txt b/tests/auto/qml/qmllint/CMakeLists.txt index 422e7a08b3..5a3e2d9c0d 100644 --- a/tests/auto/qml/qmllint/CMakeLists.txt +++ b/tests/auto/qml/qmllint/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmllint Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmllint LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -42,9 +48,15 @@ qt_internal_extend_target(tst_qmllint CONDITION NOT ANDROID AND NOT IOS QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) +if (TARGET qmllint) + add_dependencies(tst_qmllint Qt::qmllint) +endif() + if (TARGET qmljsrootgen) qt_internal_extend_target(tst_qmllint DEFINES QT_QMLJSROOTGEN_PRESENT ) + + add_dependencies(tst_qmllint Qt::qmljsrootgen) endif() diff --git a/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml new file mode 100644 index 0000000000..4141884af9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property string myProperty +} diff --git a/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir new file mode 100644 index 0000000000..b6e958d657 --- /dev/null +++ b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir @@ -0,0 +1,2 @@ +module ModuleInImportPath +A 1.0 A.qml diff --git a/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml b/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml new file mode 100644 index 0000000000..8b20fe9ae9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml @@ -0,0 +1,14 @@ +import QtQuick + +Item { + id: item + visible: true + + enum Mode { + Hours, + Minutes + } + + property int mode: item.Mode.Hours + property string s: item.mode === IsNotAnEntryOfEnum.Mode.Hour ? "green" : "tomato" +} diff --git a/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes b/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes new file mode 100644 index 0000000000..b5baa22357 --- /dev/null +++ b/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes @@ -0,0 +1,31 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by qmltyperegistrar. + +Module { + Component { + file: "AppManager.h" + name: "AppManager" + accessSemantics: "reference" + prototype: "QObject" + exports: ["LocaleTest/AppManager 1.0"] + isCreatable: false + isSingleton: true + exportMetaObjectRevisions: [256] + Property { + name: "primaryLocale" + type: "QLocale" + read: "getPrimaryLocale" + notify: "primaryLocaleChanged" + index: 0 + isReadonly: true + } + Signal { + name: "primaryLocaleChanged" + Parameter { type: "QLocale" } + } + } +} diff --git a/tests/auto/qml/qmllint/data/LocaleTest/qmldir b/tests/auto/qml/qmllint/data/LocaleTest/qmldir new file mode 100644 index 0000000000..2dc0799d5b --- /dev/null +++ b/tests/auto/qml/qmllint/data/LocaleTest/qmldir @@ -0,0 +1,3 @@ +module LocaleTest +typeinfo localeTest.qmltypes +depends QtQml diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml b/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml new file mode 100644 index 0000000000..5dda9a4d12 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property int o: 12 +} diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc b/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc new file mode 100644 index 0000000000..b6df21ec4c --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc @@ -0,0 +1,10 @@ +<RCC> + <qresource prefix="/qt/qml/MultiDirectory"> + <file>qmldir</file> + <file>Outer.qml</file> + <file>qml/qmldir</file> + <file>qml/Inner.qml</file> + <file>qml/pages/qmldir</file> + <file>qml/pages/Page.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml b/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml new file mode 100644 index 0000000000..01b7c331d9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml @@ -0,0 +1,5 @@ +import QtQml + +Outer { + o: 25 +} diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml new file mode 100644 index 0000000000..7efc889fb1 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml @@ -0,0 +1,5 @@ +import QtQml + +Inner { + o: 32 +} diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir new file mode 100644 index 0000000000..56ef19d41b --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir @@ -0,0 +1 @@ +prefer :/qt/qml/MultiDirectory/ diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir new file mode 100644 index 0000000000..56ef19d41b --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir @@ -0,0 +1 @@ +prefer :/qt/qml/MultiDirectory/ diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qmldir new file mode 100644 index 0000000000..699132dc4f --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qmldir @@ -0,0 +1,5 @@ +module MultiDirectory +prefer :/qt/qml/MultiDirectory +Outer 1.0 Outer.qml +Inner 1.0 qml/Inner.qml +Page 1.0 qml/pages/Page.qml diff --git a/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes b/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes new file mode 100644 index 0000000000..92c1b0953e --- /dev/null +++ b/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes @@ -0,0 +1,112 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by qmltyperegistrar. + +Module { + Component { + file: "mystyle.h" + name: "MyStyle" + accessSemantics: "reference" + prototype: "QQuickAttachedPropertyPropagator" + exports: ["MyStyle/MyStyle 1.0", "MyStyle/MyStyle 254.0"] + isCreatable: false + exportMetaObjectRevisions: [256, 65024] + attachedType: "MyStyle" + Enum { + name: "Theme" + values: ["Light", "Dark"] + } + Property { + name: "theme" + type: "Theme" + read: "theme" + write: "setTheme" + reset: "resetTheme" + notify: "themeChanged" + index: 0 + isFinal: true + } + Property { + name: "windowColor" + type: "QColor" + read: "windowColor" + notify: "themeChanged" + index: 1 + isReadonly: true + isFinal: true + } + Property { + name: "windowTextColor" + type: "QColor" + read: "windowTextColor" + notify: "themeChanged" + index: 2 + isReadonly: true + isFinal: true + } + Property { + name: "buttonColor" + type: "QColor" + read: "buttonColor" + notify: "themeChanged" + index: 3 + isReadonly: true + isFinal: true + } + Property { + name: "buttonTextColor" + type: "QColor" + read: "buttonTextColor" + notify: "themeChanged" + index: 4 + isReadonly: true + isFinal: true + } + Property { + name: "toolBarColor" + type: "QColor" + read: "toolBarColor" + notify: "themeChanged" + index: 5 + isReadonly: true + isFinal: true + } + Property { + name: "popupColor" + type: "QColor" + read: "popupColor" + notify: "themeChanged" + index: 6 + isReadonly: true + isFinal: true + } + Property { + name: "popupBorderColor" + type: "QColor" + read: "popupBorderColor" + notify: "themeChanged" + index: 7 + isReadonly: true + isFinal: true + } + Property { + name: "backgroundDimColor" + type: "QColor" + read: "backgroundDimColor" + notify: "themeChanged" + index: 8 + isReadonly: true + isFinal: true + } + Signal { name: "themeChanged" } + } + Component { + file: "qquickattachedpropertypropagator.h" + name: "QQuickAttachedPropertyPropagator" + accessSemantics: "reference" + prototype: "QObject" + } +} diff --git a/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml b/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml new file mode 100644 index 0000000000..5920797d9b --- /dev/null +++ b/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml @@ -0,0 +1,12 @@ +import QtQuick +import QtQuick.Templates as T +import MyStyle + +T.ToolBar { + id: control + + property color c: MyStyle.toolBarColor + background: Rectangle { + color: MyStyle.toolBarColor + } +} diff --git a/tests/auto/qml/qmllint/data/MyStyle/qmldir b/tests/auto/qml/qmllint/data/MyStyle/qmldir new file mode 100644 index 0000000000..8cc4246f7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/MyStyle/qmldir @@ -0,0 +1,4 @@ +module MyStyle +typeinfo MyStyle.qmltypes +ToolBar 254.0 ToolBar.qml +import QtQuick.Controls.Material diff --git a/tests/auto/qml/qmllint/data/NeedImportPath.qml b/tests/auto/qml/qmllint/data/NeedImportPath.qml new file mode 100644 index 0000000000..0a63b58f7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/NeedImportPath.qml @@ -0,0 +1,5 @@ +import ModuleInImportPath + +A { + myProperty: "Hello World" +} diff --git a/tests/auto/qml/qmllint/data/Qtbug111015/qmldir b/tests/auto/qml/qmllint/data/Qtbug111015/qmldir new file mode 100644 index 0000000000..3bf1d48e13 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Qtbug111015/qmldir @@ -0,0 +1,3 @@ +module Qtbug111015 +typeinfo qtbug111015.qmltypes +import QtQml diff --git a/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes b/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes new file mode 100644 index 0000000000..7de521a379 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes @@ -0,0 +1,20 @@ +import QtQuick.tooling 1.2 + +Module { + Component { + file: "typewithjsonobjectlist.h" + name: "TypeWithJsonObjectList" + exports: ["QmlLintTestLib/TypeWithJsonObjectList 1.0"] + accessSemantics: "reference" + prototype: "QObject" + Property { name: "jsonObjectList"; type: "QJsonObject"; isList: true; read: "getJsonObjectList"; index: 0; isReadonly: true } + } + Component { + file: "typewithjsonarray.h" + name: "TypeWithJsonArray" + exports: ["QmlLintTestLib/TypeWithJsonArray 1.0"] + accessSemantics: "reference" + prototype: "QObject" + Property { name: "jsonArray"; type: "QJsonArray"; read: "getJsonArray"; index: 0; isReadonly: true } + } +} diff --git a/tests/auto/qml/qmllint/data/SharedFunctions.js b/tests/auto/qml/qmllint/data/SharedFunctions.js new file mode 100644 index 0000000000..3398d2d6d7 --- /dev/null +++ b/tests/auto/qml/qmllint/data/SharedFunctions.js @@ -0,0 +1,5 @@ +.pragma library + +function setColorAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) +} diff --git a/tests/auto/qml/qmllint/data/StringToDateTime/qmldir b/tests/auto/qml/qmllint/data/StringToDateTime/qmldir new file mode 100644 index 0000000000..761f613496 --- /dev/null +++ b/tests/auto/qml/qmllint/data/StringToDateTime/qmldir @@ -0,0 +1,3 @@ +module StringToDateTime +typeinfo stringToDateTime.qmltypes +import QtQml diff --git a/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes b/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes new file mode 100644 index 0000000000..e0f13fa2ec --- /dev/null +++ b/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes @@ -0,0 +1,14 @@ +import QtQuick.tooling 1.2 +Module { + Component { + file: "stringToQDateTime.h" + name: "StringToDateTimeComponent" + exports: ["QmlLintTestLib/StringToDateTime 1.0"] + exportMetaObjectRevisions: [256] + accessSemantics: "reference" + prototype: "QObject" + Property { name: "aDate"; type: "QDate"; read: "getADate"; write: "setADate" } + Property { name: "aTime"; type: "QTime"; read: "getATime"; write: "setATime" } + Property { name: "aDateTime"; type: "QDateTime"; read: "getADateTime"; write: "setADateTime" } + } +} diff --git a/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes b/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes index 04ca30c184..77ad6a3cef 100644 --- a/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes +++ b/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes @@ -178,4 +178,24 @@ Module { exportMetaObjectRevisions: [256] Method { name: "createObject"; isJavaScriptFunction: true } } + Component { + file: "variantMapLookup.h" + name: "VariantMapLookupFoo" + prototype: "QObject" + exports: ["TestTypes/VariantMapLookupFoo 1.0"] + exportMetaObjectRevisions: [256] + Property { + name: "data" + type: "QVariantMap" + read: "data" + index: 0 + } + } + Component { + file: "foo.h" + name: "Foo" + prototype: "QObject" + exports: ["TestTypes/Foo 1.0"] + Method { name: "print" } + } } diff --git a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes index 9d36d91a90..3b0da602ac 100644 --- a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes +++ b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes @@ -12,6 +12,7 @@ Module { exports: ["Things/SomethingEntirelyStrange 1.0"] Enum { name: "AnEnum" + isScoped: true values: { "AAA": 0, "BBB": 1, @@ -20,7 +21,7 @@ Module { } Enum { name: "TheEnum" - scoped: false + isScoped: false values: { "V1": 0, "V2": 1 @@ -96,4 +97,21 @@ Module { Property { name: "foo"; type: "string" } hasCustomParser: true } + Component { + file: "mediaplayer-qml.h" + name: "MediaPlayerStateMachine" + accessSemantics: "value" + exports: ["Mediaplayer/MediaPlayerStateMachine 1.0"] + isCreatable: false + exportMetaObjectRevisions: [256] + } + Component { + file: "constinvokable.h" + name: "ConstInvokable" + accessSemantics: "reference" + prototype: "QObject" + exports: ["Things/ConstInvokable 1.0"] + exportMetaObjectRevisions: [256] + Method { name: "getObject"; type: "QObject"; isPointer: true; isTypeConstant: true } + } } diff --git a/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml b/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml new file mode 100644 index 0000000000..197b74fa60 --- /dev/null +++ b/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + Rectangle { + property real divisor: 0 + + Timer { + onTriggered: divisor = 1 + } + } +} diff --git a/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml b/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml new file mode 100644 index 0000000000..198e2146f5 --- /dev/null +++ b/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + Rectangle { + property real divisor: 0 + + Timer { + onTriggered: function() {"use strict"; divisor = 1 } + } + } +} diff --git a/tests/auto/qml/qmllint/data/addressableValue.qml b/tests/auto/qml/qmllint/data/addressableValue.qml new file mode 100644 index 0000000000..9b93728ed8 --- /dev/null +++ b/tests/auto/qml/qmllint/data/addressableValue.qml @@ -0,0 +1,8 @@ +pragma ValueTypeBehavior: Addressable + +import QtQml +import scripts + +QtObject { + property var v: "red" as vvv +} diff --git a/tests/auto/qml/qmllint/data/attachedImportUse.qml b/tests/auto/qml/qmllint/data/attachedImportUse.qml new file mode 100644 index 0000000000..56b4e2bf7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/attachedImportUse.qml @@ -0,0 +1,7 @@ +import QtQml +import TestTypes + +QtObject { + id: control + objectName: control.BirthdayParty.objectName +} diff --git a/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml b/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml new file mode 100644 index 0000000000..cdc2b28c07 --- /dev/null +++ b/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +Item { + + property alias innerObj: { + id: inner + } + +} diff --git a/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes new file mode 100644 index 0000000000..96f98dd14e --- /dev/null +++ b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes @@ -0,0 +1,513 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -builtins' + +Module { + dependencies: [] + Component { + name: "Qt" + Enum { + name: "GlobalColor" + values: { + "color0": 0, + "color1": 1, + "black": 2, + "white": 3, + "darkGray": 4, + "gray": 5, + "lightGray": 6, + "red": 7, + "green": 8, + "blue": 9, + "cyan": 10, + "magenta": 11, + "yellow": 12, + "darkRed": 13, + "darkGreen": 14, + "darkBlue": 15, + "darkCyan": 16, + "darkMagenta": 17, + "darkYellow": 18, + "transparent": 19 + } + } + Enum { + name: "KeyboardModifiers" + values: { + "NoModifier": 0, + "ShiftModifier": 33554432, + "ControlModifier": 67108864, + "AltModifier": 134217728, + "MetaModifier": 268435456, + "KeypadModifier": 536870912, + "GroupSwitchModifier": 1073741824, + "KeyboardModifierMask": -33554432 + } + } + Enum { + name: "MouseButtons" + values: { + "NoButton": 0, + "LeftButton": 1, + "RightButton": 2, + "MidButton": 4, // For backwards compatibility + "MiddleButton": 4, + "BackButton": 8, + "XButton1": 8, + "ExtraButton1": 8, + "ForwardButton": 16, + "XButton2": 16, + "ExtraButton2": 16, + "TaskButton": 32, + "ExtraButton3": 32, + "ExtraButton4": 64, + "ExtraButton5": 128, + "ExtraButton6": 256, + "ExtraButton7": 512, + "ExtraButton8": 1024, + "ExtraButton9": 2048, + "ExtraButton10": 4096, + "ExtraButton11": 8192, + "ExtraButton12": 16384, + "ExtraButton13": 32768, + "ExtraButton14": 65536, + "ExtraButton15": 131072, + "ExtraButton16": 262144, + "ExtraButton17": 524288, + "ExtraButton18": 1048576, + "ExtraButton19": 2097152, + "ExtraButton20": 4194304, + "ExtraButton21": 8388608, + "ExtraButton22": 16777216, + "ExtraButton23": 33554432, + "ExtraButton24": 67108864, + "AllButtons": 134217727, + "MaxMouseButton": 67108864, + "MouseButtonMask": -1 + } + } + Enum { + name: "Orientation" + values: { + "Horizontal": 1, + "Vertical": 2 + } + } + Enum { + name: "Orientations" + values: { + "Horizontal": 1, + "Vertical": 2 + } + } + Enum { + name: "FocusPolicy" + values: { + "NoFocus": 0, + "TabFocus": 1, + "ClickFocus": 2, + "StrongFocus": 11, + "WheelFocus": 15 + } + } + Enum { + name: "TabFocusBehavior" + values: { + "NoTabFocus": 0, + "TabFocusTextControls": 1, + "TabFocusListControls": 2, + "TabFocusAllControls": 255 + } + } + Enum { + name: "SortOrder" + values: { + "AscendingOrder": 0, + "DescendingOrder": 1 + } + } + Enum { + name: "SplitBehavior" + values: { + "KeepEmptyParts": 0, + "SkipEmptyParts": 1 + } + } + Enum { + name: "Alignment" + values: { + "AlignLeft": 1, + "AlignLeading": 1, + "AlignRight": 2, + "AlignTrailing": 2, + "AlignHCenter": 4, + "AlignJustify": 8, + "AlignAbsolute": 16, + "AlignHorizontal_Mask": 31, + "AlignTop": 32, + "AlignBottom": 64, + "AlignVCenter": 128, + "AlignBaseline": 256, + "AlignVertical_Mask": 480, + "AlignCenter": 132 + } + } + Enum { + name: "TextFlag" + values: { + "TextSingleLine": 256, + "TextDontClip": 512, + "TextExpandTabs": 1024, + "TextShowMnemonic": 2048, + "TextWordWrap": 4096, + "TextWrapAnywhere": 8192, + "TextDontPrint": 16384, + "TextIncludeTrailingSpaces": 134217728, + "TextHideMnemonic": 32768, + "TextJustificationForced": 65536, + "TextForceLeftToRight": 131072, + "TextForceRightToLeft": 262144, + "TextLongestVariant": 524288, + "TextBypassShaping": 1048576 + } + } + Enum { + name: "TextElideMode" + values: { + "ElideLeft": 0, + "ElideRight": 1, + "ElideMiddle": 2, + "ElideNone": 3 + } + } + Enum { + name: "WindowType" + values: { + "Widget": 0, + "Window": 1, + "Dialog": 3, + "Sheet": 5, + "Drawer": 7, + "Popup": 9, + "Tool": 11, + "ToolTip": 13, + "SplashScreen": 15, + "Desktop": 17, + "SubWindow": 18, + "ForeignWindow": 33, + "CoverWindow": 65, + "WindowType_Mask": 255, + "MSWindowsFixedSizeDialogHint": 256, + "MSWindowsOwnDC": 512, + "BypassWindowManagerHint": 1024, + "X11BypassWindowManagerHint": 1024, + "FramelessWindowHint": 2048, + "WindowTitleHint": 4096, + "WindowSystemMenuHint": 8192, + "WindowMinimizeButtonHint": 16384, + "WindowMaximizeButtonHint": 32768, + "WindowMinMaxButtonsHint": 49152, + "WindowContextHelpButtonHint": 65536, + "WindowShadeButtonHint": 131072, + "WindowStaysOnTopHint": 262144, + "WindowTransparentForInput": 524288, + "WindowOverridesSystemGestures": 1048576, + "WindowDoesNotAcceptFocus": 2097152, + "MaximizeUsingFullscreenGeometryHint": 4194304, + "CustomizeWindowHint": 33554432, + "WindowStaysOnBottomHint": 67108864, + "WindowCloseButtonHint": 134217728, + "MacWindowToolBarButtonHint": 268435456, + "BypassGraphicsProxyWidget": 536870912, + "NoDropShadowWindowHint": 1073741824, + "WindowFullscreenButtonHint": -2147483648 + } + } + Enum { + name: "WindowFlags" + values: { + "Widget": 0, + "Window": 1, + "Dialog": 3, + "Sheet": 5, + "Drawer": 7, + "Popup": 9, + "Tool": 11, + "ToolTip": 13, + "SplashScreen": 15, + "Desktop": 17, + "SubWindow": 18, + "ForeignWindow": 33, + "CoverWindow": 65, + "WindowType_Mask": 255, + "MSWindowsFixedSizeDialogHint": 256, + "MSWindowsOwnDC": 512, + "BypassWindowManagerHint": 1024, + "X11BypassWindowManagerHint": 1024, + "FramelessWindowHint": 2048, + "WindowTitleHint": 4096, + "WindowSystemMenuHint": 8192, + "WindowMinimizeButtonHint": 16384, + "WindowMaximizeButtonHint": 32768, + "WindowMinMaxButtonsHint": 49152, + "WindowContextHelpButtonHint": 65536, + "WindowShadeButtonHint": 131072, + "WindowStaysOnTopHint": 262144, + "WindowTransparentForInput": 524288, + "WindowOverridesSystemGestures": 1048576, + "WindowDoesNotAcceptFocus": 2097152, + "MaximizeUsingFullscreenGeometryHint": 4194304, + "CustomizeWindowHint": 33554432, + "WindowStaysOnBottomHint": 67108864, + "WindowCloseButtonHint": 134217728, + "MacWindowToolBarButtonHint": 268435456, + "BypassGraphicsProxyWidget": 536870912, + "NoDropShadowWindowHint": 1073741824, + "WindowFullscreenButtonHint": -2147483648 + } + } + Enum { + name: "WindowState" + values: { + "WindowNoState": 0, + "WindowMinimized": 1, + "WindowMaximized": 2, + "WindowFullScreen": 4, + "WindowActive": 8 + } + } + Enum { + name: "WindowStates" + values: { + "WindowNoState": 0, + "WindowMinimized": 1, + "WindowMaximized": 2, + "WindowFullScreen": 4, + "WindowActive": 8 + } + } + Enum { + name: "ApplicationState" + values: { + "ApplicationSuspended": 0, + "ApplicationHidden": 1, + "ApplicationInactive": 2, + "ApplicationActive": 4 + } + } + Enum { + name: "ScreenOrientation" + values: { + "PrimaryOrientation": 0, + "PortraitOrientation": 1, + "LandscapeOrientation": 2, + "InvertedPortraitOrientation": 4, + "InvertedLandscapeOrientation": 8 + } + } + Enum { + name: "ScreenOrientations" + values: { + "PrimaryOrientation": 0, + "PortraitOrientation": 1, + "LandscapeOrientation": 2, + "InvertedPortraitOrientation": 4, + "InvertedLandscapeOrientation": 8 + } + } + Enum { + name: "WidgetAttribute" + values: { + "WA_Disabled": 0, + "WA_UnderMouse": 1, + "WA_MouseTracking": 2, + "WA_ContentsPropagated": 3, + "WA_OpaquePaintEvent": 4, + "WA_NoBackground": 4, + "WA_StaticContents": 5, + "WA_LaidOut": 7, + "WA_PaintOnScreen": 8, + "WA_NoSystemBackground": 9, + "WA_UpdatesDisabled": 10, + "WA_Mapped": 11, + "WA_MacNoClickThrough": 12, + "WA_InputMethodEnabled": 14, + "WA_WState_Visible": 15, + "WA_WState_Hidden": 16, + "WA_ForceDisabled": 32, + "WA_KeyCompression": 33, + "WA_PendingMoveEvent": 34, + "WA_PendingResizeEvent": 35, + "WA_SetPalette": 36, + "WA_SetFont": 37, + "WA_SetCursor": 38, + "WA_NoChildEventsFromChildren": 39, + "WA_WindowModified": 41, + "WA_Resized": 42, + "WA_Moved": 43, + "WA_PendingUpdate": 44, + "WA_InvalidSize": 45, + "WA_MacBrushedMetal": 46, + "WA_MacMetalStyle": 46, + "WA_CustomWhatsThis": 47, + "WA_LayoutOnEntireRect": 48, + "WA_OutsideWSRange": 49, + "WA_GrabbedShortcut": 50, + "WA_TransparentForMouseEvents": 51, + "WA_PaintUnclipped": 52, + "WA_SetWindowIcon": 53, + "WA_NoMouseReplay": 54, + "WA_DeleteOnClose": 55, + "WA_RightToLeft": 56, + "WA_SetLayoutDirection": 57, + "WA_NoChildEventsForParent": 58, + "WA_ForceUpdatesDisabled": 59, + "WA_WState_Created": 60, + "WA_WState_CompressKeys": 61, + "WA_WState_InPaintEvent": 62, + "WA_WState_Reparented": 63, + "WA_WState_ConfigPending": 64, + "WA_WState_Polished": 66, + "WA_WState_DND": 67, + "WA_WState_OwnSizePolicy": 68, + "WA_WState_ExplicitShowHide": 69, + "WA_ShowModal": 70, + "WA_MouseNoMask": 71, + "WA_GroupLeader": 72, + "WA_NoMousePropagation": 73, + "WA_Hover": 74, + "WA_InputMethodTransparent": 75, + "WA_QuitOnClose": 76, + "WA_KeyboardFocusChange": 77, + "WA_AcceptDrops": 78, + "WA_DropSiteRegistered": 79, + "WA_ForceAcceptDrops": 79, + "WA_WindowPropagation": 80, + "WA_NoX11EventCompression": 81, + "WA_TintedBackground": 82, + "WA_X11OpenGLOverlay": 83, + "WA_AlwaysShowToolTips": 84, + "WA_MacOpaqueSizeGrip": 85, + "WA_SetStyle": 86, + "WA_SetLocale": 87, + "WA_MacShowFocusRect": 88, + "WA_MacNormalSize": 89, + "WA_MacSmallSize": 90, + "WA_MacMiniSize": 91, + "WA_LayoutUsesWidgetRect": 92, + "WA_StyledBackground": 93, + "WA_MSWindowsUseDirect3D": 94, + "WA_CanHostQMdiSubWindowTitleBar": 95, + "WA_MacAlwaysShowToolWindow": 96, + "WA_StyleSheet": 97, + "WA_ShowWithoutActivating": 98, + "WA_X11BypassTransientForHint": 99, + "WA_NativeWindow": 100, + "WA_DontCreateNativeAncestors": 101, + "WA_MacVariableSize": 102, + "WA_DontShowOnScreen": 103, + "WA_X11NetWmWindowTypeDesktop": 104, + "WA_X11NetWmWindowTypeDock": 105, + "WA_X11NetWmWindowTypeToolBar": 106, + "WA_X11NetWmWindowTypeMenu": 107, + "WA_X11NetWmWindowTypeUtility": 108, + "WA_X11NetWmWindowTypeSplash": 109, + "WA_X11NetWmWindowTypeDialog": 110, + "WA_X11NetWmWindowTypeDropDownMenu": 111, + "WA_X11NetWmWindowTypePopupMenu": 112, + "WA_X11NetWmWindowTypeToolTip": 113, + "WA_X11NetWmWindowTypeNotification": 114, + "WA_X11NetWmWindowTypeCombo": 115, + "WA_X11NetWmWindowTypeDND": 116, + "WA_MacFrameworkScaled": 117, + "WA_SetWindowModality": 118, + "WA_WState_WindowOpacitySet": 119, + "WA_TranslucentBackground": 120, + "WA_AcceptTouchEvents": 121, + "WA_WState_AcceptedTouchBeginEvent": 122, + "WA_TouchPadAcceptSingleTouchEvents": 123, + "WA_X11DoNotAcceptFocus": 126, + "WA_MacNoShadow": 127, + "WA_AlwaysStackOnTop": 128, + "WA_TabletTracking": 129, + "WA_ContentsMarginsRespectsSafeArea": 130, + "WA_StyleSheetTarget": 131, + "WA_AttributeCount": 132 + } + } + Enum { + name: "ApplicationAttribute" + values: { + "AA_ImmediateWidgetCreation": 0, + "AA_MSWindowsUseDirect3DByDefault": 1, + "AA_DontShowIconsInMenus": 2, + "AA_NativeWindows": 3, + "AA_DontCreateNativeWidgetSiblings": 4, + "AA_PluginApplication": 5, + "AA_MacPluginApplication": 5, + "AA_DontUseNativeMenuBar": 6, + "AA_MacDontSwapCtrlAndMeta": 7, + "AA_Use96Dpi": 8, + "AA_X11InitThreads": 10, + "AA_SynthesizeTouchForUnhandledMouseEvents": 11, + "AA_SynthesizeMouseForUnhandledTouchEvents": 12, + "AA_UseHighDpiPixmaps": 13, + "AA_ForceRasterWidgets": 14, + "AA_UseDesktopOpenGL": 15, + "AA_UseOpenGLES": 16, + "AA_UseSoftwareOpenGL": 17, + "AA_ShareOpenGLContexts": 18, + "AA_SetPalette": 19, + "AA_EnableHighDpiScaling": 20, + "AA_DisableHighDpiScaling": 21, + "AA_UseStyleSheetPropagationInWidgetStyles": 22, + "AA_DontUseNativeDialogs": 23, + "AA_SynthesizeMouseForUnhandledTabletEvents": 24, + "AA_CompressHighFrequencyEvents": 25, + "AA_DontCheckOpenGLContextThreadAffinity": 26, + "AA_DisableShaderDiskCache": 27, + "AA_DontShowShortcutsInContextMenus": 28, + "AA_CompressTabletEvents": 29, + "AA_DisableWindowContextHelpButton": 30, + "AA_DisableSessionManager": 31, + "AA_AttributeCount": 32 + } + } + Enum { + name: "ImageConversionFlags" + values: { + "ColorMode_Mask": 3, + "AutoColor": 0, + "ColorOnly": 3, + "MonoOnly": 2, + "AlphaDither_Mask": 12, + "ThresholdAlphaDither": 0, + "OrderedAlphaDither": 4, + "DiffuseAlphaDither": 8, + "NoAlpha": 12, + "Dither_Mask": 48, + "DiffuseDither": 0, + "OrderedDither": 16, + "ThresholdDither": 32, + "DitherMode_Mask": 192, + "AutoDither": 0, + "PreferDither": 64, + "AvoidDither": 128, + "NoOpaqueDetection": 256, + "NoFormatConversion": 512 + } + } + Enum { + name: "BGMode" + values: { + "TransparentMode": 0, + "OpaqueMode": 1 + } + } + } + Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" } +} diff --git a/tests/auto/qml/qmllint/data/coercetovoid.qml b/tests/auto/qml/qmllint/data/coercetovoid.qml new file mode 100644 index 0000000000..f1b593ea3a --- /dev/null +++ b/tests/auto/qml/qmllint/data/coercetovoid.qml @@ -0,0 +1,8 @@ +pragma Strict +import QtQml + +QtObject { + function touchThisAndReturnSomething(x: int) { + return x + 1; + } +} diff --git a/tests/auto/qml/qmllint/data/customParser.qml b/tests/auto/qml/qmllint/data/customParser.qml index a83ae7e823..324ca20953 100644 --- a/tests/auto/qml/qmllint/data/customParser.qml +++ b/tests/auto/qml/qmllint/data/customParser.qml @@ -7,11 +7,11 @@ Rectangle { states: [ State { name: "red_color" - PropertyChanges { target: root; color: "red" } + PropertyChanges { root.color: "red" } }, State { name: "blue_color" - PropertyChanges { target: root; color: "blue" } + PropertyChanges { root.color: "blue" } } ] } diff --git a/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml b/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml new file mode 100644 index 0000000000..dc582936b9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml @@ -0,0 +1,11 @@ +import QtQuick + +import "SharedFunctions.js" as Functions + +Item { + Rectangle { + color: Functions.setColorAlpha(Qt.color("orange"), 0.15) + x: Functions.setColorAlpha.asdfg + y: Functions.asdfg + } +} diff --git a/tests/auto/qml/qmllint/data/findMemberPrint.qml b/tests/auto/qml/qmllint/data/findMemberPrint.qml new file mode 100644 index 0000000000..69146eb06b --- /dev/null +++ b/tests/auto/qml/qmllint/data/findMemberPrint.qml @@ -0,0 +1,13 @@ +import QtQml + +import TestTypes + +QtObject { + property var foooooooo: Foo { + id: foo + } + + function f(): void { + foo.print() + } +} diff --git a/tests/auto/qml/qmllint/data/generalizedGroupHint.qml b/tests/auto/qml/qmllint/data/generalizedGroupHint.qml new file mode 100644 index 0000000000..5fec03f7f3 --- /dev/null +++ b/tests/auto/qml/qmllint/data/generalizedGroupHint.qml @@ -0,0 +1,23 @@ +import QtQuick + +Window { + width: 640 + height: 480 + visible: true + title: 'Resolve my color type' + + Item { + id: foo + + states: [ + State { + PropertyChanges { + target: foo + myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16) + } + } + ] + + property color myColor: 'black' + } +} diff --git a/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml b/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml new file mode 100644 index 0000000000..7cfe98d4f8 --- /dev/null +++ b/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml @@ -0,0 +1,20 @@ +import QtQuick +import QtQuick.Layouts + +Window { + id: root + + Rectangle { + id: redRect + } + + Item { + states: [ + State { + PropertyChanges { + redRect.Layout.fillWidth: true + } + } + ] + } +} diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml new file mode 100644 index 0000000000..d018110b86 --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml @@ -0,0 +1,11 @@ +import QtQuick +import QtQuick.Controls + +Window { + width: 640; height: 480 + visible: true + + Button { + text: "a" + } +} diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir new file mode 100644 index 0000000000..acb456ffd9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir @@ -0,0 +1,3 @@ +module moduleWithQrc +prefer :/qt/qml/moduleWithQrc/ + diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc new file mode 100644 index 0000000000..43d3ec805c --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/qt/qml/qmllint65/"> + <file alias="main.qml">moduleWithQrc/main.qml</file> + </qresource> +</RCC> + diff --git a/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc b/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc new file mode 100644 index 0000000000..cb362e2d55 --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/qt/qml/qmllint65"> + <file alias="qmldir">moduleWithQrc/qmldir</file> + </qresource> +</RCC> + diff --git a/tests/auto/qml/qmllint/data/importNonexistentFile.qml b/tests/auto/qml/qmllint/data/importNonexistentFile.qml new file mode 100644 index 0000000000..847023936a --- /dev/null +++ b/tests/auto/qml/qmllint/data/importNonexistentFile.qml @@ -0,0 +1,3 @@ +import "¯\(ツ)/¯:/invalid/url" + +QtObject {} diff --git a/tests/auto/qml/qmllint/data/importNullDevice.qml b/tests/auto/qml/qmllint/data/importNullDevice.qml new file mode 100644 index 0000000000..5ff3090d75 --- /dev/null +++ b/tests/auto/qml/qmllint/data/importNullDevice.qml @@ -0,0 +1,3 @@ +import "/dev/null" + +QtObject {} diff --git a/tests/auto/qml/qmllint/data/initReadonly.qml b/tests/auto/qml/qmllint/data/initReadonly.qml index 5a3cafff23..a9a2a0016b 100644 --- a/tests/auto/qml/qmllint/data/initReadonly.qml +++ b/tests/auto/qml/qmllint/data/initReadonly.qml @@ -2,4 +2,5 @@ import QtQml QtObject { readonly property int i: 14 + readonly property int j: i + 20 } diff --git a/tests/auto/qml/qmllint/data/inlineComponent.qml b/tests/auto/qml/qmllint/data/inlineComponent.qml index ce6998a980..364d5319de 100644 --- a/tests/auto/qml/qmllint/data/inlineComponent.qml +++ b/tests/auto/qml/qmllint/data/inlineComponent.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 Item { + component MyIC: IC {} component IC : QtObject {} QtObject { component IC2: QtObject {} diff --git a/tests/auto/qml/qmllint/data/invalidIdLookup.qml b/tests/auto/qml/qmllint/data/invalidIdLookup.qml new file mode 100644 index 0000000000..b351e5cfea --- /dev/null +++ b/tests/auto/qml/qmllint/data/invalidIdLookup.qml @@ -0,0 +1,10 @@ +import Things +import QtQml + +QtObject { + property MediaPlayerStateMachine m: MediaPlayerStateMachine { + id: stateMachine + } + + objectName: stateMachine.objectName +} diff --git a/tests/auto/qml/qmllint/data/jsonArrayIsRecognized.qml b/tests/auto/qml/qmllint/data/jsonArrayIsRecognized.qml new file mode 100644 index 0000000000..89c52e0e52 --- /dev/null +++ b/tests/auto/qml/qmllint/data/jsonArrayIsRecognized.qml @@ -0,0 +1,8 @@ +import QtQuick +import Qtbug111015 1.0 + +Item { + TypeWithJsonArray { + jsonArray: [] + } +} diff --git a/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml b/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml new file mode 100644 index 0000000000..0a96fa9f92 --- /dev/null +++ b/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml @@ -0,0 +1,8 @@ +import QtQuick +import Qtbug111015 1.0 + +Item { + TypeWithJsonObjectList { + jsonObjectList: [] + } +} diff --git a/tests/auto/qml/qmllint/data/locale.qml b/tests/auto/qml/qmllint/data/locale.qml new file mode 100644 index 0000000000..4ff9e6392c --- /dev/null +++ b/tests/auto/qml/qmllint/data/locale.qml @@ -0,0 +1,6 @@ +import QtQml +import LocaleTest + +QtObject { + property int monday: AppManager.primaryLocale.firstDayOfWeek +} diff --git a/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml new file mode 100644 index 0000000000..14c716c35d --- /dev/null +++ b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml @@ -0,0 +1,9 @@ +import QtQuick as test + +test.Rectangle { // crashed qqmljsimportvisitor + id: mainRect + width: 100 + height: 100 + visible: true + rotation: 11 +} diff --git a/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml new file mode 100644 index 0000000000..4e03d8091d --- /dev/null +++ b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml @@ -0,0 +1,9 @@ +import QtQuick as test + +test.Item { + property test.color c + + property var p: test.Grid {} + + component IC: test.Rectangle {} +} diff --git a/tests/auto/qml/qmllint/data/multifix.fixed.qml b/tests/auto/qml/qmllint/data/multifix.fixed.qml new file mode 100644 index 0000000000..d2188f2318 --- /dev/null +++ b/tests/auto/qml/qmllint/data/multifix.fixed.qml @@ -0,0 +1,14 @@ +pragma ComponentBehavior: Bound +import QtQml + +QtObject { + id: root + + property Component cursorDelegate: QtObject { + objectName: root.objectName + } + + property Component background: QtObject { + objectName: root.objectName + } +} diff --git a/tests/auto/qml/qmllint/data/multifix.qml b/tests/auto/qml/qmllint/data/multifix.qml new file mode 100644 index 0000000000..5f05ae7e62 --- /dev/null +++ b/tests/auto/qml/qmllint/data/multifix.qml @@ -0,0 +1,13 @@ +import QtQml + +QtObject { + id: root + + property Component cursorDelegate: QtObject { + objectName: root.objectName + } + + property Component background: QtObject { + objectName: root.objectName + } +} diff --git a/tests/auto/qml/qmllint/data/notQmlRootMethods.qml b/tests/auto/qml/qmllint/data/notQmlRootMethods.qml new file mode 100644 index 0000000000..6d067d572d --- /dev/null +++ b/tests/auto/qml/qmllint/data/notQmlRootMethods.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + id: self + + function a() { self.deleteLater(); } + function b() { self.destroyed(); } +} diff --git a/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml b/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml index 2a51cceac3..b6c0f59c7f 100644 --- a/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml +++ b/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml @@ -5,5 +5,6 @@ Item { anchors.horizontalCenter: undefined anchors.verticalCenter: undefined anchors.baseline: undefined + Component.onCompleted: anchors.bottom = undefined } } diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml new file mode 100644 index 0000000000..fa7d4ef1ac --- /dev/null +++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Rectangle { + State { + name: "test" + PropertyChanges { target: root; color: "blue" } + } +} diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml new file mode 100644 index 0000000000..94873463f1 --- /dev/null +++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml @@ -0,0 +1,19 @@ +import QtQuick + +Window { + Item { + id: foo + property color myColor: 'black' + + states: [ + State { + PropertyChanges { + target: foo + myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16) + notThere: "a" + } + } + ] + + } +} diff --git a/tests/auto/qml/qmllint/data/qEventPoint.qml b/tests/auto/qml/qmllint/data/qEventPoint.qml new file mode 100644 index 0000000000..086e710b48 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qEventPoint.qml @@ -0,0 +1,9 @@ +import QtQuick + +TapHandler { + acceptedButtons: Qt.LeftButton | Qt.RightButton + onSingleTapped: (eventPoint, button) => { + console.log("Single tap at", eventPoint, "with button", button) + } + onTapped: console.log("tapped") +} diff --git a/tests/auto/qml/qmllint/data/qmlRootMethods.qml b/tests/auto/qml/qmllint/data/qmlRootMethods.qml new file mode 100644 index 0000000000..5541de3a32 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qmlRootMethods.qml @@ -0,0 +1,12 @@ +import QtQml + +QtObject { + id: self + + objectName: self.toString() + + Component.onCompleted: { + self.destroy(); + self.destroy(25); + } +} diff --git a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml index 4847fc9196..ad88f1c58c 100644 --- a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml +++ b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml @@ -2,5 +2,5 @@ import Things 1.0 Something { property var a: SomethingEntirelyStrange {} - property var b: SomethingEntirelyStrange.AAA + property var b: SomethingEntirelyStrange.AnEnum.AAA } diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml new file mode 100644 index 0000000000..de142337b4 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml @@ -0,0 +1,9 @@ +import QtQml +import QtQuick + +QtObject { + id: root + component Comp : Item { } + property Comp c: Comp{ } + function comp() { return root.c } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml new file mode 100644 index 0000000000..0585ceceb2 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + function enumeration() { return Text.AlignRight } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml new file mode 100644 index 0000000000..dc03311e73 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + function f() { } + function method() { return f } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml new file mode 100644 index 0000000000..bb79978d85 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml @@ -0,0 +1,7 @@ +import QtQml + +QtObject { + id: root + property int i: 1 + function prop() { return root.i } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml new file mode 100644 index 0000000000..78f02a8b67 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + function type() { return 1 + 1 } +} diff --git a/tests/auto/qml/qmllint/data/scriptInTemplate.qml b/tests/auto/qml/qmllint/data/scriptInTemplate.qml new file mode 100644 index 0000000000..ba333dcd3e --- /dev/null +++ b/tests/auto/qml/qmllint/data/scriptInTemplate.qml @@ -0,0 +1,6 @@ +import QtQml +import scripts + +QtObject { + objectName: `Result: ${Foo.getText()}` +} diff --git a/tests/auto/qml/qmllint/data/scripts/Foo.js b/tests/auto/qml/qmllint/data/scripts/Foo.js new file mode 100644 index 0000000000..1d5106f733 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scripts/Foo.js @@ -0,0 +1,6 @@ +.pragma library + +function getText() { + return "whohoooooo" +} + diff --git a/tests/auto/qml/qmllint/data/scripts/qmldir b/tests/auto/qml/qmllint/data/scripts/qmldir new file mode 100644 index 0000000000..b4bf844348 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scripts/qmldir @@ -0,0 +1,4 @@ +module scripts +typeinfo scripts.qmltypes +Foo 1.0 Foo.js + diff --git a/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes b/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes new file mode 100644 index 0000000000..ebbfc41eb2 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes @@ -0,0 +1,22 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by qmltyperegistrar. + +Module { + Component { + file: "value.h" + name: "ValueType" + accessSemantics: "value" + exports: ["scripts/vvv 1.0"] + exportMetaObjectRevisions: [256] + Method { + name: "ValueType" + isConstructor: true + Parameter { name: "v"; type: "QString" } + } + Method { name: "ValueType"; isCloned: true; isConstructor: true } + } +} diff --git a/tests/auto/qml/qmllint/data/scriptstring.qml b/tests/auto/qml/qmllint/data/scriptstring.qml new file mode 100644 index 0000000000..733434e924 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scriptstring.qml @@ -0,0 +1,73 @@ +import QtQuick + +Window { + id: root + + Rectangle { + id: main + + MouseArea { + id: mouse + property int clickCount: 0 + onClicked: { + clickCount++ + + switch ( clickCount % 3 ) { + case 1 : + main.state = "middleState" + break + case 2 : + main.state = "rightState" + break + default : + main.state = "leftState" + } + } + } + + Rectangle { + id: mover + anchors { + left: undefined + right: undefined + horizontalCenter: undefined + top: main.top + bottom: main.bottom + } + } + + states: [ + State { + name: "leftState" + AnchorChanges { + target: mover + anchors.left: main.left + anchors.right: undefined + anchors.horizontalCenter: undefined + } + }, + State { + name: "middleState" + AnchorChanges { + target: mover + anchors { + left: undefined + right: undefined + horizontalCenter: main.horizontalCenter + } + } + }, + State { + name: "rightState" + AnchorChanges { + target: mover + anchors { + left: undefined + right: main.right + horizontalCenter: undefined + } + } + } + ] + } +} diff --git a/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini b/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini new file mode 100644 index 0000000000..a72e0e29e2 --- /dev/null +++ b/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini @@ -0,0 +1,2 @@ +[Warnings] +TestWarning=disable diff --git a/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml b/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml new file mode 100644 index 0000000000..b9250a2d11 --- /dev/null +++ b/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + Rectangle { + radius: 5 + } +} diff --git a/tests/auto/qml/qmllint/data/something.qml b/tests/auto/qml/qmllint/data/something.qml new file mode 100644 index 0000000000..38998f606d --- /dev/null +++ b/tests/auto/qml/qmllint/data/something.qml @@ -0,0 +1,2 @@ +import ModuleInImportPath +A {} diff --git a/tests/auto/qml/qmllint/data/storeNameMethod.qml b/tests/auto/qml/qmllint/data/storeNameMethod.qml new file mode 100644 index 0000000000..fca02b288f --- /dev/null +++ b/tests/auto/qml/qmllint/data/storeNameMethod.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + Rectangle { + + Timer { + function foo() {} + onTriggered: foo = 1 + } + } +} diff --git a/tests/auto/qml/qmllint/data/stringToDateTime.qml b/tests/auto/qml/qmllint/data/stringToDateTime.qml new file mode 100644 index 0000000000..dc4bd6cda5 --- /dev/null +++ b/tests/auto/qml/qmllint/data/stringToDateTime.qml @@ -0,0 +1,7 @@ +import StringToDateTime + +StringToDateTime { + aDate: "2023-03-29" + aTime: "15:10:41" + aDateTime: "2023-03-29 15:10:41" +} diff --git a/tests/auto/qml/qmllint/data/untitled/components/Foo.qml b/tests/auto/qml/qmllint/data/untitled/components/Foo.qml new file mode 100644 index 0000000000..10e5741900 --- /dev/null +++ b/tests/auto/qml/qmllint/data/untitled/components/Foo.qml @@ -0,0 +1,5 @@ +import QtQuick + +Text { + text: "Here I am!" +} diff --git a/tests/auto/qml/qmllint/data/untitled/main.qml b/tests/auto/qml/qmllint/data/untitled/main.qml new file mode 100644 index 0000000000..cf8980ab55 --- /dev/null +++ b/tests/auto/qml/qmllint/data/untitled/main.qml @@ -0,0 +1,9 @@ +pragma Strict + +import QtQuick +import 'qrc:/untitled/components' as C + +Window { + id: root + C.Foo {} +} diff --git a/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc b/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc new file mode 100644 index 0000000000..0ad0d57fbb --- /dev/null +++ b/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/untitled/"> + <file alias="main.qml">main.qml</file> + <file alias="components/Foo.qml">components/Foo.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmllint/data/useConstInvokable.qml b/tests/auto/qml/qmllint/data/useConstInvokable.qml new file mode 100644 index 0000000000..4f89e89918 --- /dev/null +++ b/tests/auto/qml/qmllint/data/useConstInvokable.qml @@ -0,0 +1,5 @@ +import Things + +ConstInvokable { + objectName: getObject().objectName +} diff --git a/tests/auto/qml/qmllint/data/validLiterals.qml b/tests/auto/qml/qmllint/data/validLiterals.qml index 4f8c575cd3..55792eaae2 100644 --- a/tests/auto/qml/qmllint/data/validLiterals.qml +++ b/tests/auto/qml/qmllint/data/validLiterals.qml @@ -29,9 +29,9 @@ QtObject { property date date1: "2021-08-13T14:16:21.435Z" - property point point1: "1,2" + property point point1: ({ x: 1, y: 2 }) - property size size1: "50x50" + property size size1: ({ width: 50, height: 50 }) - property rect rect1: "10,20,30x30" + property rect rect1: ({ x: 10, y: 20, width: 30, height: 30 }) } diff --git a/tests/auto/qml/qmllint/data/valueTypesFromString.qml b/tests/auto/qml/qmllint/data/valueTypesFromString.qml new file mode 100644 index 0000000000..fc013f858f --- /dev/null +++ b/tests/auto/qml/qmllint/data/valueTypesFromString.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + property point p: "30,50" + property rect p3: "10, 20, 30x50" + property size p4: "30x50" +} diff --git a/tests/auto/qml/qmllint/data/variantMapLookup.qml b/tests/auto/qml/qmllint/data/variantMapLookup.qml new file mode 100644 index 0000000000..7f50879932 --- /dev/null +++ b/tests/auto/qml/qmllint/data/variantMapLookup.qml @@ -0,0 +1,13 @@ +pragma Strict +import TestTypes +import QtQuick + +Item { + VariantMapLookupFoo { + id: moo + } + Component.onCompleted: { + moo.data.value = 5 + moo.data["value"] = 6 + } +} diff --git a/tests/auto/qml/qmllint/data/writeListProperty.qml b/tests/auto/qml/qmllint/data/writeListProperty.qml new file mode 100644 index 0000000000..8015fdf51b --- /dev/null +++ b/tests/auto/qml/qmllint/data/writeListProperty.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + id: self + property Item a: Item { id: a } + Component.onCompleted: self.data = [ a ] +} diff --git a/tests/auto/qml/qmllint/lintplugin.cpp b/tests/auto/qml/qmllint/lintplugin.cpp index 47279adcaa..58b174cb6b 100644 --- a/tests/auto/qml/qmllint/lintplugin.cpp +++ b/tests/auto/qml/qmllint/lintplugin.cpp @@ -1,11 +1,11 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "lintplugin.h" using namespace Qt::StringLiterals; -static constexpr LoggerWarningId plugin { "testPlugin.test" }; +static constexpr QQmlSA::LoggerWarningId plugin{ "testPlugin.test" }; class ElementTest : public QQmlSA::ElementPass { @@ -17,25 +17,25 @@ public: bool shouldRun(const QQmlSA::Element &element) override { - return element->baseType() == m_rectangle; + return element.baseType() == m_rectangle; } void run(const QQmlSA::Element &element) override { - auto property = element->property(u"radius"_s); - if (!property.isValid() || element->property(u"radius"_s).typeName() != u"double") { - emitWarning(u"Failed to verify radius property", plugin, element->sourceLocation()); + auto property = element.property(u"radius"_s); + if (!property.isValid() || element.property(u"radius"_s).typeName() != u"double") { + emitWarning(u"Failed to verify radius property", plugin, element.sourceLocation()); return; } - auto bindings = element->propertyBindings(u"radius"_s); + auto bindings = element.propertyBindings(u"radius"_s); if (bindings.isEmpty() || bindings.constFirst().numberValue() != 5) { emitWarning(u"Failed to verify radius property binding", plugin, - element->sourceLocation()); + element.sourceLocation()); return; } - emitWarning(u"ElementTest OK", plugin, element->sourceLocation()); + emitWarning(u"ElementTest OK", plugin, element.sourceLocation()); } private: @@ -48,37 +48,37 @@ public: PropertyTest(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager) { } void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override { emitWarning(u"Saw binding on %1 property %2 with value %3 (and type %4) in scope %5"_s - .arg(element->baseTypeName(), propertyName, + .arg(element.baseTypeName(), propertyName, value.isNull() ? u"NULL"_s - : (value->internalName().isNull() ? value->baseTypeName() - : value->baseTypeName())) - .arg(binding.bindingType()) - .arg(bindingScope->baseTypeName()), - plugin, bindingScope->sourceLocation()); + : (value.name().isNull() ? value.baseTypeName() + : value.name())) + .arg(qToUnderlying(binding.bindingType())) + .arg(bindingScope.baseTypeName()), + plugin, bindingScope.sourceLocation()); } void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override { emitWarning(u"Saw read on %1 property %2 in scope %3"_s.arg( - element->baseTypeName(), propertyName, readScope->baseTypeName()), + element.baseTypeName(), propertyName, readScope.baseTypeName()), plugin, location); } void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) override + QQmlSA::SourceLocation location) override { emitWarning(u"Saw write on %1 property %2 with value %3 in scope %4"_s.arg( - element->baseTypeName(), propertyName, - (value->internalName().isNull() ? value->baseTypeName() - : value->internalName()), - writeScope->baseTypeName()), + element.baseTypeName(), propertyName, + (value.name().isNull() ? value.baseTypeName() + : value.name()), + writeScope.baseTypeName()), plugin, location); } }; @@ -109,7 +109,7 @@ private: void LintPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) { - if (!rootElement->filePath().endsWith(u"_pluginTest.qml")) + if (!rootElement.filePath().endsWith(u"_pluginTest.qml")) return; manager->registerElementPass(std::make_unique<ElementTest>(manager)); diff --git a/tests/auto/qml/qmllint/lintplugin.h b/tests/auto/qml/qmllint/lintplugin.h index 76733ca7a7..c121657456 100644 --- a/tests/auto/qml/qmllint/lintplugin.h +++ b/tests/auto/qml/qmllint/lintplugin.h @@ -1,12 +1,12 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef LINTPLUGIN_H #define LINTPLUGIN_H #include <QtPlugin> #include <QtCore/qobject.h> -#include <QtQmlCompiler/private/qqmlsa_p.h> +#include <QtQmlCompiler/qqmlsa.h> class LintPlugin : public QObject, public QQmlSA::LintPlugin { diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index a5e906ce9a..7f7b5317cc 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com> // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QProcess> @@ -43,6 +43,11 @@ public: Flags flags = {}; }; + struct Environment : public QList<QPair<QString, QString>> + { + using QList<QPair<QString, QString>>::QList; + }; + private Q_SLOTS: void initTestCase() override; @@ -73,12 +78,19 @@ private Q_SLOTS: void autoqmltypes(); void resources(); + void multiDirectory(); + void requiredProperty(); void settingsFile(); void additionalImplicitImport(); + void qrcUrlImport(); + + void incorrectImportFromHost_data(); + void incorrectImportFromHost(); + void attachedPropertyReuse(); void missingBuiltinsNoCrash(); @@ -90,11 +102,21 @@ private Q_SLOTS: void lintModule(); void testLineEndings(); + void valueTypesFromString(); + + void ignoreSettingsNotCommandLineOptions(); + void backslashedQmldirPath(); + + void environment_data(); + void environment(); + + void maxWarnings(); #if QT_CONFIG(library) void testPlugin(); void quickPlugin(); #endif + private: enum DefaultImportOption { NoDefaultImports, UseDefaultImports }; enum ContainOption { StringNotContained, StringContained }; @@ -105,17 +127,24 @@ private: enum LintType { LintFile, LintModule }; + static QStringList warningsShouldFailArgs() { + static QStringList args {"-W", "0"}; + return args; + } + QString runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult, const QStringList &extraArgs = QStringList(), bool ignoreSettings = true, - bool addImportDirs = true, bool absolutePath = true); + bool addImportDirs = true, bool absolutePath = true, + const Environment &env = {}); QString runQmllint(const QString &fileToLint, bool shouldSucceed, const QStringList &extraArgs = QStringList(), bool ignoreSettings = true, - bool addImportDirs = true, bool absolutePath = true); + bool addImportDirs = true, bool absolutePath = true, + const Environment &env = {}); void callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings = nullptr, QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, - QList<QQmlJSLogger::Category> *categories = nullptr, bool autoFixable = false, + QList<QQmlJS::LoggerCategory> *categories = nullptr, bool autoFixable = false, LintType type = LintFile); void searchWarnings(const QJsonArray &warnings, const QString &string, @@ -137,7 +166,7 @@ private: void runTest(const QString &testFile, const Result &result, QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, - QList<QQmlJSLogger::Category> *categories = nullptr); + QList<QQmlJS::LoggerCategory> *categories = nullptr); QString m_qmllintPath; QString m_qmljsrootgenPath; @@ -258,6 +287,12 @@ void TestQmllint::testUnqualified_data() Message { QStringLiteral("index is implicitly injected into this delegate. " "Add a required property instead.") } } }; + QTest::newRow("storeSloppy") + << QStringLiteral("UnqualifiedInStoreSloppy.qml") + << Result{ { Message{ QStringLiteral("Unqualified access"), 9, 26} } }; + QTest::newRow("storeStrict") + << QStringLiteral("UnqualifiedInStoreStrict.qml") + << Result{ { Message{ QStringLiteral("Unqualified access"), 9, 52} } }; } void TestQmllint::testUnknownCausesFail() @@ -355,12 +390,12 @@ void TestQmllint::verifyJsRoot() QString currentJsRootContent, generatedJsRootContent; QFile currentJsRoot(currentJsRootPath); - QVERIFY(currentJsRoot.open(QFile::ReadOnly)); + QVERIFY(currentJsRoot.open(QFile::ReadOnly | QIODevice::Text)); currentJsRootContent = QString::fromUtf8(currentJsRoot.readAll()); currentJsRoot.close(); QFile generatedJsRoot(dir.path() + QDir::separator() + "jsroot.qmltypes"); - QVERIFY(generatedJsRoot.open(QFile::ReadOnly)); + QVERIFY(generatedJsRoot.open(QFile::ReadOnly | QIODevice::Text)); generatedJsRootContent = QString::fromUtf8(generatedJsRoot.readAll()); generatedJsRoot.close(); @@ -383,7 +418,7 @@ void TestQmllint::autoqmltypes() { QProcess process; process.setWorkingDirectory(testFile("autoqmltypes")); - process.start(m_qmllintPath, { QStringLiteral("test.qml") }); + process.start(m_qmllintPath, warningsShouldFailArgs() << QStringLiteral("test.qml") ); process.waitForFinished(); @@ -393,6 +428,21 @@ void TestQmllint::autoqmltypes() QVERIFY(process.readAllStandardError() .contains("is not a qmldir file. Assuming qmltypes")); QVERIFY(process.readAllStandardOutput().isEmpty()); + + { + QProcess bare; + bare.setWorkingDirectory(testFile("autoqmltypes")); + bare.start(m_qmllintPath, warningsShouldFailArgs() << QStringLiteral("--bare") << QStringLiteral("test.qml") ); + bare.waitForFinished(); + + const QByteArray errors = bare.readAllStandardError(); + QVERIFY(!errors.contains("is not a qmldir file. Assuming qmltypes")); + QVERIFY(errors.contains("Failed to import TestTest.")); + QVERIFY(bare.readAllStandardOutput().isEmpty()); + + QCOMPARE(bare.exitStatus(), QProcess::NormalExit); + QVERIFY(bare.exitCode() != 0); + } } void TestQmllint::resources() @@ -422,6 +472,17 @@ void TestQmllint::resources() } } +void TestQmllint::multiDirectory() +{ + callQmllint( + testFile("MultiDirectory/qml/Inner.qml"), true, nullptr, + {}, {}, { testFile("MultiDirectory/multi.qrc") }); + + callQmllint( + testFile("MultiDirectory/qml/pages/Page.qml"), true, nullptr, + {}, {}, { testFile("MultiDirectory/multi.qrc") }); +} + void TestQmllint::dirtyQmlCode_data() { QTest::addColumn<QString>("filename"); @@ -444,12 +505,12 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("MemberNotFound") << QStringLiteral("memberNotFound.qml") << Result { { Message { - QStringLiteral("Property \"foo\" not found on type \"QtObject\""), 6, + QStringLiteral("Member \"foo\" not found on type \"QtObject\""), 6, 31 } } }; QTest::newRow("UnknownJavascriptMethd") << QStringLiteral("unknownJavascriptMethod.qml") << Result { { Message { - QStringLiteral("Property \"foo2\" not found on type \"Methods\""), 5, + QStringLiteral("Member \"foo2\" not found on type \"Methods\""), 5, 25 } } }; QTest::newRow("badAlias") << QStringLiteral("badAlias.qml") @@ -463,6 +524,12 @@ void TestQmllint::dirtyQmlCode_data() QStringLiteral("Invalid alias expression. Only IDs and field member " "expressions can be aliased"), 5, 26 } } }; + QTest::newRow("badAliasNotAnExpression") + << QStringLiteral("badAliasNotAnExpression.qml") + << Result { { Message { + QStringLiteral("Invalid alias expression. Only IDs and field member " + "expressions can be aliased"), + 4, 30 } } }; QTest::newRow("aliasCycle1") << QStringLiteral("aliasCycle.qml") << Result { { Message { QStringLiteral("Alias \"b\" is part of an alias cycle"), @@ -485,17 +552,17 @@ void TestQmllint::dirtyQmlCode_data() 9, 34 } } }; QTest::newRow("badParent") << QStringLiteral("badParent.qml") - << Result { { Message { QStringLiteral("Property \"rrr\" not found on type \"Item\""), + << Result { { Message { QStringLiteral("Member \"rrr\" not found on type \"Item\""), 5, 34 } } }; QTest::newRow("parentIsComponent") << QStringLiteral("parentIsComponent.qml") << Result { { Message { - QStringLiteral("Property \"progress\" not found on type \"QQuickItem\""), 7, + QStringLiteral("Member \"progress\" not found on type \"QQuickItem\""), 7, 39 } } }; QTest::newRow("badTypeAssertion") << QStringLiteral("badTypeAssertion.qml") << Result { { Message { - QStringLiteral("Property \"rrr\" not found on type \"QQuickItem\""), 5, + QStringLiteral("Member \"rrr\" not found on type \"QQuickItem\""), 5, 39 } } }; QTest::newRow("incompleteQmltypes") << QStringLiteral("incompleteQmltypes.qml") @@ -504,7 +571,7 @@ void TestQmllint::dirtyQmlCode_data() 26 } } }; QTest::newRow("incompleteQmltypes2") << QStringLiteral("incompleteQmltypes2.qml") - << Result { { Message { QStringLiteral("Property \"weDontKnowIt\" " + << Result { { Message { QStringLiteral("Member \"weDontKnowIt\" " "not found on type \"CustomPalette\""), 5, 35 } } }; QTest::newRow("incompleteQmltypes3") @@ -526,11 +593,11 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("javascriptMethodsInModule") << QStringLiteral("javascriptMethodsInModuleBad.qml") << Result { { Message { - QStringLiteral("Property \"unknownFunc\" not found on type \"Foo\""), 5, + QStringLiteral("Member \"unknownFunc\" not found on type \"Foo\""), 5, 21 } } }; QTest::newRow("badEnumFromQtQml") << QStringLiteral("badEnumFromQtQml.qml") - << Result { { Message { QStringLiteral("Property \"Linear123\" not " + << Result { { Message { QStringLiteral("Member \"Linear123\" not " "found on type \"QQmlEasingEnums\""), 4, 30 } } }; QTest::newRow("anchors3") @@ -548,13 +615,13 @@ void TestQmllint::dirtyQmlCode_data() "unknown grouped property scope nanchors.") } } }; QTest::newRow("badAliasObject") << QStringLiteral("badAliasObject.qml") - << Result { { Message { QStringLiteral("Property \"wrongwrongwrong\" not " + << Result { { Message { QStringLiteral("Member \"wrongwrongwrong\" not " "found on type \"QtObject\""), 8, 40 } } }; QTest::newRow("badScript") << QStringLiteral("badScript.qml") << Result { { Message { QStringLiteral( - "Property \"stuff\" not found on type \"Empty\""), + "Member \"stuff\" not found on type \"Empty\""), 5, 21 } } }; QTest::newRow("badScriptOnAttachedProperty") << QStringLiteral("badScript.attached.qml") @@ -565,7 +632,7 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("segFault (bad)") << QStringLiteral("SegFault.bad.qml") << Result { { Message { QStringLiteral( - "Property \"foobar\" not found on type \"QQuickScreenAttached\"") } } }; + "Member \"foobar\" not found on type \"QQuickScreenAttached\"") } } }; QTest::newRow("VariableUsedBeforeDeclaration") << QStringLiteral("useBeforeDeclaration.qml") << Result { { Message { @@ -585,7 +652,7 @@ void TestQmllint::dirtyQmlCode_data() "than the signal it handles.") } } }; QTest::newRow("OnAssignment") << QStringLiteral("onAssignment.qml") << Result { { Message { QStringLiteral( - "Property \"loops\" not found on type \"bool\"") } } }; + "Member \"loops\" not found on type \"bool\"") } } }; QTest::newRow("BadAttached") << QStringLiteral("badAttached.qml") << Result { { Message { QStringLiteral( "unknown attached property scope WrongAttached.") } } }; @@ -702,13 +769,13 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("badAttachedProperty") << QStringLiteral("badAttachedProperty.qml") << Result { { Message { - QStringLiteral("Property \"progress\" not found on type \"TestType\"") } } }; + QStringLiteral("Member \"progress\" not found on type \"TestType\"") } } }; QTest::newRow("badAttachedPropertyNested") << QStringLiteral("badAttachedPropertyNested.qml") << Result { { Message { QStringLiteral( - "Property \"progress\" not found on type \"QObject\""), + "Member \"progress\" not found on type \"QObject\""), 12, 41 } }, - { Message { QString("Property \"progress\" not found on type \"QObject\""), + { Message { QString("Member \"progress\" not found on type \"QObject\""), 6, 37 } } }; QTest::newRow("badAttachedPropertyTypeString") << QStringLiteral("badAttachedPropertyTypeString.qml") @@ -780,6 +847,14 @@ singleTicks: ' \\' \\\\' expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", 16, 27 } }, { Result::ExitsNormally, Result::AutoFixable } }; + QTest::addRow("multifix") + << QStringLiteral("multifix.qml") + << Result { { + Message { QStringLiteral("Unqualified access"), 7, 19, QtWarningMsg}, + Message { QStringLiteral("Unqualified access"), 11, 19, QtWarningMsg}, + }, {}, { + Message { QStringLiteral("pragma ComponentBehavior: Bound\n"), 1, 1 } + }, { Result::AutoFixable }}; QTest::newRow("unresolvedType") << QStringLiteral("unresolvedType.qml") << Result { { Message { QStringLiteral( @@ -812,7 +887,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("QtQuick.Window 2.0") << QStringLiteral("qtquickWindow20.qml") << Result { { Message { QStringLiteral( - "Property \"window\" not found on type \"QQuickWindow\"") } } }; + "Member \"window\" not found on type \"QQuickWindow\"") } } }; QTest::newRow("unresolvedAttachedType") << QStringLiteral("unresolvedAttachedType.qml") << Result { { Message { QStringLiteral( @@ -879,22 +954,22 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("enumInvalid") << QStringLiteral("enumInvalid.qml") << Result { { Message { - QStringLiteral("Property \"red\" not found on type \"QtObject\"") } } }; + QStringLiteral("Member \"red\" not found on type \"QtObject\"") } } }; QTest::newRow("inaccessibleId") << QStringLiteral("inaccessibleId.qml") << Result { { Message { - QStringLiteral("Property \"objectName\" not found on type \"int\"") } } }; + QStringLiteral("Member \"objectName\" not found on type \"int\"") } } }; QTest::newRow("inaccessibleId2") << QStringLiteral("inaccessibleId2.qml") << Result { { Message { - QStringLiteral("Property \"objectName\" not found on type \"int\"") } } }; + QStringLiteral("Member \"objectName\" not found on type \"int\"") } } }; QTest::newRow("unknownTypeCustomParser") << QStringLiteral("unknownTypeCustomParser.qml") << Result { { Message { QStringLiteral("TypeDoesNotExist was not found.") } } }; QTest::newRow("nonNullStored") << QStringLiteral("nonNullStored.qml") << Result { { Message { QStringLiteral( - "Property \"objectName\" not found on type \"Foozle\"") } }, + "Member \"objectName\" not found on type \"Foozle\"") } }, { Message { QStringLiteral("Unqualified access") } } }; QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-bindable") << QStringLiteral("badCppPropertyChangeHandlers1.qml") @@ -936,13 +1011,13 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("didYouMean(property)") << QStringLiteral("didYouMeanProperty.qml") << Result { { Message { QStringLiteral( - "Property \"hoight\" not found on type \"Rectangle\"") }, + "Member \"hoight\" not found on type \"Rectangle\"") }, {}, { Message { QStringLiteral("height") } } } }; QTest::newRow("didYouMean(propertyCall)") << QStringLiteral("didYouMeanPropertyCall.qml") << Result { - { Message { QStringLiteral("Property \"lgg\" not found on type \"Console\"") }, + { Message { QStringLiteral("Member \"lgg\" not found on type \"Console\"") }, {}, { Message { QStringLiteral("log") } } } }; @@ -955,7 +1030,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("didYouMean(enum)") << QStringLiteral("didYouMeanEnum.qml") << Result { { Message { QStringLiteral( - "Property \"Readx\" not found on type \"QQuickImage\"") }, + "Member \"Readx\" not found on type \"QQuickImage\"") }, {}, { Message { QStringLiteral("Ready") } } } }; QTest::newRow("nullBinding") << QStringLiteral("nullBinding.qml") @@ -1020,9 +1095,8 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("NotScopedEnumCpp") << QStringLiteral("NotScopedEnumCpp.qml") << Result{ { Message{ - QStringLiteral( - "Type is an unscoped enum. You cannot access \"V1\" from here."), - 5, 57 } } }; + QStringLiteral("You cannot access unscoped enum \"TheEnum\" from here."), 5, + 49 } } }; QTest::newRow("unresolvedArrayBinding") << QStringLiteral("unresolvedArrayBinding.qml") @@ -1044,6 +1118,56 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", "in nested components."), 0, 0, QtInfoMsg } }, Result::AutoFixable }; + QTest::newRow("IsNotAnEntryOfEnum") + << QStringLiteral("IsNotAnEntryOfEnum.qml") + << Result{ { + Message { + QStringLiteral("Member \"Mode\" not found on type \"Item\""), 12, + 29, QtWarningMsg }, + Message{ + QStringLiteral("\"Hour\" is not an entry of enum \"Mode\"."), 13, + 62, QtInfoMsg } + }, + {}, + { Message{ QStringLiteral("Hours") } } + }; + + QTest::newRow("StoreNameMethod") + << QStringLiteral("storeNameMethod.qml") + << Result { { Message { QStringLiteral("Cannot assign to method foo") } } }; + + QTest::newRow("CoerceToVoid") + << QStringLiteral("coercetovoid.qml") + << Result { { Message { + QStringLiteral("Function without return type annotation returns double") + } } }; + + QTest::newRow("lowerCaseQualifiedImport") + << QStringLiteral("lowerCaseQualifiedImport.qml") + << Result{ { + Message{ u"Import qualifier 'test' must start with a capital letter."_s }, + Message{ + u"Namespace 'test' of 'test.Rectangle' must start with an upper case letter."_s }, + } }; + QTest::newRow("lowerCaseQualifiedImport2") + << QStringLiteral("lowerCaseQualifiedImport2.qml") + << Result{ { + Message{ u"Import qualifier 'test' must start with a capital letter."_s }, + Message{ + u"Namespace 'test' of 'test.Item' must start with an upper case letter."_s }, + Message{ + u"Namespace 'test' of 'test.Rectangle' must start with an upper case letter."_s }, + Message{ + u"Namespace 'test' of 'test.color' must start with an upper case letter."_s }, + Message{ + u"Namespace 'test' of 'test.Grid' must start with an upper case letter."_s }, + } }; + QTest::newRow("notQmlRootMethods") + << QStringLiteral("notQmlRootMethods.qml") + << Result{ { + Message{ u"Member \"deleteLater\" not found on type \"QtObject\""_s }, + Message{ u"Member \"destroyed\" not found on type \"QtObject\""_s }, + } }; } void TestQmllint::dirtyQmlCode() @@ -1167,6 +1291,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("QVariant") << QStringLiteral("qvariant.qml"); QTest::newRow("Accessible") << QStringLiteral("accessible.qml"); QTest::newRow("qjsroot") << QStringLiteral("qjsroot.qml"); + QTest::newRow("qmlRootMethods") << QStringLiteral("qmlRootMethods.qml"); QTest::newRow("InlineComponent") << QStringLiteral("inlineComponent.qml"); QTest::newRow("InlineComponentWithComponents") << QStringLiteral("inlineComponentWithComponents.qml"); QTest::newRow("InlineComponentsChained") << QStringLiteral("inlineComponentsChained.qml"); @@ -1216,6 +1341,21 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("propertyWithOn") << QStringLiteral("switcher.qml"); QTest::newRow("constructorProperty") << QStringLiteral("constructorProperty.qml"); QTest::newRow("onlyMajorVersion") << QStringLiteral("onlyMajorVersion.qml"); + QTest::newRow("attachedImportUse") << QStringLiteral("attachedImportUse.qml"); + QTest::newRow("VariantMapGetPropertyLookup") << QStringLiteral("variantMapLookup.qml"); + QTest::newRow("StringToDateTime") << QStringLiteral("stringToDateTime.qml"); + QTest::newRow("ScriptInTemplate") << QStringLiteral("scriptInTemplate.qml"); + QTest::newRow("AddressableValue") << QStringLiteral("addressableValue.qml"); + QTest::newRow("WriteListProperty") << QStringLiteral("writeListProperty.qml"); + QTest::newRow("dontConfuseMemberPrintWithGlobalPrint") << QStringLiteral("findMemberPrint.qml"); + QTest::newRow("groupedAttachedLayout") << QStringLiteral("groupedAttachedLayout.qml"); + QTest::newRow("QQmlScriptString") << QStringLiteral("scriptstring.qml"); + QTest::newRow("QEventPoint") << QStringLiteral("qEventPoint.qml"); + QTest::newRow("locale") << QStringLiteral("locale.qml"); + QTest::newRow("constInvokable") << QStringLiteral("useConstInvokable.qml"); + QTest::newRow("dontCheckJSTypes") << QStringLiteral("dontCheckJSTypes.qml"); + QTest::newRow("jsonObjectIsRecognized") << QStringLiteral("jsonObjectIsRecognized.qml"); + QTest::newRow("jsonArrayIsRecognized") << QStringLiteral("jsonArrayIsRecognized.qml"); } void TestQmllint::cleanQmlCode() @@ -1239,10 +1379,10 @@ void TestQmllint::compilerWarnings_data() QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << Result::clean() << true; QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << Result::clean() << true; - QTest::newRow("shadowable") << QStringLiteral("shadowable.qml") - << Result { { Message { QStringLiteral( - "with type NotSoSimple can be shadowed") } } } - << true; + QTest::newRow("shadowable") + << QStringLiteral("shadowable.qml") + << Result { { Message {QStringLiteral("with type NotSoSimple can be shadowed") } } } + << true; QTest::newRow("tooFewParameters") << QStringLiteral("tooFewParams.qml") << Result { { Message { QStringLiteral("No matching override found") } } } << true; @@ -1261,6 +1401,51 @@ void TestQmllint::compilerWarnings_data() << Result { { { QStringLiteral( "Functions without type annotations won't be compiled") } } } << true; + QTest::newRow("generalizedGroupHint") + << QStringLiteral("generalizedGroupHint.qml") + << Result { { { QStringLiteral( + "Cannot resolve property type for binding on myColor. " + "You may want use ID-based grouped properties here.") } } } + << true; + QTest::newRow("invalidIdLookup") + << QStringLiteral("invalidIdLookup.qml") + << Result { { { + QStringLiteral("Cannot retrieve a non-object type by ID: stateMachine") + } } } + << true; + QTest::newRow("returnTypeAnnotation-component") + << QStringLiteral("returnTypeAnnotation_component.qml") + << Result{ { { "Could not compile function comp: function without return type " + "annotation returns (component in" }, + { "returnTypeAnnotation_component.qml)::c with type Comp. " + "This may prevent proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-enum") + << QStringLiteral("returnTypeAnnotation_enum.qml") + << Result{ { { "Could not compile function enumeration: function without return type " + "annotation returns QQuickText::HAlignment::AlignRight. " + "This may prevent proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-method") + << QStringLiteral("returnTypeAnnotation_method.qml") + << Result{ { { "Could not compile function method: function without return type " + "annotation returns (component in " }, // Don't check the build folder path + { "returnTypeAnnotation_method.qml)::f(...). This may " + "prevent proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-property") + << QStringLiteral("returnTypeAnnotation_property.qml") + << Result{ { { "Could not compile function prop: function without return type " + "annotation returns (component in " }, // Don't check the build folder path + { "returnTypeAnnotation_property.qml)::i with type int. This may prevent " + "proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-type") + << QStringLiteral("returnTypeAnnotation_type.qml") + << Result{ { { "Could not compile function type: function without return type " + "annotation returns double. This may prevent proper compilation to " + "Cpp." } } } + << true; } void TestQmllint::compilerWarnings() @@ -1273,11 +1458,15 @@ void TestQmllint::compilerWarnings() auto categories = QQmlJSLogger::defaultCategories(); - auto category = std::find(categories.begin(), categories.end(), qmlCompiler); + auto category = std::find_if(categories.begin(), categories.end(), [](const QQmlJS::LoggerCategory& category) { + return category.id() == qmlCompiler; + }); Q_ASSERT(category != categories.end()); - if (enableCompilerWarnings) - category->setLevel(u"warning"_s); + if (enableCompilerWarnings) { + category->setLevel(QtWarningMsg); + category->setIgnored(false); + } runTest(filename, result, {}, {}, {}, UseDefaultImports, &categories); } @@ -1285,7 +1474,7 @@ void TestQmllint::compilerWarnings() QString TestQmllint::runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult, const QStringList &extraArgs, bool ignoreSettings, - bool addImportDirs, bool absolutePath) + bool addImportDirs, bool absolutePath, const Environment &env) { auto qmlImportDir = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); QStringList args; @@ -1311,6 +1500,11 @@ QString TestQmllint::runQmllint(const QString &fileToLint, QString errors; auto verify = [&](bool isSilent) { QProcess process; + QProcessEnvironment processEnv = QProcessEnvironment::systemEnvironment(); + for (const auto &entry : env) + processEnv.insert(entry.first, entry.second); + + process.setProcessEnvironment(processEnv); process.setWorkingDirectory(QFileInfo(absoluteFilePath).absolutePath()); process.start(m_qmllintPath, args); handleResult(process); @@ -1353,7 +1547,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint, QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, const QStringList &extraArgs, bool ignoreSettings, - bool addImportDirs, bool absolutePath) + bool addImportDirs, bool absolutePath, const Environment &env) { return runQmllint( fileToLint, @@ -1366,13 +1560,13 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, else QVERIFY(process.exitCode() != 0); }, - extraArgs, ignoreSettings, addImportDirs, absolutePath); + extraArgs, ignoreSettings, addImportDirs, absolutePath, env); } void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings, QStringList importPaths, QStringList qmldirFiles, QStringList resources, DefaultImportOption defaultImports, - QList<QQmlJSLogger::Category> *categories, bool autoFixable, + QList<QQmlJS::LoggerCategory> *categories, bool autoFixable, LintType type) { QJsonArray jsonOutput; @@ -1382,18 +1576,22 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs QQmlJSLinter::LintResult lintResult; + const QStringList resolvedImportPaths = defaultImports == UseDefaultImports + ? m_defaultImportPaths + importPaths + : importPaths; if (type == LintFile) { + const QList<QQmlJS::LoggerCategory> resolvedCategories = + categories != nullptr ? *categories : QQmlJSLogger::defaultCategories(); lintResult = m_linter.lintFile( - lintedFile, nullptr, true, &jsonOutput, - defaultImports == UseDefaultImports ? m_defaultImportPaths + importPaths - : importPaths, - qmldirFiles, resources, - categories != nullptr ? *categories : QQmlJSLogger::defaultCategories()); + lintedFile, nullptr, true, &jsonOutput, resolvedImportPaths, qmldirFiles, + resources, resolvedCategories); } else { - lintResult = m_linter.lintModule(fileToLint, true, &jsonOutput); + lintResult = + m_linter.lintModule(fileToLint, true, &jsonOutput, resolvedImportPaths, resources); } bool success = lintResult == QQmlJSLinter::LintSuccess; + QEXPECT_FAIL("qtquickdialog", "Will fail until QTBUG-104091 is implemented", Abort); QVERIFY2(success == shouldSucceed, QJsonDocument(jsonOutput).toJson()); if (warnings) { @@ -1425,7 +1623,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs if (QFileInfo(fixedPath).exists()) { QFile fixedFile(fixedPath); - fixedFile.open(QFile::ReadOnly); + QVERIFY(fixedFile.open(QFile::ReadOnly)); QString fixedFileContents = QString::fromUtf8(fixedFile.readAll()); #ifdef Q_OS_WIN fixedCode = fixedCode.replace(u"\r\n"_s, u"\n"_s); @@ -1447,7 +1645,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs void TestQmllint::runTest(const QString &testFile, const Result &result, QStringList importDirs, QStringList qmltypesFiles, QStringList resources, DefaultImportOption defaultImports, - QList<QQmlJSLogger::Category> *categories) + QList<QQmlJS::LoggerCategory> *categories) { QJsonArray warnings; callQmllint(testFile, result.flags.testFlag(Result::Flag::ExitsNormally), &warnings, importDirs, @@ -1604,17 +1802,17 @@ void TestQmllint::requiredProperty() void TestQmllint::settingsFile() { - QVERIFY(runQmllint("settings/unqualifiedSilent/unqualified.qml", true, QStringList(), false) + QVERIFY(runQmllint("settings/unqualifiedSilent/unqualified.qml", true, warningsShouldFailArgs(), false) .isEmpty()); - QVERIFY(runQmllint("settings/unusedImportWarning/unused.qml", false, QStringList(), false) + QVERIFY(runQmllint("settings/unusedImportWarning/unused.qml", false, warningsShouldFailArgs(), false) .contains(QStringLiteral("Warning: %1:2:1: Unused import") .arg(testFile("settings/unusedImportWarning/unused.qml")))); - QVERIFY(runQmllint("settings/bare/bare.qml", false, {}, false, false) + QVERIFY(runQmllint("settings/bare/bare.qml", false, warningsShouldFailArgs(), false, false) .contains(QStringLiteral("Failed to find the following builtins: " - "builtins.qmltypes, jsroot.qmltypes"))); - QVERIFY(runQmllint("settings/qmltypes/qmltypes.qml", false, QStringList(), false) + "jsroot.qmltypes, builtins.qmltypes"))); + QVERIFY(runQmllint("settings/qmltypes/qmltypes.qml", false, warningsShouldFailArgs(), false) .contains(QStringLiteral("not a qmldir file. Assuming qmltypes."))); - QVERIFY(runQmllint("settings/qmlimports/qmlimports.qml", true, QStringList(), false).isEmpty()); + QVERIFY(runQmllint("settings/qmlimports/qmlimports.qml", true, warningsShouldFailArgs(), false).isEmpty()); } void TestQmllint::additionalImplicitImport() @@ -1623,17 +1821,56 @@ void TestQmllint::additionalImplicitImport() const auto guard = qScopeGuard([this]() {m_linter.clearCache(); }); runTest("additionalImplicitImport.qml", Result::clean(), {}, {}, { testFile("implicitImportResource.qrc") }); +} + +void TestQmllint::qrcUrlImport() +{ + const auto guard = qScopeGuard([this]() { m_linter.clearCache(); }); + QJsonArray warnings; + callQmllint(testFile("untitled/main.qml"), true, &warnings, {}, {}, + { testFile("untitled/qrcUrlImport.qrc") }); + checkResult(warnings, Result::clean()); } -void TestQmllint::attachedPropertyReuse() +void TestQmllint::incorrectImportFromHost_data() { + QTest::addColumn<QString>("filename"); + QTest::addColumn<Result>("result"); + + QTest::newRow("NonexistentFile") + << QStringLiteral("importNonexistentFile.qml") + << Result{ { Message{ + QStringLiteral("File or directory you are trying to import does not exist"), + 1, 1 } } }; +#ifndef Q_OS_WIN + // there is no /dev/null device on Win + QTest::newRow("NullDevice") + << QStringLiteral("importNullDevice.qml") + << Result{ { Message{ QStringLiteral("is neither a file nor a directory. Are sure the " + "import path is correct?"), + 1, 1 } } }; +#endif +} + +void TestQmllint::incorrectImportFromHost() +{ + QFETCH(QString, filename); + QFETCH(Result, result); + + runTest(filename, result); +} +void TestQmllint::attachedPropertyReuse() +{ auto categories = QQmlJSLogger::defaultCategories(); - auto category = std::find(categories.begin(), categories.end(), qmlAttachedPropertyReuse); + auto category = std::find_if(categories.begin(), categories.end(), [](const QQmlJS::LoggerCategory& category) { + return category.id() == qmlAttachedPropertyReuse; + }); Q_ASSERT(category != categories.end()); - category->setLevel(u"warning"_s); + category->setLevel(QtWarningMsg); + category->setIgnored(false); runTest("attachedPropNotReused.qml", Result { { Message { QStringLiteral("Using attached type QQuickKeyNavigationAttached " "already initialized in a parent " @@ -1641,6 +1878,24 @@ void TestQmllint::attachedPropertyReuse() {}, {}, {}, UseDefaultImports, &categories); runTest("attachedPropEnum.qml", Result::clean(), {}, {}, {}, UseDefaultImports, &categories); + runTest("MyStyle/ToolBar.qml", Result { + { + Message { + "Using attached type MyStyle already initialized in a parent scope"_L1, + 10, + 16 + } + }, + {}, + { + Message { + "Reference it by id instead"_L1, + 10, + 16 + } + }, + Result::AutoFixable + }); } void TestQmllint::missingBuiltinsNoCrash() @@ -1662,13 +1917,13 @@ void TestQmllint::missingBuiltinsNoCrash() checkResult(warnings, Result { { Message { QStringLiteral("Failed to find the following builtins: " - "builtins.qmltypes, jsroot.qmltypes") } } }); + "jsroot.qmltypes, builtins.qmltypes") } } }); } void TestQmllint::absolutePath() { - QString absPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, true); - QString relPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, false); + QString absPathOutput = runQmllint("memberNotFound.qml", false, warningsShouldFailArgs(), true, true, true); + QString relPathOutput = runQmllint("memberNotFound.qml", false, warningsShouldFailArgs(), true, true, false); const QString absolutePath = QFileInfo(testFile("memberNotFound.qml")).absoluteFilePath(); QVERIFY(absPathOutput.contains(absolutePath)); @@ -1683,10 +1938,14 @@ void TestQmllint::importMultipartUri() void TestQmllint::lintModule_data() { QTest::addColumn<QString>("module"); + QTest::addColumn<QStringList>("importPaths"); + QTest::addColumn<QStringList>("resources"); QTest::addColumn<Result>("result"); QTest::addRow("Things") << u"Things"_s + << QStringList() + << QStringList() << Result { { Message { u"Type \"QPalette\" not found. Used in SomethingEntirelyStrange.palette"_s, @@ -1696,16 +1955,30 @@ void TestQmllint::lintModule_data() }; QTest::addRow("missingQmltypes") << u"Fake5Compat.GraphicalEffects.private"_s + << QStringList() + << QStringList() << Result { { Message { u"QML types file does not exist"_s } } }; + + QTest::addRow("moduleWithQrc") + << u"moduleWithQrc"_s + << QStringList({ testFile("hidden") }) + << QStringList({ + testFile("hidden/qmake_moduleWithQrc.qrc"), + testFile("hidden/moduleWithQrc_raw_qml_0.qrc") + }) + << Result::clean(); } void TestQmllint::lintModule() { QFETCH(QString, module); + QFETCH(QStringList, importPaths); + QFETCH(QStringList, resources); QFETCH(Result, result); QJsonArray warnings; - callQmllint(module, false, &warnings, {}, {}, {}, {}, nullptr, false, LintModule); + callQmllint(module, result.flags & Result::ExitsNormally, &warnings, importPaths, {}, resources, + UseDefaultImports, nullptr, false, LintModule); checkResult(warnings, result); } @@ -1739,6 +2012,25 @@ void TestQmllint::testLineEndings() } } +void TestQmllint::valueTypesFromString() +{ + runTest("valueTypesFromString.qml", + Result{ { + Message{ + u"Binding is not supported: Type QSizeF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s }, + Message{ + u"Binding is not supported: Type QRectF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s }, + Message{ + u"Binding is not supported: Type QPointF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s }, + }, + { /*bad messages */ }, + { + Message{ u"({ width: 30, height: 50 })"_s }, + Message{ u"({ x: 10, y: 20, width: 30, height: 50 })"_s }, + Message{ u"({ x: 30, y: 50 })"_s }, + } }); +} + #if QT_CONFIG(library) void TestQmllint::testPlugin() { @@ -1780,6 +2072,9 @@ void TestQmllint::testPlugin() Result { { Message { u"QtQuick.Controls and NO QtQuick present"_s } } }); // Verify that none of the passes do anything when they're not supposed to runTest("nothing_pluginTest.qml", Result::clean()); + + QVERIFY(runQmllint("settings/plugin/elemenpass_pluginSettingTest.qml", true, QStringList(), false) + .isEmpty()); } // TODO: Eventually tests for (real) plugins need to be moved into a separate file @@ -1835,7 +2130,7 @@ void TestQmllint::quickPlugin() Message { u"SplitView attached property only works with Items"_s }, Message { u"ScrollIndicator must be attached to a Flickable"_s }, Message { u"ScrollBar must be attached to a Flickable or ScrollView"_s }, - Message { u"Accessible must be attached to an Item"_s }, + Message { u"Accessible must be attached to an Item or an Action"_s }, Message { u"EnterKey attached property only works with Items"_s }, Message { u"LayoutDirection attached property only works with Items and Windows"_s }, @@ -1885,8 +2180,108 @@ void TestQmllint::quickPlugin() runTest("pluginQuick_attachedClean.qml", Result::clean()); runTest("pluginQuick_attachedIgnore.qml", Result::clean()); runTest("pluginQuick_noCrashOnUneresolved.qml", Result {}); // we don't care about the specific warnings + + runTest("pluginQuick_propertyChangesParsed.qml", + Result { { + Message { + u"Property \"myColor\" is custom-parsed in PropertyChanges. " + "You should phrase this binding as \"foo.myColor: Qt.rgba(0.5, ...\""_s, + 12, 30 + }, + Message { + u"You should remove any bindings on the \"target\" property and avoid " + "custom-parsed bindings in PropertyChanges."_s, + 11, 29 + }, + Message { + u"Unknown property \"notThere\" in PropertyChanges."_s, + 13, 31 + } + } }); + runTest("pluginQuick_propertyChangesInvalidTarget.qml", Result {}); // we don't care about the specific warnings +} + +void TestQmllint::environment_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<bool>("shouldSucceed"); + QTest::addColumn<QStringList>("extraArgs"); + QTest::addColumn<Environment>("env"); + QTest::addColumn<QString>("expectedWarning"); + + const QString fileThatNeedsImportPath = testFile(u"NeedImportPath.qml"_s); + const QString importPath = testFile(u"ImportPath"_s); + const QString invalidImportPath = testFile(u"ImportPathThatDoesNotExist"_s); + const QString noWarningExpected; + + QTest::addRow("missing-import-dir") + << fileThatNeedsImportPath << false << warningsShouldFailArgs() + << Environment{ { u"QML_IMPORT_PATH"_s, importPath } } << noWarningExpected; + + QTest::addRow("import-dir-via-arg") + << fileThatNeedsImportPath << true << QStringList{ u"-I"_s, importPath } + << Environment{ { u"QML_IMPORT_PATH"_s, invalidImportPath } } << noWarningExpected; + + QTest::addRow("import-dir-via-env") + << fileThatNeedsImportPath << true << QStringList{ u"-E"_s } + << Environment{ { u"QML_IMPORT_PATH"_s, importPath } } + << u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s + .arg(importPath); + + QTest::addRow("import-dir-via-env2") + << fileThatNeedsImportPath << true << QStringList{ u"-E"_s } + << Environment{ { u"QML2_IMPORT_PATH"_s, importPath } } + << u"Using import directories passed from the deprecated environment variable \"QML2_IMPORT_PATH\": \"%1\"."_s + .arg(importPath); } + +void TestQmllint::environment() +{ + QFETCH(QString, file); + QFETCH(bool, shouldSucceed); + QFETCH(QStringList, extraArgs); + QFETCH(Environment, env); + QFETCH(QString, expectedWarning); + + const QString output = runQmllint(file, shouldSucceed, extraArgs, false, true, false, env); + if (!expectedWarning.isEmpty()) { + QVERIFY(output.contains(expectedWarning)); + } +} + +void TestQmllint::maxWarnings() +{ + // warnings are not fatal by default + runQmllint(testFile("badScript.qml"), true); + // or when max-warnings is set to -1 + runQmllint(testFile("badScript.qml"), true, {"-W", "-1"}); + // 1 warning => should fail + runQmllint(testFile("badScript.qml"), false, {"--max-warnings", "0"}); + // only 1 warning => should exit normally + runQmllint(testFile("badScript.qml"), true, {"--max-warnings", "1"}); +} + #endif -QTEST_MAIN(TestQmllint) +void TestQmllint::ignoreSettingsNotCommandLineOptions() +{ + const QString importPath = testFile(u"ImportPath"_s); + // makes sure that ignore settings only ignores settings and not command line options like + // "-I". + const QString output = runQmllint(testFile(u"NeedImportPath.qml"_s), true, + QStringList{ u"-I"_s, importPath }, true); + // should not complain about not finding the module that is in importPath + QCOMPARE(output, QString()); +} + +void TestQmllint::backslashedQmldirPath() +{ + const QString qmldirPath + = testFile(u"ImportPath/ModuleInImportPath/qmldir"_s).replace('/', QDir::separator()); + const QString output = runQmllint( + testFile(u"something.qml"_s), true, QStringList{ u"-i"_s, qmldirPath }); + QVERIFY(output.isEmpty()); +} + +QTEST_GUILESS_MAIN(TestQmllint) #include "tst_qmllint.moc" diff --git a/tests/auto/qml/qmlplugindump/CMakeLists.txt b/tests/auto/qml/qmlplugindump/CMakeLists.txt index 9153660ef0..053268bcfb 100644 --- a/tests/auto/qml/qmlplugindump/CMakeLists.txt +++ b/tests/auto/qml/qmlplugindump/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmlplugindump Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlplugindump LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmlplugindump SOURCES tst_qmlplugindump.cpp diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp index 171a185ad7..793191695d 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "dummy.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h index 0fb354d6c4..b72814df82 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DUMMY_H #define DUMMY_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp index eec547cd29..1409876ec6 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "dummy_plugin.h" #include "dummy.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h index 2c3f25ae61..45437e0500 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DUMMY_PLUGIN_H #define DUMMY_PLUGIN_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp index e84355eb11..4c8da8aeab 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "plugin.h" #include "types.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h index f16177351d..02c9d42022 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef PLUGIN_H #define PLUGIN_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h index 5fdc5fa1e4..7a1b73ac90 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPES_H #define TYPES_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp index 04fcfc5edd..9ba1365d40 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "imports.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h index 386cce9f4d..e88deda2f9 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef IMPORTS_H #define IMPORTS_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp index b71719fb6f..c215917c7d 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "imports_plugin.h" #include "imports.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h index 17855d5a22..1353bbd24a 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef IMPORTS_PLUGIN_H #define IMPORTS_PLUGIN_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp index 117b6db82f..6295ec2201 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "versions.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h index 991823aaf3..b5afc16c2a 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef VERSIONS_H #define VERSIONS_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp index 6a554c596d..9c1a2d4a4e 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "versions_plugin.h" #include "versions.h" diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h index 9bc70ca473..aba5f1fac7 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef VERSIONS_PLUGIN_H #define VERSIONS_PLUGIN_H diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp index 50af75380d..2e7b704413 100644 --- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QLibraryInfo> @@ -152,10 +152,10 @@ void tst_qmlplugindump::plugin() QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); - const QString &result = dumper.readAllStandardOutput(); + const QByteArray result = dumper.readAllStandardOutput(); QFile expectedFile(expectedPath); QVERIFY2(expectedFile.open(QIODevice::ReadOnly), qPrintable(expectedFile.errorString())); - const QString expected = expectedFile.readAll(); + const QByteArray expected = expectedFile.readAll(); QCOMPARE(result, expected); } diff --git a/tests/auto/qml/qmlsplitlib/CMakeLists.txt b/tests/auto/qml/qmlsplitlib/CMakeLists.txt index 523ab5cbfd..7c77d5fbae 100644 --- a/tests/auto/qml/qmlsplitlib/CMakeLists.txt +++ b/tests/auto/qml/qmlsplitlib/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmlsplitlib LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + include(GenerateExportHeader) qt_add_library(tst_qmlsplitlib_library @@ -13,6 +19,17 @@ generate_export_header(tst_qmlsplitlib_library) target_link_libraries(tst_qmlsplitlib_library Qt::Core Qt::QmlIntegration) +qt_add_library(tst-qmlsplitlib-library-2 + lib2.h + lib2.cpp +) +qt_autogen_tools_initial_setup(tst-qmlsplitlib-library-2) +target_include_directories(tst-qmlsplitlib-library-2 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # find autogenerated header +generate_export_header(tst-qmlsplitlib-library-2) + +target_link_libraries(tst-qmlsplitlib-library-2 Qt::Core Qt::QmlIntegration) + + qt_internal_add_test(tst_qmlsplitlib SOURCES tst_qmlsplitlib.cpp @@ -23,12 +40,15 @@ qt_internal_add_test(tst_qmlsplitlib qt_autogen_tools_initial_setup(tst_qmlsplitlib) qt6_generate_foreign_qml_types(tst_qmlsplitlib_library tst_qmlsplitlib) +qt6_generate_foreign_qml_types(tst-qmlsplitlib-library-2 tst_qmlsplitlib) + +qt_policy(SET QTP0001 NEW) qt6_add_qml_module(tst_qmlsplitlib URI "SplitLib" - AUTO_RESOURCE_PREFIX QML_FILES main.qml + main2.qml ) -target_link_libraries(tst_qmlsplitlib PRIVATE tst_qmlsplitlib_library) +target_link_libraries(tst_qmlsplitlib PRIVATE tst_qmlsplitlib_library tst-qmlsplitlib-library-2) diff --git a/tests/auto/qml/qmlsplitlib/lib.h b/tests/auto/qml/qmlsplitlib/lib.h index 02fc88894d..b6c394a3b6 100644 --- a/tests/auto/qml/qmlsplitlib/lib.h +++ b/tests/auto/qml/qmlsplitlib/lib.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SPLITLIB_LIB_H #define SPLITLIB_LIB_H diff --git a/tests/auto/qml/qmlsplitlib/lib2.cpp b/tests/auto/qml/qmlsplitlib/lib2.cpp new file mode 100644 index 0000000000..d4489f5c77 --- /dev/null +++ b/tests/auto/qml/qmlsplitlib/lib2.cpp @@ -0,0 +1,5 @@ +#include "lib2.h" + +bool SplitLib2::transmogrify() { return true; } + +#include "moc_lib2.cpp" diff --git a/tests/auto/qml/qmlsplitlib/lib2.h b/tests/auto/qml/qmlsplitlib/lib2.h new file mode 100644 index 0000000000..6d6aaddda5 --- /dev/null +++ b/tests/auto/qml/qmlsplitlib/lib2.h @@ -0,0 +1,30 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef SPLITLIB_LIB2_H +#define SPLITLIB_LIB2_H +#include "tst-qmlsplitlib-library-2_export.h" + +#include <QtQmlIntegration/qqmlintegration.h> +#include <QtCore/qglobal.h> +#include <QObject> + +class TST_QMLSPLITLIB_LIBRARY_2_EXPORT SplitLib2 : public QObject +{ +public: + Q_OBJECT + QML_ELEMENT + + Q_INVOKABLE bool transmogrify(); +}; + + +class TST_QMLSPLITLIB_LIBRARY_2_EXPORT Foo2 : public QObject +{ +public: + Q_OBJECT + QML_NAMED_ELEMENT(Bar2) + QML_SINGLETON +}; + +#endif diff --git a/tests/auto/qml/qmlsplitlib/main.qml b/tests/auto/qml/qmlsplitlib/main.qml index 2309a2e8c3..47be48acc4 100644 --- a/tests/auto/qml/qmlsplitlib/main.qml +++ b/tests/auto/qml/qmlsplitlib/main.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import SplitLib diff --git a/tests/auto/qml/qmlsplitlib/main2.qml b/tests/auto/qml/qmlsplitlib/main2.qml new file mode 100644 index 0000000000..c5081c6627 --- /dev/null +++ b/tests/auto/qml/qmlsplitlib/main2.qml @@ -0,0 +1,8 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import SplitLib + +SplitLib2 { + property string s: Bar2.objectName +} diff --git a/tests/auto/qml/qmlsplitlib/manual_imports.cpp b/tests/auto/qml/qmlsplitlib/manual_imports.cpp index f1b30f8352..dd1c8dbe89 100644 --- a/tests/auto/qml/qmlsplitlib/manual_imports.cpp +++ b/tests/auto/qml/qmlsplitlib/manual_imports.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlextensionplugin.h> diff --git a/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp b/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp index 893ed77ff7..d04a1c6845 100644 --- a/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp +++ b/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp @@ -1,17 +1,19 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QQmlEngine> #include <QQmlComponent> #include <QObject> #include <qtest.h> #include "lib.h" +#include "lib2.h" class tst_splitlib : public QObject { Q_OBJECT private slots: void verifyComponent(); + void verifyComponent2(); }; void tst_splitlib::verifyComponent() @@ -25,5 +27,16 @@ void tst_splitlib::verifyComponent() QVERIFY(lib); } +void tst_splitlib::verifyComponent2() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QStringLiteral("qrc:/qt/qml/SplitLib/main2.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + auto lib = qobject_cast<SplitLib2 *>(o.get()); + QVERIFY(lib); +} + QTEST_MAIN(tst_splitlib) #include "tst_qmlsplitlib.moc" diff --git a/tests/auto/qml/qmltc/BLACKLIST b/tests/auto/qml/qmltc/BLACKLIST deleted file mode 100644 index 004fbeb8d9..0000000000 --- a/tests/auto/qml/qmltc/BLACKLIST +++ /dev/null @@ -1,5 +0,0 @@ -[listView] -qnx ci -# QTBUG-101342 -[listView] -android diff --git a/tests/auto/qml/qmltc/CMakeLists.txt b/tests/auto/qml/qmltc/CMakeLists.txt index 4b86a6c018..1e6c6b9e30 100644 --- a/tests/auto/qml/qmltc/CMakeLists.txt +++ b/tests/auto/qml/qmltc/CMakeLists.txt @@ -1,8 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmltc LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + add_subdirectory(QmltcTests) add_subdirectory(NamespaceTest/Subfolder) +add_subdirectory(QmltcExportedTests) +add_subdirectory(QmltcExportedNoFileNameTest) set(test_sources nameconflict.h nameconflict.cpp @@ -18,6 +26,11 @@ set(qmltc_module_libs # automatic type registration that comes from the plugin) qmltc_test_moduleplugin qmltc_namespace_test_module + qmltc_namespace_test_moduleplugin + qmltc_exported_tests_module + qmltc_exported_tests_moduleplugin + qmltc_exported_no_file_name_test_module + qmltc_exported_no_file_name_test_moduleplugin ) qt_internal_add_test(tst_qmltc_diskcache SOURCES ${test_sources} @@ -25,6 +38,7 @@ qt_internal_add_test(tst_qmltc_diskcache ) target_compile_definitions(tst_qmltc_diskcache PRIVATE QMLTC_TESTS_DISABLE_CACHE=0 + QT_NO_URL_CAST_FROM_STRING #QTBUG-113875 ) qt_internal_add_test(tst_qmltc_nodiskcache @@ -33,6 +47,7 @@ qt_internal_add_test(tst_qmltc_nodiskcache ) target_compile_definitions(tst_qmltc_nodiskcache PRIVATE QMLTC_TESTS_DISABLE_CACHE=1 + QT_NO_URL_CAST_FROM_STRING #QTBUG-113875 ) # Add qmltc documentation example to the tests. This is not beautiful but allows diff --git a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt index f661cdea1e..daf1139a8f 100644 --- a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt +++ b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt @@ -24,9 +24,10 @@ qt_autogen_tools_initial_setup(qmltc_namespace_test_module) target_link_libraries(qmltc_namespace_test_module PUBLIC ${common_libraries}) +qt_policy(SET QTP0001 NEW) + qt6_add_qml_module(qmltc_namespace_test_module URI NamespaceTest.Subfolder - AUTO_RESOURCE_PREFIX SOURCES ${cpp_sources} QML_FILES diff --git a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml index 101e9f69a1..3576353b02 100644 --- a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml +++ b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml @@ -1,5 +1,5 @@ import QtQuick Item { - property string data + property string data: "Hello from namespace" } diff --git a/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt new file mode 100644 index 0000000000..47f920129a --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_add_library(qmltc_exported_no_file_name_test_module STATIC) +qt_autogen_tools_initial_setup(qmltc_exported_no_file_name_test_module) + +set(common_libraries + Qt::QuickPrivate +) + +target_link_libraries(qmltc_exported_no_file_name_test_module PUBLIC ${common_libraries}) + +qt_policy(SET QTP0001 NEW) + +qt6_add_qml_module(qmltc_exported_no_file_name_test_module + URI QmltcExportedNoFileNameTest + QML_FILES + HelloExportedWorldNoFileName.qml + DEPENDENCIES + Qt::Quick + ENABLE_TYPE_COMPILER + QMLTC_EXPORT_DIRECTIVE "Q_DECL_EXPORT" + # explicitly omitting QMLTC_EXPORT_FILE_NAME +) + +target_include_directories(qmltc_exported_no_file_name_test_module PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + +qt_autogen_tools_initial_setup(qmltc_exported_no_file_name_test_moduleplugin) diff --git a/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml new file mode 100644 index 0000000000..89986df5a6 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property string myString: "Hello! I should be exported by qmltc" +} diff --git a/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt new file mode 100644 index 0000000000..709297bed4 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_add_library(qmltc_exported_tests_module STATIC) +qt_autogen_tools_initial_setup(qmltc_exported_tests_module) + +include(GenerateExportHeader) +generate_export_header(qmltc_exported_tests_module) + +set(common_libraries + Qt::QuickPrivate +) + +target_link_libraries(qmltc_exported_tests_module PUBLIC ${common_libraries}) + +qt_policy(SET QTP0001 NEW) + +qt6_add_qml_module(qmltc_exported_tests_module + URI QmltcExportedTests + QML_FILES + HelloExportedWorld.qml + DEPENDENCIES + Qt::Quick + ENABLE_TYPE_COMPILER + QMLTC_EXPORT_DIRECTIVE "QMLTC_EXPORTED_TESTS_MODULE_EXPORT" + QMLTC_EXPORT_FILE_NAME "qmltc_exported_tests_module_export.h" +) + +target_include_directories(qmltc_exported_tests_module PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + +qt_autogen_tools_initial_setup(qmltc_exported_tests_moduleplugin) diff --git a/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml b/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml new file mode 100644 index 0000000000..5e6886bced --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property string myString: "Hello! I should be exported by qmltc" +} diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt index 870d21f61d..4c472ec4ab 100644 --- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt +++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt @@ -22,6 +22,8 @@ set(cpp_sources cpptypes/singletontype.h cpptypes/singletontype.cpp cpptypes/typewithnamespace.h cpptypes/typewithnamespace.cpp cpptypes/typewithsignal.h + cpptypes/custominitialization.h + cpptypes/typewithrequiredproperties.h ) set(qml_sources @@ -44,11 +46,16 @@ set(qml_sources regexpBindings.qml AliasBase.qml aliasAssignments.qml + Connections.qml qtbug103956/SubComponent.qml qtbug103956/MainComponent.qml qtbug103956/qtbug103956_main.qml + qtbug120700_main.qml + + qtbug123476.qml + signalHandlers.qml javaScriptFunctions.qml changingBindings.qml @@ -108,6 +115,9 @@ set(qml_sources inlineComponentsFromDifferentFiles.qml singletons.qml mySignals.qml + stringToUrl.qml + myCheckBox.qml + signalConnections.qml # support types: DefaultPropertySingleChild.qml @@ -125,6 +135,8 @@ set(qml_sources NamespacedTypes.qml badFile.qml + + requiredProperties.qml ) set(js_sources @@ -135,10 +147,16 @@ set(common_libraries Qt::Core Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTemplates2Private Qt::TestPrivate Qt::Gui # QColor, QMatrix4x4, ... ) +if (QT_FEATURE_qml_table_model) + list(APPEND qml_sources QmlTableModel.qml) + list(APPEND common_libraries Qt::LabsQmlModelsPrivate) +endif() + set_source_files_properties(NameConflict.qml PROPERTIES QT_QMLTC_FILE_BASENAME ResolvedNameConflict) @@ -166,10 +184,11 @@ qt_internal_add_resource(qmltc_test_module "qmake_immediate" ${qmake_immediate_resource_files} ) +qt_policy(SET QTP0001 NEW) + qt6_add_qml_module(qmltc_test_module VERSION 1.0 URI QmltcTests - AUTO_RESOURCE_PREFIX SOURCES ${cpp_sources} QML_FILES diff --git a/tests/auto/qml/qmltc/QmltcTests/Connections.qml b/tests/auto/qml/qmltc/QmltcTests/Connections.qml new file mode 100644 index 0000000000..d7fc0d87c6 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/Connections.qml @@ -0,0 +1,12 @@ +import QtQuick + +Rectangle { + property string hello + + id: root + + Connections { + target: root + function onHelloChanged(argument) {} + } +} diff --git a/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml b/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml index 9dbe9d6fbe..d3c1cfe293 100644 --- a/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml +++ b/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml b/tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml new file mode 100644 index 0000000000..696c2b40bb --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml @@ -0,0 +1,62 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.12 +import QtQuick.Window 2.12 +import Qt.labs.qmlmodels 1.0 + +TableView { + anchors.fill: parent + columnSpacing: 1 + rowSpacing: 1 + boundsBehavior: Flickable.StopAtBounds + + model: TableModel { + property string testName: "MyTableModel" + TableModelColumn { display: "checked" } + TableModelColumn { display: "amount" } + TableModelColumn { display: "fruitType" } + TableModelColumn { display: "fruitName" } + TableModelColumn { display: "fruitPrice" } + + // Each row is one type of fruit that can be ordered + rows: [ + { + // Each property is one cell/column. + checked: false, + amount: 1, + fruitType: "Apple", + fruitName: "Granny Smith", + fruitPrice: 1.50 + }, + { + checked: true, + amount: 4, + fruitType: "Orange", + fruitName: "Navel", + fruitPrice: 2.50 + }, + { + checked: false, + amount: 1, + fruitType: "Banana", + fruitName: "Cavendish", + fruitPrice: 3.50 + } + ] + } + delegate: TextInput { + text: model.display + padding: 12 + selectByMouse: true + + onAccepted: model.display = text + + Rectangle { + anchors.fill: parent + color: "#efefef" + z : -1 + } + } +} + diff --git a/tests/auto/qml/qmltc/QmltcTests/aliases.qml b/tests/auto/qml/qmltc/QmltcTests/aliases.qml index 9f13f7b17a..41c34b4187 100644 --- a/tests/auto/qml/qmltc/QmltcTests/aliases.qml +++ b/tests/auto/qml/qmltc/QmltcTests/aliases.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml b/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml index 680e297edb..70636c0a2e 100644 --- a/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml +++ b/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QmltcTests diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h new file mode 100644 index 0000000000..7546f344d2 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h @@ -0,0 +1,64 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CUSTOMINITIALIAZATION_H_ +#define CUSTOMINITIALIAZATION_H_ + +#include <QtCore/qobject.h> +#include <QtCore/qproperty.h> +#include <QtQuick/qquickitem.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qqmlregistration.h> + +class ExtensionType : public QObject +{ + Q_OBJECT + QML_ANONYMOUS + Q_PROPERTY(double propertyFromExtension READ getPropertyFromExtension WRITE + setPropertyFromExtension) + Q_PROPERTY(QQmlListProperty<QQuickItem> extensionObjectList READ getExtensionObjectList) + + QProperty<double> m_propertyFromExtension{ 0 }; + QList<QQuickItem *> m_extensionObjectList; + +public: + ExtensionType(QObject *parent = nullptr) : QObject(parent) { } + + double getPropertyFromExtension() const { return m_propertyFromExtension; } + void setPropertyFromExtension(double v) { m_propertyFromExtension = v; } + + QQmlListProperty<QQuickItem> getExtensionObjectList() + { + return QQmlListProperty<QQuickItem>(this, &m_extensionObjectList); + } +}; + +class TypeForCustomInitialization : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QQmlListProperty<QQuickItem> cppObjectList READ getCppObjectList) + Q_PROPERTY( + double defaultedBindable BINDABLE bindableDefaultedBindable READ default WRITE default) + + QML_EXTENDED(ExtensionType) + + QList<QQuickItem *> m_cppObjectList; + QProperty<double> m_defaultedBindable; + +public: + TypeForCustomInitialization(QObject *parent = nullptr) : QObject(parent) { } + + QQmlListProperty<QQuickItem> getCppObjectList() + { + return QQmlListProperty<QQuickItem>(this, &m_cppObjectList); + } + + QBindable<double> bindableDefaultedBindable() + { + return QBindable<double>(&m_defaultedBindable); + } +}; + +#endif // CUSTOMINITIALIAZATION_H_ diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp index 422e0021f1..1487dbcf15 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "deferredpropertytypes.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h index d9aa801c77..1701d6ee91 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DEFERREDPROPERTYTYPES_H #define DEFERREDPROPERTYTYPES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp index 16f555c0b7..b6ed76eed4 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "extensiontypes.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h index 667e2952ce..080d3b4380 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef EXTENSIONTYPES_H #define EXTENSIONTYPES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h index 31cb385cfd..3135f4e3d3 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // NB: fake private header for testing purposes #ifndef TESTPRIVATEPROPERTY_P_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h index 293aece065..a2e1518fc3 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SINGLETONTYPE_H #define SINGLETONTYPE_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp index fd70c0a3f4..b44945fba0 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testattachedtype.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h index b7cc6348aa..2e3ef97009 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTATTACHEDTYPE_H #define TESTATTACHEDTYPE_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp index 410aae1528..45d6c3e322 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testgroupedtype.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h index f895f19531..cef875545c 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTGROUPEDTYPE_H #define TESTGROUPEDTYPE_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp index c1d101653c..f24728b597 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "private/testprivateproperty_p.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h index 230c86affd..22863ba52a 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTPRIVATEPROPERTY_H #define TESTPRIVATEPROPERTY_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h index 873c38771e..b0f1a17adc 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef PROPERTYALIASATTRIBUTES_H #define PROPERTYALIASATTRIBUTES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp index fbfaf447da..ae4aaa5092 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "typewithnamespace.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h index 7b8bc7803a..173e8624ff 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPEWITHNAMESPACE_H #define TYPEWITHNAMESPACE_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp index 80557209a5..eb0dae7465 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "typewithproperties.h" diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h index e06f9079f9..d7a0d03405 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPEWITHPROPERTIES_H #define TYPEWITHPROPERTIES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h new file mode 100644 index 0000000000..cc1ded9f74 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h @@ -0,0 +1,75 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/qobject.h> +#include <QtCore/qproperty.h> +#include <QtQuick/qquickitem.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qqmlregistration.h> + +#ifndef TYPEWITHREQUIREDPROPERTIES_H_ +# define TYPEWITHREQUIREDPROPERTIES_H_ + +class ExtensionTypeWithRequiredProperties : public QObject +{ + Q_OBJECT + QML_ANONYMOUS + + Q_PROPERTY(double requiredPropertyFromExtension READ getRequiredPropertyFromExtension WRITE + setRequiredPropertyFromExtension REQUIRED) + + QProperty<double> m_requiredPropertyFromExtension{}; + +public: + ExtensionTypeWithRequiredProperties(QObject *parent = nullptr) : QObject(parent) { } + + double getRequiredPropertyFromExtension() const { return m_requiredPropertyFromExtension; } + void setRequiredPropertyFromExtension(double v) { m_requiredPropertyFromExtension = v; } +}; + +class TypeWithRequiredProperties : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QQuickItem *inheritedRequiredProperty READ getInheritedRequiredProperty WRITE + setInheritedRequiredProperty REQUIRED) + Q_PROPERTY(int inheritedRequiredPropertyThatWillBeBound READ + getInheritedRequiredPropertyThatWillBeBound WRITE + setInheritedRequiredPropertyThatWillBeBound REQUIRED) + Q_PROPERTY(int nonRequiredInheritedPropertyThatWillBeMarkedRequired READ + getNonRequiredInheritedPropertyThatWillBeMarkedRequired WRITE + setNonRequiredInheritedPropertyThatWillBeMarkedRequired REQUIRED) + + QML_EXTENDED(ExtensionTypeWithRequiredProperties) + + QProperty<QQuickItem *> m_inheritedRequiredProperty{}; + QProperty<int> m_inheritedRequiredPropertyThatWillBeBound{}; + QProperty<int> m_nonRequiredInheritedPropertyThatWillBeMarkedRequired{}; + +public: + TypeWithRequiredProperties(QObject *parent = nullptr) : QObject(parent) { } + + QQuickItem *getInheritedRequiredProperty() const { return m_inheritedRequiredProperty; } + void setInheritedRequiredProperty(QQuickItem *v) { m_inheritedRequiredProperty = v; } + + int getInheritedRequiredPropertyThatWillBeBound() const + { + return m_inheritedRequiredPropertyThatWillBeBound; + } + void setInheritedRequiredPropertyThatWillBeBound(int v) + { + m_inheritedRequiredPropertyThatWillBeBound = v; + } + + int getNonRequiredInheritedPropertyThatWillBeMarkedRequired() const + { + return m_nonRequiredInheritedPropertyThatWillBeMarkedRequired; + } + void setNonRequiredInheritedPropertyThatWillBeMarkedRequired(int v) + { + m_nonRequiredInheritedPropertyThatWillBeMarkedRequired = v; + } +}; + +#endif // TYPEWITHREQUIREDPROPERTIES_H_ diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h index 139b431a40..6bb94ae051 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPEWITHSIGNAL_H #define TYPEWITHSIGNAL_H diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h index 9d68275b1e..8bf068ba35 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPEWITHSPECIALPROPERTIES_H #define TYPEWITHSPECIALPROPERTIES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml b/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml index a037776a52..2c21298c35 100644 --- a/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml +++ b/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml b/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml index f9146083f6..284a8f5034 100644 --- a/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml +++ b/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick as MyQtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml b/tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml new file mode 100644 index 0000000000..c5a2f5a7b8 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml @@ -0,0 +1,5 @@ +import QtQuick.Templates + +CheckBox { + +} diff --git a/tests/auto/qml/qmltc/QmltcTests/mySignals.qml b/tests/auto/qml/qmltc/QmltcTests/mySignals.qml index c79a0518c2..0348a0af54 100644 --- a/tests/auto/qml/qmltc/QmltcTests/mySignals.qml +++ b/tests/auto/qml/qmltc/QmltcTests/mySignals.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QmltcTests import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/properties.qml b/tests/auto/qml/qmltc/QmltcTests/properties.qml index ca827519d5..f42b14a356 100644 --- a/tests/auto/qml/qmltc/QmltcTests/properties.qml +++ b/tests/auto/qml/qmltc/QmltcTests/properties.qml @@ -26,13 +26,13 @@ QtObject { property date dateP property font fontP property matrix4x4 matrix4x4P - property point pointP - property quaternion quatP - property rect rectP - property size sizeP - property vector2d vec2dP - property vector3d vec3dP - property vector4d vec4dP + property point pointP: ({ x: 100, y: 200 }) + property quaternion quatP: ({ x: 100, y: 200, z: 300, scalar: 400 }) + property rect rectP: ({ x: 100, y: 200, width: 300, height: 400 }) + property size sizeP: ({ width: 100, height: 200 }) + property vector2d vec2dP : ({ x: 100, y: 200 }) + property vector3d vec3dP: ({ x: 100, y: 200, z: 300 }) + property vector4d vec4dP: ({ x: 100, y: 200, z: 300, w: 400 }) default property QtObject defaultObjP readonly property string readonlyStringP: "foobar" diff --git a/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml index 66cd948ac4..fb57a2bbc8 100644 --- a/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml +++ b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import QmltcTests diff --git a/tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml b/tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml new file mode 100644 index 0000000000..0796d00732 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QmltcTests 1.0 + +TypeForCustomInitialization { + id: myWindow + required property int someValue + + property alias someValueAlias: myWindow.someValue + property int someValueBinding: someValue + 1 + + property bool wasSomeValueChanged: false + + property int someComplexValueThatWillBeSet: { return 5 } + property int someComplexValueThatWillNotBeSet: { return 5 } + + property list<int> valueTypeList : [] + property list<Item> objectTypeList : [] + + //QTBUG-114403: onValueChanged should not trigger when setting + //the initial values. + onSomeValueChanged: { wasSomeValueChanged = true; } +} diff --git a/tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml b/tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml new file mode 100644 index 0000000000..79aa178de4 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml @@ -0,0 +1,9 @@ +import QtQuick + +Item { + // qmltc should see that rectangle is used as the type argument + // for the list and produce an import for QQuickRectangle. + // Failure to do so will produce code that cannot compile and the + // test will fail at build time. + property list<Rectangle> listWithUniqueReferenceToType : [] +} diff --git a/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml b/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml index 7c4dad5a4e..d4eb72786b 100644 --- a/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml +++ b/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml b/tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml new file mode 100644 index 0000000000..77e9c88b9b --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml @@ -0,0 +1,61 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QmltcTests 1.0 + +TypeWithRequiredProperties { + id: self + required property int primitiveType + required property list<int> valueList + required property list<Item> objectList + + property int propertyThatWillBeMarkedRequired + + // This is already bound so it should not appear as part of the + // bundle. + inheritedRequiredPropertyThatWillBeBound : 10 + + // This should be ignored as it alias a required property we are + // already going to consider. It should thus not appear as part of + // the bundle. + property alias aliasToRequiredProperty : self.primitiveType + + required propertyThatWillBeMarkedRequired + required nonRequiredInheritedPropertyThatWillBeMarkedRequired + + property alias aliasToRequiredInner: inner.requiredInner + + // This should be ignored as the underlying property is already bound. + property alias aliasToRequiredBoundedInner: inner.requiredBoundedInner + + property alias aliasToInnerThatWillBeMarkedRequired: inner.nonRequiredInner + required aliasToInnerThatWillBeMarkedRequired + + property int notRequired + property alias requiredAliasToUnrequiredProperty : self.notRequired + required requiredAliasToUnrequiredProperty + + // When we have an alias to a required property in the same scope + // we exclude the alias in favor of setting the property directly. + // See for example aliasToRequiredProperty in this file. + // + // The following alias should instead be picked up, as it point to + // an inner scope. + // Nonetheless, an initial implementation had a bug that would + // discard the alias as long as a property with the same name as + // the target was present in the same scope. + // + // The following alias tests this initially failing case. + property alias aliasToPropertyThatShadows: inner.primitiveType + + property Item children : Item { + id: inner + required property int requiredInner + property int nonRequiredInner + required property int requiredBoundedInner + requiredBoundedInner: 43 + + required property int primitiveType + } +} diff --git a/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml b/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml new file mode 100644 index 0000000000..d88e3920c3 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { + id: root + + property bool cycleEnabled: false + property bool cycleFirst: false + property bool cycleSecond: false + + property Timer enableTimer: Timer { + running: root.cycleEnabled + interval: 1 + onTriggered: { + conn.enabled = !conn.enabled; + root.cycleEnabled = false; + } + } + + property Timer firstTimer: Timer { + id: firstTimer + objectName: "first" + running: root.cycleFirst + interval: 1 + onTriggered: root.cycleFirst = false + } + + property Timer secondTimer: Timer { + objectName: "second" + running: root.cycleSecond + interval: 1 + onTriggered: conn.target = this; + repeat: true + } + + property Connections conn: Connections { + id: conn + target: firstTimer + function onTriggered(m) { + root.objectName = target.objectName + root.cycleSecond = false; + } + } +} diff --git a/tests/auto/qml/qmltc/QmltcTests/singletons.qml b/tests/auto/qml/qmltc/QmltcTests/singletons.qml index a45e502bc0..d30a50dd6b 100644 --- a/tests/auto/qml/qmltc/QmltcTests/singletons.qml +++ b/tests/auto/qml/qmltc/QmltcTests/singletons.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QmltcTests diff --git a/tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml b/tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml new file mode 100644 index 0000000000..1172c42032 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml @@ -0,0 +1,12 @@ +import QtQuick 2.12 + +QtObject { + readonly property FontLoader iconLoader: FontLoader { + source: "qrc:/qt/qml/path/to/font.ttf" + } + property string myUrl: "qrc:/qt/qml/path/to/font2.ttf" + readonly property FontLoader iconLoader2: FontLoader { + source: myUrl + } +} + diff --git a/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js b/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js index a248743d29..7b552e7f67 100644 --- a/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js +++ b/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only function isGood(value) { diff --git a/tests/auto/qml/qmltc/QmltcTests/translations.qml b/tests/auto/qml/qmltc/QmltcTests/translations.qml index e00d9f5b86..71e9298fd1 100644 --- a/tests/auto/qml/qmltc/QmltcTests/translations.qml +++ b/tests/auto/qml/qmltc/QmltcTests/translations.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/translationsById.qml b/tests/auto/qml/qmltc/QmltcTests/translationsById.qml index e3606d9b0f..98fd5cb846 100644 --- a/tests/auto/qml/qmltc/QmltcTests/translationsById.qml +++ b/tests/auto/qml/qmltc/QmltcTests/translationsById.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml b/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml index 7226abbb2e..300af9d31f 100644 --- a/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml +++ b/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import QtQuick diff --git a/tests/auto/qml/qmltc/nameconflict.cpp b/tests/auto/qml/qmltc/nameconflict.cpp index e95272913f..b1fb21977b 100644 --- a/tests/auto/qml/qmltc/nameconflict.cpp +++ b/tests/auto/qml/qmltc/nameconflict.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "nameconflict.h" #include <QtCore/qobject.h> diff --git a/tests/auto/qml/qmltc/nameconflict.h b/tests/auto/qml/qmltc/nameconflict.h index ea971b5159..64297af4d4 100644 --- a/tests/auto/qml/qmltc/nameconflict.h +++ b/tests/auto/qml/qmltc/nameconflict.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef NAME_CONFLICT_FLAG #define NAME_CONFLICT_FLAG diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index 1fa4030642..ef33cebc00 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_qmltc.h" @@ -21,10 +21,12 @@ #include "qjsvalueassignments.h" #include "extensiontypebindings.h" #include "qtbug103956_main.h" +#include "qtbug120700_main.h" #include "nonstandardinclude.h" #include "specialproperties.h" #include "regexpbindings.h" #include "aliasassignments.h" +#include "connections.h" #include "signalhandlers.h" #include "javascriptfunctions.h" @@ -77,12 +79,18 @@ #include "repeatercrash.h" #include "aliases.h" #include "inlinecomponentsfromdifferentfiles.h" +#include "helloexportedworld.h" +#include "helloexportedworldnofilename.h" #include "testprivateproperty.h" #include "singletons.h" #include "mysignals.h" #include "namespacedtypes.h" #include "type.h" +#include "qmltablemodel.h" +#include "stringtourl.h" +#include "signalconnections.h" +#include "requiredproperties.h" // Qt: #include <QtCore/qstring.h> @@ -147,11 +155,14 @@ void tst_qmltc::initTestCase() QUrl("qrc:/qt/qml/QmltcTests/regexpBindings.qml"), QUrl("qrc:/qt/qml/QmltcTests/AliasBase.qml"), QUrl("qrc:/qt/qml/QmltcTests/aliasAssignments.qml"), + QUrl("qrc:/qt/qml/QmltcTests/Connections.qml"), QUrl("qrc:/qt/qml/QmltcTests/qtbug103956/SubComponent.qml"), QUrl("qrc:/qt/qml/QmltcTests/qtbug103956/MainComponent.qml"), QUrl("qrc:/qt/qml/QmltcTests/qtbug103956/qtbug103956_main.qml"), + QUrl("qrc:/qt/qml/QmltcTests/qtbug120700_main.qml"), + QUrl("qrc:/qt/qml/QmltcTests/signalHandlers.qml"), QUrl("qrc:/qt/qml/QmltcTests/javaScriptFunctions.qml"), QUrl("qrc:/qt/qml/QmltcTests/changingBindings.qml"), @@ -192,6 +203,7 @@ void tst_qmltc::initTestCase() QUrl("qrc:/qt/qml/QmltcTests/calqlatrBits.qml"), QUrl("qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"), QUrl("qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"), + QUrl("qrc:/qt/qml/QmltcTests/requiredProperties.qml"), }; QQmlEngine e; @@ -374,7 +386,14 @@ void tst_qmltc::properties() QCOMPARE(created.intP(), 42); QCOMPARE(created.realP(), 2.32); QCOMPARE(created.stringP(), u"hello, world"_s); - QCOMPARE(created.urlP(), u"https://www.qt.io/"_s); + QCOMPARE(created.urlP(), QUrl(u"https://www.qt.io/"_s)); + QCOMPARE(created.pointP(), QPoint(100, 200)); + QCOMPARE(created.quatP(), QQuaternion(400, 100, 200, 300)); + QCOMPARE(created.rectP(), QRectF(100, 200, 300, 400)); + QCOMPARE(created.sizeP(), QSizeF(100, 200)); + QCOMPARE(created.vec2dP(), QVector2D(100, 200)); + QCOMPARE(created.vec3dP(), QVector3D(100, 200, 300)); + QCOMPARE(created.vec4dP(), QVector4D(100, 200, 300, 400)); QCOMPARE(created.varP(), 42.42); QCOMPARE(created.boolP(), true); @@ -818,6 +837,103 @@ void tst_qmltc::visibleAliasMethods() QCOMPARE(created.firstComponent()->setMe(), true); } +// QTBUG-120700 +void tst_qmltc::customInitialization() +{ + int valueToTest = 10; + + QQuickItem firstItem; + QQuickItem secondItem; + + QQmlEngine e; + PREPEND_NAMESPACE(qtbug120700_main) + created(&e, {valueToTest} ,nullptr, [valueToTest, &firstItem, &secondItem](auto& component) { + component.setSomeComplexValueThatWillBeSet(valueToTest); + component.setPropertyFromExtension(static_cast<double>(valueToTest)); + component.setDefaultedBindable(static_cast<double>(valueToTest)); + component.setValueTypeList({1, 2, 3, 4}); + component.setObjectTypeList({&firstItem, &secondItem}); + component.setExtensionObjectList({&firstItem, &secondItem}); + component.setCppObjectList({&firstItem, &secondItem}); + }); + + // QTBUG-114403: onValueChanged should have not been triggered + // when setting the initial value for the property. + // If this is true then the handler was called. + QCOMPARE(created.wasSomeValueChanged(), false); + + // someComplexValueThatWillBeSet is set through a binding in the + // QML code, but is initialized when the instance is created. + // The bindings, which is generally set after the custom + // initialization was perfomed, should not overwrite the initial + // value that the user provided. + // On the other side, someComplexValueThatWillNotBeSet should + // still respect the original binding as an initial value for it + // was not provided. + QCOMPARE(created.someComplexValueThatWillBeSet(), valueToTest); + QCOMPARE(created.someComplexValueThatWillNotBeSet(), 5); + + QCOMPARE(created.someValue(), valueToTest); + QCOMPARE(created.someValueAlias(), valueToTest); + QCOMPARE(created.someValueBinding(), valueToTest + 1); + QCOMPARE(created.property("propertyFromExtension").toDouble(), static_cast<double>(valueToTest)); + QCOMPARE(created.bindableDefaultedBindable().value(), static_cast<double>(valueToTest)); + QCOMPARE(created.valueTypeList(), QList({1, 2, 3, 4})); + QCOMPARE(created.objectTypeList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem})); + QCOMPARE( + created.property("extensionObjectList").value<QQmlListProperty<QQuickItem>>().toList<QList<QQuickItem*>>(), + QList({&firstItem, &secondItem}) + ); + QCOMPARE(created.getCppObjectList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem})); +} + +void tst_qmltc::requiredPropertiesInitialization() +{ + QQuickItem item{}; + + int aliasToInnerThatWillBeMarkedRequired = 10; + int aliasToPropertyThatShadows = 42; + int aliasToRequiredInner = 11; + QQuickItem inheritedRequiredProperty{}; + int nonRequiredInheritedPropertyThatWillBeMarkedRequired = 12; + QList<QQuickItem*> objectList{&item}; + int primitiveType = 13; + int propertyThatWillBeMarkedRequired = 14; + int requiredAliasToUnrequiredProperty = 15; + double requiredPropertyFromExtension = 16.0; + QList<int> valueList{1, 2, 3, 4}; + + QQmlEngine e; + PREPEND_NAMESPACE(requiredProperties) created( + &e, + { + aliasToInnerThatWillBeMarkedRequired, + aliasToPropertyThatShadows, + aliasToRequiredInner, + &inheritedRequiredProperty, + nonRequiredInheritedPropertyThatWillBeMarkedRequired, + objectList, + primitiveType, + propertyThatWillBeMarkedRequired, + requiredAliasToUnrequiredProperty, + requiredPropertyFromExtension, + valueList + } + ); + + QCOMPARE(created.aliasToInnerThatWillBeMarkedRequired(), aliasToInnerThatWillBeMarkedRequired); + QCOMPARE(created.aliasToPropertyThatShadows(), aliasToPropertyThatShadows); + QCOMPARE(created.aliasToRequiredInner(), aliasToRequiredInner); + QCOMPARE(created.getInheritedRequiredProperty(), &inheritedRequiredProperty); + QCOMPARE(created.getNonRequiredInheritedPropertyThatWillBeMarkedRequired(), nonRequiredInheritedPropertyThatWillBeMarkedRequired); + QCOMPARE(created.objectList().toList<QList<QQuickItem*>>(), objectList); + QCOMPARE(created.primitiveType(), primitiveType); + QCOMPARE(created.propertyThatWillBeMarkedRequired(), propertyThatWillBeMarkedRequired); + QCOMPARE(created.requiredAliasToUnrequiredProperty(), requiredAliasToUnrequiredProperty); + QCOMPARE(created.property("requiredPropertyFromExtension").toDouble(), requiredPropertyFromExtension); + QCOMPARE(created.valueList(), valueList); +} + // QTBUG-104094 void tst_qmltc::nonStandardIncludesInsideModule() { @@ -887,6 +1003,12 @@ void tst_qmltc::aliasAssignments() } } +void tst_qmltc::connections() +{ + QQmlEngine e; + PREPEND_NAMESPACE(Connections) created(&e); +} + void tst_qmltc::signalHandlers() { QQmlEngine e; @@ -1058,7 +1180,7 @@ void tst_qmltc::propertyAlias_external() void tst_qmltc::propertyAliasAttribute() { QQmlEngine e; - PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e); + PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e, {""}); QQmlComponent c(&e); c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/propertyAliasAttributes.qml")); @@ -3204,8 +3326,74 @@ void tst_qmltc::namespacedName() { // cmake script should be able to auto-fill the namespace of the generated modules, and to // replace . with :: - NamespaceTest::Subfolder::Type *t; - Q_UNUSED(t); + QQmlEngine e; + NamespaceTest::Subfolder::Type t(&e); + QCOMPARE(t.data(), u"Hello from namespace"_s); +} + +void tst_qmltc::checkExportsAreCompiling() +{ + QQmlEngine e; + QmltcExportedTests::HelloExportedWorld w(&e); + QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s); +} + +void tst_qmltc::checkExportsNoFileName() +{ + QQmlEngine e; + QmltcExportedNoFileNameTest::HelloExportedWorldNoFileName w(&e); + QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s); +} + +#if QT_CONFIG(qml_table_model) +void tst_qmltc::qmlTableModel() +{ + QQmlEngine e; + PREPEND_NAMESPACE(QmlTableModel) createdByQmltc(&e); + // check that the tableModel is not default constructed + QVariant model = createdByQmltc.model(); + QVERIFY(model.isValid()); + QQmlTableModel *tableModel = model.value<QQmlTableModel *>(); + QVERIFY(tableModel); + QCOMPARE(tableModel->property("testName").toString(), u"MyTableModel"_s); +} +#endif + +void tst_qmltc::urlToString() +{ + QQmlEngine e; + PREPEND_NAMESPACE(stringToUrl) createdByQmltc(&e); + // check that the tableModel is not default constructed + QUrl first = createdByQmltc.iconLoader()->source(); + QUrl second = createdByQmltc.iconLoader2()->source(); + QCOMPARE(first, QUrl("qrc:/qt/qml/path/to/font.ttf")); + QCOMPARE(second, QUrl("qrc:/qt/qml/path/to/font2.ttf")); +} + +void tst_qmltc::signalConnections() +{ + QQmlEngine e; + PREPEND_NAMESPACE(signalConnections) createdByQmltc(&e); + + QVERIFY(createdByQmltc.objectName().isEmpty()); + createdByQmltc.setCycleFirst(true); + QTRY_VERIFY(!createdByQmltc.cycleFirst()); + QCOMPARE(createdByQmltc.objectName(), QLatin1String("first")); + + createdByQmltc.setObjectName(QLatin1String("none")); + createdByQmltc.setCycleEnabled(true); + QTRY_VERIFY(!createdByQmltc.cycleEnabled()); + + createdByQmltc.setCycleFirst(true); + QTRY_VERIFY(!createdByQmltc.cycleFirst()); + QCOMPARE(createdByQmltc.objectName(), QLatin1String("none")); + + createdByQmltc.setCycleEnabled(true); + QTRY_VERIFY(!createdByQmltc.cycleEnabled()); + + createdByQmltc.setCycleSecond(true); + QTRY_VERIFY(!createdByQmltc.cycleSecond()); + QCOMPARE(createdByQmltc.objectName(), QLatin1String("second")); } QTEST_MAIN(tst_qmltc) diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h index af084dcc01..ede6d551a0 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.h +++ b/tests/auto/qml/qmltc/tst_qmltc.h @@ -1,7 +1,8 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> +#include <private/qtqmlmodelsglobal_p.h> using namespace Qt::StringLiterals; @@ -34,10 +35,13 @@ private slots: void jsvalueAssignments(); void extensionTypeBindings(); void visibleAliasMethods(); // QTBUG-103956 + void customInitialization(); // QTBUG-120700 + void requiredPropertiesInitialization(); void nonStandardIncludesInsideModule(); // QTBUG-104094 void specialProperties(); void regexpBindings(); void aliasAssignments(); + void connections(); void signalHandlers(); void jsFunctions(); @@ -93,4 +97,12 @@ private slots: void constSignalParameters(); void cppNamespaces(); void namespacedName(); + void checkExportsAreCompiling(); + void checkExportsNoFileName(); + +#if QT_CONFIG(qml_table_model) + void qmlTableModel(); +#endif + void urlToString(); + void signalConnections(); }; diff --git a/tests/auto/qml/qmltc_manual/CMakeLists.txt b/tests/auto/qml/qmltc_manual/CMakeLists.txt index 8101bcbcea..6f1b065f99 100644 --- a/tests/auto/qml/qmltc_manual/CMakeLists.txt +++ b/tests/auto/qml/qmltc_manual/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmltc_manual LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -19,9 +25,10 @@ qt_internal_add_test(tst_qmltc_manual TESTDATA ${test_data} ) +qt_policy(SET QTP0001 NEW) + qt6_add_qml_module(tst_qmltc_manual URI "QmltcManualTests" - AUTO_RESOURCE_PREFIX QML_FILES ${test_data} ) diff --git a/tests/auto/qml/qmltc_manual/testclasses.h b/tests/auto/qml/qmltc_manual/testclasses.h index a3fc9cfc21..879b815f16 100644 --- a/tests/auto/qml/qmltc_manual/testclasses.h +++ b/tests/auto/qml/qmltc_manual/testclasses.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTCLASSES_H #define TESTCLASSES_H diff --git a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp index ca8e0895d7..6e41ff3baa 100644 --- a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp +++ b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt index bac445dec8..85c01d0a5f 100644 --- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt +++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmltc_qprocess LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmltc_qprocess SOURCES tst_qmltc_qprocess.cpp @@ -14,12 +20,14 @@ qt_internal_add_test(tst_qmltc_qprocess set_source_files_properties(data/SingletonThing.qml data/singletonUncreatable.qml PROPERTIES QT_QML_SINGLETON_TYPE true) +qt_policy(SET QTP0001 NEW) + qt6_add_qml_module(tst_qmltc_qprocess VERSION 1.0 URI QmltcQProcessTests - AUTO_RESOURCE_PREFIX SOURCES cpptypes/testtype.h + cpptypes/typewithrequiredproperty.h DEPENDENCIES QtQuick/auto QML_FILES @@ -33,6 +41,13 @@ qt6_add_qml_module(tst_qmltc_qprocess data/singletonUncreatable.qml data/uncreatable.qml data/invalidSignalHandlers.qml + data/QmlBaseFromAnotherModule.qml + data/invalidTypeAnnotation.qml + data/constructFromString.qml + data/unboundRequiredPropertyInInlineComponent.qml + data/componentDefinitionInnerRequiredProperty.qml + data/componentDefinitionInnerRequiredPropertyFromOutside.qml + data/innerLevelRequiredProperty.qml ) set(common_libraries diff --git a/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h b/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h index 81c5f00c84..401a63accc 100644 --- a/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h +++ b/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPE_H #define TESTTYPE_H diff --git a/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h b/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h new file mode 100644 index 0000000000..1b0c69efaa --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h @@ -0,0 +1,27 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef TYPEWITHREQUIREDPROPERTY_H_ +#define TYPEWITHREQUIREDPROPERTY_H_ + +#include <QtCore/qobject.h> +#include <QtCore/qproperty.h> +#include <QtQml/qqmlregistration.h> + +class TypeWithRequiredProperty : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QString requiredProperty READ requiredProperty WRITE setRequiredProperty REQUIRED) + + QProperty<QString> m_requiredProperty; + +public: + TypeWithRequiredProperty(QObject *parent = nullptr) : QObject(parent) { } + + QString requiredProperty() const { return m_requiredProperty; } + void setRequiredProperty(const QString &s) { m_requiredProperty = s; } +}; + +#endif // TYPEWITHREQUIREDPROPERTY_H_ diff --git a/tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml b/tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml new file mode 100644 index 0000000000..3c62716105 --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml @@ -0,0 +1,13 @@ +import QtQml +import QtQuick +import QtQuick.Controls.Basic +import QtQuick.Window + +Item { + property ScrollBar myBar: ScrollBar {} + function f(a: ScrollBar): ScrollBar {} + + // C++ defined QML types from other modules are fine + property Item myItem: Item {} + function g(a: Item): Item {} +} diff --git a/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml new file mode 100644 index 0000000000..638cf9fa1e --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Item { + Component { + id: mycomp + + Item { + Rectangle { + // Inner required properties cannot be set so this + // should produce an error + required property bool bar + } + } + } +} diff --git a/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml new file mode 100644 index 0000000000..56f00edbe9 --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QmltcQProcessTests + +Item { + Component { + id: mycomp + + Item { + // This introduces an inner required property + // without a binding that cannot be set later and should + // thus block the compilation. + TypeWithRequiredProperty {} + } + } +} diff --git a/tests/auto/qml/qmltc_qprocess/data/constructFromString.qml b/tests/auto/qml/qmltc_qprocess/data/constructFromString.qml new file mode 100644 index 0000000000..76ec189f3d --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/constructFromString.qml @@ -0,0 +1,7 @@ +import QtQuick 2.15 + +Item { + property point p: "30,50" + property rect p3: "10, 20, 30x50" + property size p4: "30x50" +} diff --git a/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml b/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml new file mode 100644 index 0000000000..8b28418670 --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml @@ -0,0 +1,10 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Item { + Item { + required property int foo + } +} diff --git a/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml b/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml index 55e3e73c31..44111430f7 100644 --- a/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml +++ b/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import QmltcQProcessTests diff --git a/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml b/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml new file mode 100644 index 0000000000..fb8c8eb198 --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml @@ -0,0 +1,20 @@ +import QtQuick + + +Item { + function f(): Qt.point { + + } + + function g() { + + } + + function h(a: int, b: Item) { + + } + + function alpha(a: int, b) {} + function beta(a: int, b): Item {} + function gamma(a: Qt.point, b) {} +} diff --git a/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml b/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml new file mode 100644 index 0000000000..3955a228d8 --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml @@ -0,0 +1,10 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Item { + component InlineComponent: Item { required property int foo } + + InlineComponent {} +} diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp index 7f12f29342..cdbd9695fd 100644 --- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp +++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/qtest.h> @@ -51,6 +51,14 @@ private slots: void topLevelComponent(); void dashesInFilename(); void invalidSignalHandlers(); + void exports(); + void qmlBaseFromAnotherModule(); + void invalidTypeAnnotation(); + void constructFromString(); + void unboundRequiredPropertyInInlineComponent(); + void componentDefinitionInnerRequiredProperty(); + void componentDefinitionInnerRequiredPropertyFromOutside(); + void innerLevelRequiredProperty(); }; #ifndef TST_QMLTC_QPROCESS_RESOURCES @@ -92,6 +100,7 @@ QString tst_qmltc_qprocess::runQmltc(const QString &inputFile, args << u"--resource"_s << resource; args << u"--header"_s << (m_tmpPath + u"/"_s + QFileInfo(inputFile).baseName() + u".h"_s); args << u"--impl"_s << (m_tmpPath + u"/"_s + QFileInfo(inputFile).baseName() + u".cpp"_s); + args << u"--module"_s << u"QmltcQProcessTestModule"_s; args << extraArgs; QString errors; @@ -144,7 +153,7 @@ void tst_qmltc_qprocess::noBuiltins() QVERIFY(file.rename(original)); }; - for (QString builtin : { u"builtins.qmltypes"_s, u"jsroot.qmltypes"_s }) { + for (QString builtin : { u"jsroot.qmltypes"_s, u"builtins.qmltypes"_s }) { const auto path = QLibraryInfo::path(QLibraryInfo::QmlImportsPath) + u"/"_s + builtin; QScopeGuard scope(std::bind(renameBack, path)); @@ -261,5 +270,120 @@ void tst_qmltc_qprocess::invalidSignalHandlers() } } +void tst_qmltc_qprocess::qmlBaseFromAnotherModule() +{ + { + const auto errors = runQmltc(u"QmlBaseFromAnotherModule.qml"_s, false); + QVERIFY(errors.contains( + u"QmlBaseFromAnotherModule.qml:6:1: Can't compile the QML property type \"ScrollBar\" to C++ because it lives in \"QtQuick.Controls.Basic\" instead of the current file's \"QmltcQProcessTestModule\" QML module."_s)); + QVERIFY(errors.contains( + u"QmlBaseFromAnotherModule.qml:6:1: Can't compile the QML method return type \"ScrollBar\" to C++ because it lives in \"QtQuick.Controls.Basic\" instead of the current file's \"QmltcQProcessTestModule\" QML module."_s)); + QVERIFY(errors.contains( + u"QmlBaseFromAnotherModule.qml:6:1: Can't compile the QML parameter type \"ScrollBar\" to C++ because it lives in \"QtQuick.Controls.Basic\" instead of the current file's \"QmltcQProcessTestModule\" QML module."_s)); + // it should not complain about the usages of Item, a C++ defined QML element from another + // module + QVERIFY(!errors.contains(u"\"Item\""_s)); + } +} + +void tst_qmltc_qprocess::invalidTypeAnnotation() +{ + { + const auto errors = runQmltc(u"invalidTypeAnnotation.qml"_s, false); + QVERIFY(errors.contains( + u"invalidTypeAnnotation.qml:5:17: \"Qt.point\" was not found for the return type of method \"f\"."_s)); + QVERIFY(errors.contains( + u"invalidTypeAnnotation.qml:19:21: \"Qt.point\" was not found for the type of parameter \"a\" in method \"gamma\"."_s)); + QVERIFY(!errors.contains(u"\"var\""_s)); + QVERIFY(!errors.contains(u"\"void\""_s)); + } +} + +static QString fileToString(const QString &path) +{ + QFile f(path); + if (f.open(QIODevice::ReadOnly)) + return QString::fromLatin1(f.readAll()); + return QString(); +} + +void tst_qmltc_qprocess::exports() +{ + const QString fileName = u"dummy.qml"_s; + QStringList extraArgs; + extraArgs << "--export" + << "MYLIB_EXPORT_MACRO" + << "--exportInclude" + << "exportheader.h"; + const auto errors = runQmltc(fileName, true, extraArgs); + + const QString headerName = m_tmpPath + u"/"_s + QFileInfo(fileName).baseName() + u".h"_s; + const QString header = fileToString(headerName); + const QString implementationName = + m_tmpPath + u"/"_s + QFileInfo(fileName).baseName() + u".cpp"_s; + const QString implementation = fileToString(implementationName); + + QCOMPARE(errors.size(), 0); + + QVERIFY(header.contains(u"class MYLIB_EXPORT_MACRO dummy : public QObject\n"_s)); + QVERIFY(!implementation.contains(u"MYLIB_EXPORT_MACRO"_s)); + + QVERIFY(header.contains(u"#include \"exportheader.h\"\n"_s)); + QVERIFY(!implementation.contains(u"exportheader.h"_s)); +} + +void tst_qmltc_qprocess::constructFromString() +{ + const auto errors = runQmltc(u"constructFromString.qml"_s, false); + const QString warningMessage = + u"constructFromString.qml:%1:%2: Binding is not supported: Type %3 should be" + u" constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a" + u" string."_s; + QVERIFY(errors.contains(warningMessage.arg(4).arg(23).arg(u"QPointF"))); + QVERIFY(errors.contains(warningMessage.arg(5).arg(23).arg(u"QRectF"))); + QVERIFY(errors.contains(warningMessage.arg(6).arg(23).arg(u"QSizeF"))); +} + +void tst_qmltc_qprocess::unboundRequiredPropertyInInlineComponent() +{ + { + const auto errors = runQmltc(u"unboundRequiredPropertyInInlineComponent.qml"_s, false); + QVERIFY(errors.contains( + u"unboundRequiredPropertyInInlineComponent.qml:9:5: Component is missing required property foo from InlineComponent [required]"_s + )); + } +} + +void tst_qmltc_qprocess::componentDefinitionInnerRequiredProperty() +{ + { + const auto errors = runQmltc(u"componentDefinitionInnerRequiredProperty.qml"_s, false); + QVERIFY(errors.contains( + u"componentDefinitionInnerRequiredProperty.qml:11:13: Component is missing required property bar from here [required]" + )); + } +} + +void tst_qmltc_qprocess::componentDefinitionInnerRequiredPropertyFromOutside() +{ + { + const auto errors = + runQmltc(u"componentDefinitionInnerRequiredPropertyFromOutside.qml"_s, false); + QVERIFY(errors.contains( + u"componentDefinitionInnerRequiredPropertyFromOutside.qml:15:13: Component is missing required property requiredProperty from TypeWithRequiredProperty [required]" + )); + } +} + +void tst_qmltc_qprocess::innerLevelRequiredProperty() +{ + { + const auto errors = runQmltc(u"innerLevelRequiredProperty.qml"_s, false); + QVERIFY(errors.contains( + u"innerLevelRequiredProperty.qml:7:5: Component is missing required property foo from here [required]" + )); + } +} + QTEST_MAIN(tst_qmltc_qprocess) #include "tst_qmltc_qprocess.moc" diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt index 039f1647db..45669769e4 100644 --- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt +++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt @@ -7,12 +7,18 @@ ## tst_qmltyperegistrar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmltyperegistrar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + add_subdirectory(VersionZero) add_subdirectory(UnregisteredTypes) add_subdirectory(foreign) qt_manual_moc(moc_files OUTPUT_MOC_JSON_FILES json_list noextheader - INCLUDE_DIRECTORY_TARGETS Qt::Qml) + TARGETS Qt6::Qml) # Dummy target to pass --private-includes to qmltyperegistrar for tst_qmltyperegistrar. # We want to test that it expects files named foo_p.h appearing in foreign metatypes @@ -76,9 +82,11 @@ qt_add_library(tst-qmltyperegistrar-with-dashes) qt_autogen_tools_initial_setup(tst-qmltyperegistrar-with-dashes) target_link_libraries(tst-qmltyperegistrar-with-dashes PRIVATE Qt::Core Qt::Qml) qt_enable_autogen_tool(tst-qmltyperegistrar-with-dashes "moc" ON) + +qt_policy(SET QTP0001 NEW) + qt_add_qml_module(tst-qmltyperegistrar-with-dashes URI Module-With-Dashes - AUTO_RESOURCE_PREFIX SOURCES foo.cpp foo.h OUTPUT_DIRECTORY Module-With-Dashes @@ -90,4 +98,20 @@ qt_internal_add_resource(tst_qmltyperegistrar "resources" "/" FILES duplicatedExports.json + missingTypes.json ) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +qt_add_library(tst-qmltyperegistrar-enum-foreign STATIC enum.cpp) +qt_autogen_tools_initial_setup(tst-qmltyperegistrar-enum-foreign) +qt_enable_autogen_tool(tst-qmltyperegistrar-enum-foreign "moc" ON) +target_link_libraries(tst-qmltyperegistrar-enum-foreign PRIVATE Qt::QmlIntegration) + +qt_add_library(tst-qmltyperegistrar-enum STATIC) +qt_autogen_tools_initial_setup(tst-qmltyperegistrar-enum) +qt_enable_autogen_tool(tst-qmltyperegistrar-enum "moc" ON) +target_link_libraries(tst-qmltyperegistrar-enum PRIVATE Qt::Qml tst-qmltyperegistrar-enum-foreign) + +qt_add_qml_module(tst-qmltyperegistrar-enum URI TstEnum OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/TstEnum) +qt_autogen_tools_initial_setup(tst-qmltyperegistrar-enumplugin) +qt_generate_foreign_qml_types(tst-qmltyperegistrar-enum-foreign tst-qmltyperegistrar-enum) diff --git a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt index cb020e95e0..d469f1e97f 100644 --- a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt +++ b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt @@ -2,13 +2,15 @@ # SPDX-License-Identifier: BSD-3-Clause # Use NO_GENERATE_QMLTYPES to avoid static asserts during compilation of the types to be tested, same for NO_PLUGIN + +qt_policy(SET QTP0001 NEW) + qt_add_qml_module(UnregisteredTypes STATIC URI UnregisteredTypes NO_GENERATE_QMLTYPES NO_PLUGIN SOURCES uncreatable.h - AUTO_RESOURCE_PREFIX ) qt_enable_autogen_tool(UnregisteredTypes "moc" ON) diff --git a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h index 9726f4683c..2274f87e3d 100644 --- a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h +++ b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef UNCREATABLE_H #define UNCREATABLE_H @@ -39,6 +39,39 @@ public: static SingletonCreatable3 *create(QQmlEngine *, QJSEngine *) { return nullptr; } }; +class SingletonForeign : public QObject +{ + Q_OBJECT + SingletonForeign() = delete; +}; + +class SingletonLocalCreatable +{ + Q_GADGET + QML_FOREIGN(SingletonForeign) + QML_ELEMENT + QML_SINGLETON +public: + static SingletonForeign *create(QQmlEngine *, QJSEngine *) { return nullptr; } +}; + +class SingletonLocalUncreatable1 +{ + Q_GADGET + QML_FOREIGN(SingletonForeign) + QML_ELEMENT + QML_SINGLETON + static SingletonForeign *create(QQmlEngine *, QJSEngine *) { return nullptr; } +}; + +class SingletonLocalUncreatable2 +{ + Q_GADGET + QML_FOREIGN(SingletonForeign) + QML_ELEMENT + QML_SINGLETON +}; + class SingletonIncreatable : public QObject { Q_OBJECT @@ -184,4 +217,20 @@ class FixingBadUncreatable QML_FOREIGN(BadUncreatable) QML_UNCREATABLE("") }; + +class SingletonVesion0 : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + // is default constructible +}; + +class SingletonVesion1 : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + // is default constructible +}; #endif // UNCREATABLE_H diff --git a/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h index a3277e6ab3..0ad477c3cb 100644 --- a/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h +++ b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef VERSION_ZERO_TYPE_H #define VERSION_ZERO_TYPE_H diff --git a/tests/auto/qml/qmltyperegistrar/duplicatedExports.h b/tests/auto/qml/qmltyperegistrar/duplicatedExports.h index 5ae2e77551..e72a2f19c9 100644 --- a/tests/auto/qml/qmltyperegistrar/duplicatedExports.h +++ b/tests/auto/qml/qmltyperegistrar/duplicatedExports.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DUPLICATEDEXPORTS_H #define DUPLICATEDEXPORTS_H diff --git a/tests/auto/qml/qmltyperegistrar/enum.cpp b/tests/auto/qml/qmltyperegistrar/enum.cpp new file mode 100644 index 0000000000..34d2e00ffa --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/enum.cpp @@ -0,0 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "enum.h" +#include "moc_enum.cpp" diff --git a/tests/auto/qml/qmltyperegistrar/enum.h b/tests/auto/qml/qmltyperegistrar/enum.h new file mode 100644 index 0000000000..653c48c79f --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/enum.h @@ -0,0 +1,40 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef ENUM_NS_HELLO_H +#define ENUM_NS_HELLO_H + +#include <QObject> +#include <QtQmlIntegration/qqmlintegration.h> + +namespace Hello { + Q_NAMESPACE + QML_NAMED_ELEMENT(World) + enum class World { + Europe = 2024, + }; + Q_ENUM_NS(World) +} + +namespace Universe { + namespace Galaxy { + Q_NAMESPACE + QML_NAMED_ELEMENT(Solar) + enum class Solar { + Earth, + }; + Q_ENUM_NS(Solar) + } + + class Blackhole { + Q_GADGET + QML_ELEMENT + public: + enum SagittariusA { + Singularity + }; + Q_ENUM(SagittariusA) + }; +} + +#endif // ENUM_NS_HELLO_H diff --git a/tests/auto/qml/qmltyperegistrar/foo.cpp b/tests/auto/qml/qmltyperegistrar/foo.cpp index ba95235bd6..c5ef8313dc 100644 --- a/tests/auto/qml/qmltyperegistrar/foo.cpp +++ b/tests/auto/qml/qmltyperegistrar/foo.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "foo.h" #include <QDebug> diff --git a/tests/auto/qml/qmltyperegistrar/foo.h b/tests/auto/qml/qmltyperegistrar/foo.h index 7872c35c17..5af2e11eaa 100644 --- a/tests/auto/qml/qmltyperegistrar/foo.h +++ b/tests/auto/qml/qmltyperegistrar/foo.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef FOO_H #define FOO_H diff --git a/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt index 5334225692..68223ae6a5 100644 --- a/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt +++ b/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt @@ -10,7 +10,8 @@ qt_internal_add_cmake_library(foreign STATIC SOURCES - foreign.cpp foreign.h foreign_p.h + foreign.cpp foreign.h + private/foreign_p.h PUBLIC_LIBRARIES Qt::Core ) diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp index f78dd47237..1691f5396a 100644 --- a/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "foreign.h" diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.h b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h index ea78a58432..79ac8074cf 100644 --- a/tests/auto/qml/qmltyperegistrar/foreign/foreign.h +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef FOREIGN_H #define FOREIGN_H diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h b/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h index fe7986471f..ed23d78284 100644 --- a/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h +++ b/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef FOREIGN_P_H #define FOREIGN_P_H @@ -7,7 +7,6 @@ #include <QtCore/qobject.h> // qmltyperegistrar will assume this file is reachable under <private/foreign_p.h> -// It's not true, but this is how it works on actual private headers in Qt. // See the trick in tst_qmltyperegistrar's CMakeLists.txt to turn on the --private-includes option. class ForeignPrivate : public QObject { diff --git a/tests/auto/qml/qmltyperegistrar/hppheader.hpp b/tests/auto/qml/qmltyperegistrar/hppheader.hpp index dc82fc8530..4278601a9e 100644 --- a/tests/auto/qml/qmltyperegistrar/hppheader.hpp +++ b/tests/auto/qml/qmltyperegistrar/hppheader.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef HPPHEADER_HPP #define HPPHEADER_HPP diff --git a/tests/auto/qml/qmltyperegistrar/missingTypes.json b/tests/auto/qml/qmltyperegistrar/missingTypes.json new file mode 100644 index 0000000000..dacec11c4c --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/missingTypes.json @@ -0,0 +1,167 @@ +[ + { + "classes": [ + { + "classInfos": [ + { + "name": "QML.Element", + "value": "int" + }, + { + "name": "QML.Extended", + "value": "QQmlIntForeign" + }, + { + "name": "QML.Foreign", + "value": "int" + } + ], + "className": "QQmlIntForeign", + "gadget": true, + "qualifiedClassName": "QQmlIntForeign" + }, + { + "classInfos": [ + { + "name": "QML.Element", + "value": "auto" + } + ], + "className": "ExcessiveVersion", + "object": true, + "properties": [ + { + "constant": false, + "designable": true, + "final": false, + "index": 0, + "name": "palette", + "notify": "paletteChanged", + "read": "palette", + "required": false, + "revision": 1536, + "scriptable": true, + "stored": true, + "type": "int", + "user": false, + "write": "setPalette" + } + ], + "enums": [ + { + "isClass": false, + "isFlag": false, + "name": "RestorationMode", + "type": "NotAnUnderlyingType", + "values": [ + "RestoreNone", + "RestoreBinding", + "RestoreValue", + "RestoreBindingOrValue" + ] + } + ], + "qualifiedClassName": "ExcessiveVersion", + "signals": [ + { + "access": "public", + "name": "paletteChanged", + "returnType": "void", + "revision": 1536 + } + ], + "superClasses": [ + { + "access": "public", + "name": "NotQObject" + } + ] + }, + { + "classInfos": [ + { + "name": "QML.Element", + "value": "Versioned" + }, + { + "name": "QML.AddedInVersion", + "value": "264" + } + ], + "className": "AddedInLateVersion", + "object": true, + "properties": [ + { + "constant": true, + "designable": true, + "final": false, + "index": 0, + "name": "revisioned", + "read": "revisioned", + "required": false, + "revision": 260, + "scriptable": true, + "stored": true, + "type": "NotAPropertyType", + "user": false + } + ], + "methods": [ + { + "access": "public", + "arguments": [ + { + "type": "NotAnArgumentType" + } + ], + "name": "createAThing", + "returnType": "NotAReturnType" + } + ], + "qualifiedClassName": "AddedInLateVersion", + "superClasses": [ + { + "access": "public", + "name": "NotQObject" + } + ] + }, + { + "classInfos": [ + { + "name": "QML.Foreign", + "value": "Invisible" + }, + { + "name": "QML.Element", + "value": "Invisible" + } + ], + "className": "InvisibleForeign", + "gadget": true, + "qualifiedClassName": "InvisibleForeign" + }, + { + "classInfos": [ + { + "name": "QML.Element", + "value": "anonymous" + }, + { + "name": "QML.Sequence", + "value": "NotQByteArray" + }, + { + "name": "QML.Foreign", + "value": "std::vector<NotQByteArray>" + } + ], + "className": "NotQByteArrayStdVectorForeign", + "gadget": true, + "qualifiedClassName": "NotQByteArrayStdVectorForeign" + } + ], + "inputFile": "tst_qmltyperegistrar.h", + "outputRevision": 68 + } +] diff --git a/tests/auto/qml/qmltyperegistrar/noextheader b/tests/auto/qml/qmltyperegistrar/noextheader index 1bad168044..2b3579a34b 100644 --- a/tests/auto/qml/qmltyperegistrar/noextheader +++ b/tests/auto/qml/qmltyperegistrar/noextheader @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef NOEXTHEADER #define NOEXTHEADER diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp index a0f9a92b5c..e79e9b84b6 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_qmltyperegistrar.h" #include <QtTest/qtest.h> @@ -116,8 +116,8 @@ void tst_qmltyperegistrar::pastMajorVersions() void tst_qmltyperegistrar::implementsInterfaces() { - QVERIFY(qmltypesData.contains("interfaces: [\"Interface\"]")); - QVERIFY(qmltypesData.contains("interfaces: [\"Interface\", \"Interface2\"]")); + QVERIFY(qmltypesData.contains("interfaces: [\"Interface1\"]")); + QVERIFY(qmltypesData.contains("interfaces: [\"Interface1\", \"Interface2\"]")); } void tst_qmltyperegistrar::namespacedElement() @@ -145,9 +145,9 @@ void tst_qmltyperegistrar::metaTypesRegistered() auto verifyMetaType = [](const char *name, const char *className) { const auto foundMetaType = QMetaType::fromName(name); - QVERIFY(foundMetaType.isValid()); + QVERIFY2(foundMetaType.isValid(), name); QCOMPARE(foundMetaType.name(), name); - QVERIFY(foundMetaType.metaObject()); + QVERIFY2(foundMetaType.metaObject(), name); QCOMPARE(foundMetaType.metaObject()->className(), className); }; @@ -356,6 +356,12 @@ void tst_qmltyperegistrar::addRemoveVersion() QCOMPARE(o->property("thing").toInt(), thingAccessible ? 24 : 0); } +void tst_qmltyperegistrar::addInMinorVersion() +{ + QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/MinorVersioned 1.5\"]")); + QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/MinorVersioned 1.2\"]")); +} + #ifdef QT_QUICK_LIB void tst_qmltyperegistrar::foreignRevisionedProperty() { @@ -398,23 +404,83 @@ void tst_qmltyperegistrar::duplicateExportWarnings() MetaTypesJsonProcessor processor(true); QVERIFY(processor.processTypes({ ":/duplicatedExports.json" })); processor.postProcessTypes(); - QVector<QJsonObject> types = processor.types(); - QVector<QJsonObject> typesforeign = processor.foreignTypes(); + QVector<MetaType> types = processor.types(); + QVector<MetaType> typesforeign = processor.foreignTypes(); r.setTypes(types, typesforeign); - auto expectWarning = [](QString message) { - QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); + const auto expectWarning = [](const char *message) { + QTest::ignoreMessage(QtWarningMsg, message); }; - expectWarning("Warning: ExportedQmlElement was registered multiple times by following Cpp " - "classes: ExportedQmlElement, ExportedQmlElement2 (added in 1.2), " - "ExportedQmlElementDifferentVersion (added in 1.0) (removed in 1.7)"); - expectWarning("Warning: SameNameSameExport was registered multiple times by following Cpp " - "classes: SameNameSameExport, SameNameSameExport2 (added in 1.2), " - "SameNameSameExportDifferentVersion (added in 1.0)"); + expectWarning("Warning: duplicatedExports.h:: ExportedQmlElement is registered multiple times " + "by the following C++ classes: ExportedQmlElement, ExportedQmlElement2 " + "(added in 1.2), ExportedQmlElementDifferentVersion (added in 1.0) " + "(removed in 1.7)"); + expectWarning("Warning: duplicatedExports.h:: SameNameSameExport is registered multiple times " + "by the following C++ classes: SameNameSameExport, SameNameSameExport2 " + "(added in 1.2), SameNameSameExportDifferentVersion (added in 1.0)"); QString outputData; QTextStream output(&outputData, QIODeviceBase::ReadWrite); - r.write(output); + r.write(output, "tst_qmltyperegistrar_qmltyperegistrations.cpp"); +} + +void tst_qmltyperegistrar::consistencyWarnings() +{ + QmlTypeRegistrar r; + r.setModuleVersions(QTypeRevision::fromVersion(1, 1), {}, false); + QString moduleName = "tstmodule"; + QString targetNamespace = "tstnamespace"; + r.setModuleNameAndNamespace(moduleName, targetNamespace); + + MetaTypesJsonProcessor processor(true); + + QVERIFY(processor.processTypes({ ":/missingTypes.json" })); + processor.postProcessTypes(); + + const auto expectWarning = [](const char *message) { + QTest::ignoreMessage(QtWarningMsg, message); + }; + + expectWarning("Warning: tst_qmltyperegistrar.h:: " + "NotQObject is used as base type but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: NotQObject is used as base type " + "but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: Invisible is declared as foreign type, " + "but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: NotQByteArray is used as sequence value type " + "but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: NotAPropertyType is used as property type " + "but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnArgumentType is used as argument type " + "but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: NotAReturnType is used as return type " + "but cannot be found."); + expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnUnderlyingType is used as enum type " + "but cannot be found."); + + processor.postProcessForeignTypes(); + + QVector<MetaType> types = processor.types(); + QVector<MetaType> typesforeign = processor.foreignTypes(); + r.setTypes(types, typesforeign); + + QString outputData; + QTextStream output(&outputData, QIODeviceBase::ReadWrite); + + expectWarning("Warning: tst_qmltyperegistrar.h:: AddedInLateVersion is trying to register " + "property revisioned with future version 1.4 when module version is only 1.1"); + expectWarning("Warning: tst_qmltyperegistrar.h:: ExcessiveVersion is trying to register " + "property palette with future version 6.0 when module version is only 1.1"); + + r.write(output, "tst_qmltyperegistrar_qmltyperegistrations.cpp"); + + QTemporaryFile pluginTypes; + QVERIFY(pluginTypes.open()); + + expectWarning("Warning: tst_qmltyperegistrar.h:: Refusing to generate non-lowercase name " + "Invisible for unknown foreign type"); + + r.generatePluginTypes(pluginTypes.fileName()); } void tst_qmltyperegistrar::clonedSignal() @@ -432,17 +498,17 @@ void tst_qmltyperegistrar::hasIsConstantInParameters() QVERIFY(qmltypesData.contains(R"( Signal { name: "mySignal" Parameter { name: "myObject"; type: "QObject"; isPointer: true } - Parameter { name: "myConstObject"; type: "QObject"; isPointer: true; isConstant: true } - Parameter { name: "myConstObject2"; type: "QObject"; isPointer: true; isConstant: true } + Parameter { name: "myConstObject"; type: "QObject"; isPointer: true; isTypeConstant: true } + Parameter { name: "myConstObject2"; type: "QObject"; isPointer: true; isTypeConstant: true } Parameter { name: "myObject2"; type: "QObject"; isPointer: true } - Parameter { name: "myConstObject3"; type: "QObject"; isPointer: true; isConstant: true } + Parameter { name: "myConstObject3"; type: "QObject"; isPointer: true; isTypeConstant: true } } )")); QVERIFY(qmltypesData.contains(R"(Signal { name: "myVolatileSignal" - Parameter { name: "a"; type: "volatile QObject"; isPointer: true; isConstant: true } - Parameter { name: "b"; type: "volatile QObject"; isPointer: true; isConstant: true } + Parameter { name: "a"; type: "volatile QObject"; isPointer: true; isTypeConstant: true } + Parameter { name: "b"; type: "volatile QObject"; isPointer: true; isTypeConstant: true } Parameter { name: "nonConst"; type: "volatile QObject"; isPointer: true } } )")); @@ -450,70 +516,101 @@ void tst_qmltyperegistrar::hasIsConstantInParameters() void tst_qmltyperegistrar::uncreatable() { + using namespace QQmlPrivate; + // "normal" constructible types - QVERIFY(QQmlPrivate::QmlMetaType<Creatable>::hasAcceptableCtors()); - QVERIFY(QQmlPrivate::QmlMetaType<Creatable2>::hasAcceptableCtors()); + QVERIFY(QmlMetaType<Creatable>::hasAcceptableCtors()); + QVERIFY(QmlMetaType<Creatable2>::hasAcceptableCtors()); // good singletons - QVERIFY(QQmlPrivate::QmlMetaType<SingletonCreatable>::hasAcceptableSingletonCtors()); - QVERIFY(QQmlPrivate::QmlMetaType<SingletonCreatable2>::hasAcceptableSingletonCtors()); - QVERIFY(QQmlPrivate::QmlMetaType<SingletonCreatable3>::hasAcceptableSingletonCtors()); + QCOMPARE((singletonConstructionMode<SingletonCreatable, SingletonCreatable>()), + SingletonConstructionMode::Factory); + QCOMPARE((singletonConstructionMode<SingletonCreatable2, SingletonCreatable2>()), + SingletonConstructionMode::Constructor); + QCOMPARE((singletonConstructionMode<SingletonCreatable2, SingletonCreatable2>()), + SingletonConstructionMode::Constructor); + QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalCreatable>()), + SingletonConstructionMode::FactoryWrapper); // bad singletons - QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable>::hasAcceptableSingletonCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable2>::hasAcceptableSingletonCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable3>::hasAcceptableSingletonCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable4>::hasAcceptableSingletonCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatableExtended>::hasAcceptableSingletonCtors()); + QCOMPARE((singletonConstructionMode<SingletonIncreatable, SingletonIncreatable>()), + SingletonConstructionMode::None); + QCOMPARE((singletonConstructionMode<SingletonIncreatable2, SingletonIncreatable2>()), + SingletonConstructionMode::None); + QCOMPARE((singletonConstructionMode<SingletonIncreatable3, SingletonIncreatable3>()), + SingletonConstructionMode::None); + QCOMPARE((singletonConstructionMode<SingletonIncreatable4, SingletonIncreatable4>()), + SingletonConstructionMode::None); + QCOMPARE((singletonConstructionMode<SingletonIncreatableExtended, + SingletonIncreatableExtended>()), + SingletonConstructionMode::None); + QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalUncreatable1>()), + SingletonConstructionMode::None); + QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalUncreatable2>()), + SingletonConstructionMode::None); #if QT_DEPRECATED_SINCE(6, 4) QTest::ignoreMessage( QtWarningMsg, - "Singleton SingletonIncreatable needs either a default constructor or, " - "when adding a default constructor is infeasible, a public static " - "create(QQmlEngine *, QJSEngine *) method."); + "Singleton SingletonIncreatable needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); qmlRegisterTypesAndRevisions<SingletonIncreatable>("A", 1); QTest::ignoreMessage( QtWarningMsg, - "Singleton SingletonIncreatable2 needs either a default constructor or, " - "when adding a default constructor is infeasible, a public static " - "create(QQmlEngine *, QJSEngine *) method."); + "Singleton SingletonIncreatable2 needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); qmlRegisterTypesAndRevisions<SingletonIncreatable2>("A", 1); QTest::ignoreMessage( QtWarningMsg, - "Singleton SingletonIncreatable3 needs either a default constructor or, " - "when adding a default constructor is infeasible, a public static " - "create(QQmlEngine *, QJSEngine *) method."); + "Singleton SingletonIncreatable3 needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); qmlRegisterTypesAndRevisions<SingletonIncreatable3>("A", 1); QTest::ignoreMessage( QtWarningMsg, - "Singleton SingletonIncreatable4 needs either a default constructor or, " - "when adding a default constructor is infeasible, a public static " - "create(QQmlEngine *, QJSEngine *) method."); + "Singleton SingletonIncreatable4 needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); qmlRegisterTypesAndRevisions<SingletonIncreatable4>("A", 1); QTest::ignoreMessage( QtWarningMsg, - "Singleton SingletonIncreatableExtended needs either a default constructor or, " - "when adding a default constructor is infeasible, a public static " - "create(QQmlEngine *, QJSEngine *) method."); + "Singleton SingletonIncreatableExtended needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); qmlRegisterTypesAndRevisions<SingletonIncreatableExtended>("A", 1); + QTest::ignoreMessage( + QtWarningMsg, + "Singleton SingletonForeign needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); + qmlRegisterTypesAndRevisions<SingletonLocalUncreatable1>("A", 1); + QTest::ignoreMessage( + QtWarningMsg, + "Singleton SingletonForeign needs to be a concrete class with either a " + "default constructor or, when adding a default constructor is infeasible, " + "a public static create(QQmlEngine *, QJSEngine *) method."); + qmlRegisterTypesAndRevisions<SingletonLocalUncreatable2>("A", 1); #endif // QML_UNCREATABLE types - QVERIFY(!QQmlPrivate::QmlMetaType<BadUncreatable>::hasAcceptableCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<BadUncreatableExtended>::hasAcceptableCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<GoodUncreatable>::hasAcceptableCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<UncreatableNeedsForeign>::hasAcceptableCtors()); - QVERIFY(!QQmlPrivate::QmlMetaType<GoodUncreatableExtended>::hasAcceptableCtors()); + QVERIFY(!QmlMetaType<BadUncreatable>::hasAcceptableCtors()); + QVERIFY(!QmlMetaType<BadUncreatableExtended>::hasAcceptableCtors()); + QVERIFY(!QmlMetaType<GoodUncreatable>::hasAcceptableCtors()); + QVERIFY(!QmlMetaType<UncreatableNeedsForeign>::hasAcceptableCtors()); + QVERIFY(!QmlMetaType<GoodUncreatableExtended>::hasAcceptableCtors()); #if QT_DEPRECATED_SINCE(6, 4) QTest::ignoreMessage( QtWarningMsg, - "BadUncreatable is neither a QObject, nor default- and copy-constructible, " - "nor uncreatable. You should not use it as a QML type."); + "BadUncreatable is neither a default constructible QObject, nor a default- " + "and copy-constructible Q_GADGET, nor marked as uncreatable.\n" + "You should not use it as a QML type."); qmlRegisterTypesAndRevisions<BadUncreatable>("A", 1); QTest::ignoreMessage( QtWarningMsg, - "BadUncreatableExtended is neither a QObject, nor default- and copy-constructible, " - "nor uncreatable. You should not use it as a QML type."); + "BadUncreatableExtended is neither a default constructible QObject, nor a default- " + "and copy-constructible Q_GADGET, nor marked as uncreatable.\n" + "You should not use it as a QML type."); qmlRegisterTypesAndRevisions<BadUncreatableExtended>("A", 1); #endif @@ -537,6 +634,26 @@ void tst_qmltyperegistrar::uncreatable() qmlRegisterTypesAndRevisions<GoodUncreatableExtended>("A", 1); } +void tst_qmltyperegistrar::singletonVersions() +{ + QQmlEngine engine; + qmlRegisterTypesAndRevisions<SingletonVesion0>("A", 0); + qmlRegisterTypesAndRevisions<SingletonVesion1>("B", 1); + + QQmlComponent c(&engine); + c.setData("import QtQuick\n" + "import A\n" + "import B\n" + "QtObject {\n" + " property QtObject v0: SingletonVesion0\n" + " property QtObject v1: SingletonVesion1\n" + "}", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> obj(c.create()); + QVERIFY2(!obj->property("v0").isNull(), "Singleton version 0 is not registered"); + QVERIFY2(!obj->property("v1").isNull(), "Singleton version 1 is not registered"); +} + void tst_qmltyperegistrar::baseVersionInQmltypes() { // Since it has no QML_ADDED_IN_VERSION, WithMethod was added in .0 of the current version. @@ -544,4 +661,428 @@ void tst_qmltyperegistrar::baseVersionInQmltypes() QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/WithMethod 1.0\"]")); } +void tst_qmltyperegistrar::unconstructibleValueType() +{ + QVERIFY(qmltypesData.contains( + R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Unconstructible" + accessSemantics: "value" + exports: ["QmlTypeRegistrarTest/unconstructible 1.0"] + isCreatable: false + exportMetaObjectRevisions: [256] + })")); +} + +void tst_qmltyperegistrar::constructibleValueType() +{ + QVERIFY(qmltypesData.contains( + R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Constructible" + accessSemantics: "value" + exports: ["QmlTypeRegistrarTest/constructible 1.0"] + exportMetaObjectRevisions: [256] + Method { + name: "Constructible" + isConstructor: true + Parameter { name: "i"; type: "int" } + } + Method { name: "Constructible"; isCloned: true; isConstructor: true } + })")); +} + +void tst_qmltyperegistrar::structuredValueType() +{ + QVERIFY(qmltypesData.contains( + R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Structured" + accessSemantics: "value" + exports: ["QmlTypeRegistrarTest/structured 1.0"] + isStructured: true + exportMetaObjectRevisions: [256] + Property { name: "i"; type: "int"; index: 0; isFinal: true } + })")); +} + +void tst_qmltyperegistrar::anonymousAndUncreatable() +{ + QVERIFY(qmltypesData.contains( + R"(Component { + file: "tst_qmltyperegistrar.h" + name: "AnonymousAndUncreatable" + accessSemantics: "reference" + prototype: "QObject" + })")); +} + +void tst_qmltyperegistrar::omitInvisible() +{ + // If it cannot resolve the type a QML_FOREIGN refers to, it should not generate anything. + QVERIFY(qmltypesData.contains( + R"(Component { file: "tst_qmltyperegistrar.h"; name: "Invisible"; accessSemantics: "none" })")); +} + +void tst_qmltyperegistrar::typedEnum() +{ + QVERIFY(qmltypesData.contains( + R"(Component { + file: "tst_qmltyperegistrar.h" + name: "TypedEnum" + accessSemantics: "reference" + prototype: "QObject" + exports: ["QmlTypeRegistrarTest/TypedEnum 1.0"] + exportMetaObjectRevisions: [256] + Enum { + name: "UChar" + type: "quint8" + values: ["V0"] + } + Enum { + name: "Int8_T" + type: "qint8" + values: ["V1"] + } + Enum { + name: "UInt8_T" + type: "quint8" + values: ["V2"] + } + Enum { + name: "Int16_T" + type: "short" + values: ["V3"] + } + Enum { + name: "UInt16_T" + type: "ushort" + values: ["V4"] + } + Enum { + name: "Int32_T" + type: "int" + values: ["V5"] + } + Enum { + name: "UInt32_T" + type: "uint" + values: ["V6"] + } + Enum { + name: "S" + type: "short" + values: ["A", "B", "C"] + } + Enum { + name: "T" + type: "ushort" + values: ["D", "E", "F"] + } + Enum { + name: "U" + type: "qint8" + values: ["G", "H", "I"] + } + Enum { + name: "V" + type: "quint8" + values: ["J", "K", "L"] + } + })")); +} + +void tst_qmltyperegistrar::listSignal() +{ + QVERIFY(qmltypesData.contains( + R"(Component { + file: "tst_qmltyperegistrar.h" + name: "ListSignal" + accessSemantics: "reference" + prototype: "QObject" + Signal { + name: "objectListHappened" + Parameter { type: "QObjectList" } + } + })")); +} + +void tst_qmltyperegistrar::withNamespace() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Bar" + accessSemantics: "reference" + prototype: "QObject" + Property { + name: "outerBarProp" + type: "int" + read: "bar" + index: 0 + isReadonly: true + isPropertyConstant: true + } + })")); + + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Testing::Bar" + accessSemantics: "reference" + prototype: "Testing::Foo" + exports: ["QmlTypeRegistrarTest/Bar 1.0"] + exportMetaObjectRevisions: [256] + Property { + name: "barProp" + type: "int" + read: "bar" + index: 0 + isReadonly: true + isPropertyConstant: true + } + })")); + + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Testing::Foo" + accessSemantics: "reference" + prototype: "QObject" + Property { + name: "fooProp" + type: "int" + read: "foo" + index: 0 + isReadonly: true + isPropertyConstant: true + } + })")); + + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "Testing::Inner::Baz" + accessSemantics: "reference" + prototype: "Testing::Bar" + extension: "Bar" + exports: ["QmlTypeRegistrarTest/Baz 1.0"] + exportMetaObjectRevisions: [256] + attachedType: "Testing::Foo" + })")); +} + +void tst_qmltyperegistrar::sequenceRegistration() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "std::vector<QByteArray>" + accessSemantics: "sequence" + valueType: "QByteArray" + })")); +} + +void tst_qmltyperegistrar::valueTypeSelfReference() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "QPersistentModelIndex" + accessSemantics: "value" + extension: "QPersistentModelIndexValueType" + })")); + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "QPersistentModelIndexValueType" + accessSemantics: "value" + Property { name: "row"; type: "int"; read: "row"; index: 0; isReadonly: true; isFinal: true } + })")); +} + +void tst_qmltyperegistrar::foreignNamespaceFromGadget() +{ + QQmlEngine engine; + { + QQmlComponent c(&engine); + c.setData(QStringLiteral(R"( + import QtQml + import QmlTypeRegistrarTest + QtObject { + objectName: 'b' + NetworkManager.B + } + )").toUtf8(), QUrl("foreignNamespaceFromGadget.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QCOMPARE(o->objectName(), QStringLiteral("b1")); + } + + { + QQmlComponent c(&engine); + c.setData(QStringLiteral(R"( + import QtQml + import QmlTypeRegistrarTest + QtObject { + objectName: 'b' + NotNamespaceForeign.B + } + )").toUtf8(), QUrl("foreignNamespaceFromGadget2.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QCOMPARE(o->objectName(), QStringLiteral("b1")); + } +} + +void tst_qmltyperegistrar::nameExplosion_data() +{ + QTest::addColumn<QByteArray>("qml"); + QTest::addRow("Name1") << QByteArray("import QmlTypeRegistrarTest\nName1{}"); + QTest::addRow("Name2") << QByteArray("import QmlTypeRegistrarTest\nName2{}"); + QTest::addRow("NameExplosion") << QByteArray("import QmlTypeRegistrarTest\nNameExplosion{}"); +} + +void tst_qmltyperegistrar::nameExplosion() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "NameExplosion" + accessSemantics: "reference" + prototype: "QObject" + exports: [ + "QmlTypeRegistrarTest/Name1 1.0", + "QmlTypeRegistrarTest/Name2 1.0", + "QmlTypeRegistrarTest/NameExplosion 1.0" + ] + exportMetaObjectRevisions: [256] + })")); + + QFETCH(QByteArray, qml); + + QQmlEngine engine; + QQmlComponent c(&engine); + + c.setData(qml, QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} + +void tst_qmltyperegistrar::javaScriptExtension() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "JavaScriptExtension" + accessSemantics: "reference" + prototype: "QObject" + extension: "SymbolPrototype" + extensionIsJavaScript: true + exports: ["QmlTypeRegistrarTest/JavaScriptExtension 1.0"] + exportMetaObjectRevisions: [256] + })")); +} + +void tst_qmltyperegistrar::relatedAddedInVersion() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "AddedIn1_0" + accessSemantics: "reference" + prototype: "AddedIn1_5" + exports: [ + "QmlTypeRegistrarTest/AddedIn1_0 1.0", + "QmlTypeRegistrarTest/AddedIn1_0 1.5" + ] + exportMetaObjectRevisions: [256, 261] + })")); +} + +void tst_qmltyperegistrar::longNumberTypes() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "LongNumberTypes" + accessSemantics: "reference" + prototype: "QObject" + exports: ["QmlTypeRegistrarTest/LongNumberTypes 1.0"] + exportMetaObjectRevisions: [256] + Property { name: "a"; type: "qlonglong"; index: 0 } + Property { name: "b"; type: "qlonglong"; index: 1 } + Property { name: "c"; type: "qulonglong"; index: 2 } + Property { name: "d"; type: "qulonglong"; index: 3 } + })")); +} + +void tst_qmltyperegistrar::enumList() { + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "QList<NetworkManager::NM>" + accessSemantics: "sequence" + valueType: "NetworkManager::NM" + })")); +} + +void tst_qmltyperegistrar::constReturnType() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "ConstInvokable" + accessSemantics: "reference" + prototype: "QObject" + exports: ["QmlTypeRegistrarTest/ConstInvokable 1.0"] + exportMetaObjectRevisions: [256] + Method { name: "getObject"; type: "QObject"; isPointer: true; isTypeConstant: true } + })")); +} + +void tst_qmltyperegistrar::usingDeclaration() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "WithMyInt" + accessSemantics: "reference" + prototype: "QObject" + exports: ["QmlTypeRegistrarTest/WithMyInt 1.0"] + exportMetaObjectRevisions: [256] + Property { name: "a"; type: "int"; read: "a"; index: 0; isReadonly: true; isPropertyConstant: true } + })")); +} + +void tst_qmltyperegistrar::enumsRegistered() +{ + QCOMPARE(QMetaType::fromName("SizeEnums::Unit"), QMetaType::fromType<SizeEnums::Unit>()); + QCOMPARE(QMetaType::fromName("Local::Flag"), QMetaType::fromType<Local::Flag>()); + QCOMPARE(QMetaType::fromName("Local::Flags"), QMetaType::fromType<Local::Flags>()); + QCOMPARE(QMetaType::fromName("ValueTypeWithEnum1::Quality"), + QMetaType::fromType<ValueTypeWithEnum1::Quality>()); + QCOMPARE(QMetaType::fromName("ValueTypeWithEnum2::Quality"), + QMetaType::fromType<ValueTypeWithEnum2::Quality>()); + QCOMPARE(QMetaType::fromName("BaseNamespace::BBB"), QMetaType::fromType<BaseNamespace::BBB>()); + QCOMPARE(QMetaType::fromName("ExtensionValueType::EEE"), + QMetaType::fromType<ExtensionValueType::EEE>()); + QCOMPARE(QMetaType::fromName("TypedEnum::UChar"), QMetaType::fromType<TypedEnum::UChar>()); + QCOMPARE(QMetaType::fromName("TypedEnum::Int8_T"), QMetaType::fromType<TypedEnum::Int8_T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::UInt8_T"), QMetaType::fromType<TypedEnum::UInt8_T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::Int16_T"), QMetaType::fromType<TypedEnum::Int16_T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::UInt16_T"), QMetaType::fromType<TypedEnum::UInt16_T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::Int32_T"), QMetaType::fromType<TypedEnum::Int32_T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::UInt32_T"), QMetaType::fromType<TypedEnum::UInt32_T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::S"), QMetaType::fromType<TypedEnum::S>()); + QCOMPARE(QMetaType::fromName("TypedEnum::T"), QMetaType::fromType<TypedEnum::T>()); + QCOMPARE(QMetaType::fromName("TypedEnum::U"), QMetaType::fromType<TypedEnum::U>()); + QCOMPARE(QMetaType::fromName("TypedEnum::V"), QMetaType::fromType<TypedEnum::V>()); + QCOMPARE(QMetaType::fromName("NetworkManager::NM"), QMetaType::fromType<NetworkManager::NM>()); + QCOMPARE(QMetaType::fromName("NotNamespace::Abc"), QMetaType::fromType<NotNamespace::Abc>()); +} + +void tst_qmltyperegistrar::doNotDuplicateQtNamespace() +{ + QVERIFY(!qmltypesData.contains(R"(file: "qnamespace.h")")); +} + +void tst_qmltyperegistrar::slotsBeforeInvokables() +{ + QVERIFY(qmltypesData.contains(R"(Component { + file: "tst_qmltyperegistrar.h" + name: "SlotsBeforeInvokables" + accessSemantics: "reference" + prototype: "QObject" + Method { name: "bar" } + Method { name: "foo" } + Method { name: "baz" } + })")); +} + QTEST_MAIN(tst_qmltyperegistrar) diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h index df755472d7..1eff2af024 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h @@ -1,46 +1,50 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TST_QMLTYPEREGISTRAR_H #define TST_QMLTYPEREGISTRAR_H #include "foreign.h" -#include "foreign_p.h" +#include "private/foreign_p.h" -#include <QtQml/qqml.h> -#include <QtQml/qqmlcomponent.h> -#include <QtCore/qproperty.h> -#include <QtCore/qtimeline.h> -#include <QtCore/qrect.h> #include <QtQmlTypeRegistrar/private/qqmltyperegistrar_p.h> -#include <QtCore/qtemporaryfile.h> #ifdef QT_QUICK_LIB # include <QtQuick/qquickitem.h> #endif -class Interface {}; +#include <QtQml/qqml.h> +#include <QtQml/qqmlcomponent.h> + +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qnamespace.h> +#include <QtCore/qproperty.h> +#include <QtCore/qrect.h> +#include <QtCore/qtemporaryfile.h> +#include <QtCore/qtimeline.h> + +class Interface1 {}; class Interface2 {}; class Interface3 {}; QT_BEGIN_NAMESPACE -Q_DECLARE_INTERFACE(Interface, "io.qt.bugreports.Interface"); +Q_DECLARE_INTERFACE(Interface1, "io.qt.bugreports.Interface1"); Q_DECLARE_INTERFACE(Interface2, "io.qt.bugreports.Interface2"); Q_DECLARE_INTERFACE(Interface3, "io.qt.bugreports.Interface3"); QT_END_NAMESPACE -class ImplementsInterfaces : public QObject, public Interface +class ImplementsInterfaces : public QObject, public Interface1 { Q_OBJECT QML_ELEMENT - QML_IMPLEMENTS_INTERFACES(Interface) + QML_IMPLEMENTS_INTERFACES(Interface1) }; -class ImplementsInterfaces2 : public QObject, public Interface, public Interface2 +class ImplementsInterfaces2 : public QObject, public Interface1, public Interface2 { Q_OBJECT QML_ELEMENT - QML_IMPLEMENTS_INTERFACES(Interface Interface2) + QML_IMPLEMENTS_INTERFACES(Interface1 Interface2) }; class ExcessiveVersion : public QObject @@ -458,6 +462,29 @@ public: int revisioned() const { return 24; } }; +class AddedInLateMinorVersion : public QObject +{ + Q_OBJECT + QML_ADDED_IN_VERSION(1, 5) + Q_PROPERTY(int revisioned READ revisioned CONSTANT) + QML_NAMED_ELEMENT(MinorVersioned) +public: + AddedInLateMinorVersion(QObject *parent = nullptr) : QObject(parent) {} + int revisioned() const { return 123; } +}; + +class RemovedInLateMinorVersion : public QObject +{ + Q_OBJECT + QML_ADDED_IN_VERSION(1, 2) + QML_REMOVED_IN_VERSION(1, 4) + Q_PROPERTY(int revisioned READ revisioned CONSTANT) + QML_NAMED_ELEMENT(MinorVersioned) +public: + RemovedInLateMinorVersion(QObject *parent = nullptr) : QObject(parent) { } + int revisioned() const { return 456; } +}; + class RemovedInEarlyVersion : public AddedInLateVersion { Q_OBJECT @@ -468,6 +495,23 @@ public: RemovedInEarlyVersion(QObject *parent = nullptr) : AddedInLateVersion(parent) {} }; +class AddedIn1_5 : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_ADDED_IN_VERSION(1, 5) +}; + +// Slightly absurd. The reason for such a thing may be a change in the versioning +// scheme of the base class. We still have to retain all of the version information +// so that you can at least use version 1.5. +class AddedIn1_0 : public AddedIn1_5 +{ + Q_OBJECT + QML_ELEMENT + QML_ADDED_IN_VERSION(1, 0) +}; + class HasResettableProperty : public QObject { Q_OBJECT @@ -501,6 +545,293 @@ signals: void clonedSignal(int i = 7); }; +class Unconstructible +{ + Q_GADGET + QML_VALUE_TYPE(unconstructible) + int m_i = 11; +}; + +class Constructible +{ + Q_GADGET + QML_VALUE_TYPE(constructible) + QML_CONSTRUCTIBLE_VALUE +public: + Q_INVOKABLE Constructible(int i = 12) : m_i(i) {} + +private: + int m_i; +}; + +class Structured +{ + Q_GADGET + QML_VALUE_TYPE(structured) + QML_STRUCTURED_VALUE + Q_PROPERTY(int i MEMBER m_i FINAL) + +private: + int m_i; +}; + +class AnonymousAndUncreatable : public QObject +{ + Q_OBJECT + QML_ANONYMOUS + QML_UNCREATABLE("Pointless uncreatable message") +}; + +class Invisible : public QObject +{ +}; + +struct InvisibleForeign +{ + Q_GADGET + QML_FOREIGN(Invisible) + QML_NAMED_ELEMENT(Invisible) +}; + +class TypedEnum : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + enum UChar: uchar { V0 = 41 }; + Q_ENUM(UChar) + enum Int8_T: int8_t { V1 = 42 }; + Q_ENUM(Int8_T) + enum UInt8_T: uint8_t { V2 = 43 }; + Q_ENUM(UInt8_T) + enum Int16_T: int16_t { V3 = 44 }; + Q_ENUM(Int16_T) + enum UInt16_T: uint16_t { V4 = 45 }; + Q_ENUM(UInt16_T) + enum Int32_T: int32_t { V5 = 46 }; + Q_ENUM(Int32_T) + enum UInt32_T: uint32_t { V6 = 47 }; + Q_ENUM(UInt32_T) + + // TODO: We cannot handle 64bit numbers as underlying types for enums. + // Luckily, moc generates bad code for those. So we don't have to, for now. + + enum S: qint16 { + A, B, C + }; + Q_ENUM(S) + + enum T: quint16 { + D, E, F + }; + Q_ENUM(T) + + enum U: qint8 { + G, H, I + }; + Q_ENUM(U) + + enum V: quint8 { + J, K, L + }; + Q_ENUM(V) +}; + +class ListSignal : public QObject +{ + Q_OBJECT + QML_ANONYMOUS + +Q_SIGNALS: + void objectListHappened(const QList<QObject *> &); +}; + +class Bar : public QObject +{ + Q_OBJECT + Q_PROPERTY(int outerBarProp READ bar CONSTANT) +public: + Bar(QObject *parent = nullptr) : QObject(parent) {} + int bar() const { return 44; } +}; + +namespace Testing { + +class Foo : public QObject +{ + Q_OBJECT + Q_PROPERTY(int fooProp READ foo CONSTANT) + +public: + int foo() const { return 42; } +}; + +class Bar : public Foo +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int barProp READ bar CONSTANT) + +public: + int bar() const { return 43; } +}; + +namespace Inner { + +class Baz : public Bar +{ + Q_OBJECT + QML_ELEMENT + + QML_EXTENDED(::Bar) + QML_ATTACHED(Foo) + +public: + static Foo *qmlAttachedProperties(QObject *) { return new Foo; } +}; + +} // namespace Inner +} // namespace Testing + +struct QByteArrayStdVectorForeign +{ + Q_GADGET + QML_ANONYMOUS + QML_SEQUENTIAL_CONTAINER(QByteArray) + QML_FOREIGN(std::vector<QByteArray>) +}; + +// Anonymous value type for an unknown foreign type +struct QPersistentModelIndexValueType +{ + QPersistentModelIndex v; + Q_PROPERTY(int row READ row FINAL) + Q_GADGET + QML_ANONYMOUS + QML_EXTENDED(QPersistentModelIndexValueType) + QML_FOREIGN(QPersistentModelIndex) + +public: + inline int row() const { return v.row(); } +}; + + +namespace NetworkManager { +Q_NAMESPACE + +enum NM { A, B, C}; +Q_ENUM_NS(NM) +} + +struct NMForeign +{ + Q_GADGET + QML_NAMED_ELEMENT(NetworkManager) + QML_FOREIGN_NAMESPACE(NetworkManager) +}; + +struct NotNamespace { + Q_GADGET +public: + enum Abc { + A, B, C, D + }; + Q_ENUM(Abc); +}; + +struct NotNamespaceForeign { + Q_GADGET + QML_FOREIGN_NAMESPACE(NotNamespace) + QML_ELEMENT +}; + +class NameExplosion : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(Name1) + QML_NAMED_ELEMENT(Name2) + QML_ELEMENT + QML_ANONYMOUS +}; + +class JavaScriptExtension : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_CLASSINFO("QML.Extended", "SymbolPrototype") + Q_CLASSINFO("QML.ExtensionIsJavaScript", "true") +}; + +class LongNumberTypes : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(qint64 a MEMBER m_a) + Q_PROPERTY(int64_t b MEMBER m_b) + Q_PROPERTY(quint64 c MEMBER m_c) + Q_PROPERTY(uint64_t d MEMBER m_d) + + qint64 m_a = 1; + int64_t m_b = 2; + quint64 m_c = 3; + uint64_t m_d = 4; +}; + +struct EnumList +{ + Q_GADGET + QML_ANONYMOUS + QML_FOREIGN(QList<NetworkManager::NM>) + QML_SEQUENTIAL_CONTAINER(NetworkManager::NM) +}; + +class ConstInvokable : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + Q_INVOKABLE const QObject *getObject() { return nullptr; } +}; + +using myint = int; + +struct IntAlias +{ + Q_GADGET + QML_FOREIGN(myint) + QML_USING(int); +}; + +class WithMyInt : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(myint a READ a CONSTANT) +public: + myint a() const { return 10; } +}; + +class UsesQtNamespace : public QObject +{ + Q_OBJECT + QML_ANONYMOUS + Q_PROPERTY(Qt::Key key READ key CONSTANT) +public: + Qt::Key key() const { return Qt::Key_Escape; } +}; + +class SlotsBeforeInvokables : public QObject +{ + Q_OBJECT + QML_ANONYMOUS +public: + Q_INVOKABLE void foo() {} +public Q_SLOTS: + void bar() {} +public: + Q_INVOKABLE void baz() {} +}; + class tst_qmltyperegistrar : public QObject { Q_OBJECT @@ -537,6 +868,7 @@ private slots: void methodReturnType(); void hasIsConstantInParameters(); void uncreatable(); + void singletonVersions(); #ifdef QT_QUICK_LIB void foreignRevisionedProperty(); @@ -544,11 +876,39 @@ private slots: void addRemoveVersion_data(); void addRemoveVersion(); + void addInMinorVersion(); void typeInModuleMajorVersionZero(); void resettableProperty(); void duplicateExportWarnings(); void clonedSignal(); void baseVersionInQmltypes(); + void unconstructibleValueType(); + void constructibleValueType(); + void structuredValueType(); + void anonymousAndUncreatable(); + void omitInvisible(); + void typedEnum(); + void listSignal(); + void withNamespace(); + void sequenceRegistration(); + void valueTypeSelfReference(); + void foreignNamespaceFromGadget(); + + void nameExplosion_data(); + void nameExplosion(); + + void javaScriptExtension(); + + void consistencyWarnings(); + void relatedAddedInVersion(); + void longNumberTypes(); + void enumList(); + void constReturnType(); + + void usingDeclaration(); + void enumsRegistered(); + void doNotDuplicateQtNamespace(); + void slotsBeforeInvokables(); private: QByteArray qmltypesData; diff --git a/tests/auto/qml/qqmlanybinding/CMakeLists.txt b/tests/auto/qml/qqmlanybinding/CMakeLists.txt index 8cdecae3a8..f80acd8187 100644 --- a/tests/auto/qml/qqmlanybinding/CMakeLists.txt +++ b/tests/auto/qml/qqmlanybinding/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qqmlanybinding Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlanybinding LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp b/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp index 9e2ba24969..7e0a1c659a 100644 --- a/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp +++ b/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/QQmlEngine> #include <QtQml/QQmlComponent> #include <QtCore/QScopedPointer> @@ -39,7 +39,7 @@ static int getRefCount(const QQmlAnyBinding &binding) } else { // this temporarily adds a refcount because we construc a new untypedpropertybinding // thus -1 - return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->ref - 1; + return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->refCount() - 1; } } diff --git a/tests/auto/qml/qqmlanybinding/withbindable.h b/tests/auto/qml/qqmlanybinding/withbindable.h index 0672532344..322459f991 100644 --- a/tests/auto/qml/qqmlanybinding/withbindable.h +++ b/tests/auto/qml/qqmlanybinding/withbindable.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WITH_BINDABLE_H #define WITH_BINDABLE_H diff --git a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt index 0570b9a95c..3b02ed09ef 100644 --- a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt +++ b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlapplicationengine Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlapplicationengine LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -57,5 +63,12 @@ qt_internal_extend_target(tst_qqmlapplicationengine CONDITION NOT ANDROID AND NO DEFINES QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) + add_subdirectory(testapp) add_subdirectory(androidassets) +add_dependencies(tst_qqmlapplicationengine testapp) + +add_subdirectory(loadFromModuleTranslationsQmlType) +add_subdirectory(loadFromModuleTranslationsCppType) +add_dependencies(tst_qqmlapplicationengine i18nLoadFromModuleQmlType) +add_dependencies(tst_qqmlapplicationengine i18nLoadFromModuleCppType) diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt index c5d42ed9c4..eca18010ee 100644 --- a/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt +++ b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_androidassets LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_androidassets SOURCES tst_androidassets.cpp diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp index 7a6774c268..ed55afc693 100644 --- a/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp +++ b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlapplicationengine.h> #include <QtTest/qsignalspy.h> diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt new file mode 100644 index 0000000000..f6fa23e010 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_standard_project_setup(REQUIRES 6.5 I18N_LANGUAGES es) + +qt_internal_add_executable(i18nLoadFromModuleCppType + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + SOURCES + main.cpp + LIBRARIES + Qt::Gui + Qt::Qml +) + +qt_add_qml_module(i18nLoadFromModuleCppType + URI TranslatedCpp + QML_FILES Main.qml +) + +qt_internal_extend_target(i18nLoadFromModuleCppType + ENABLE_AUTOGEN_TOOLS + uic +) + +qt_add_resources(i18nLoadFromModuleCppType "loadFromModuleCppTypeQmFile" + PREFIX + /qt/qml/TranslatedCpp/ + FILES + i18n/qml_es.qm +) diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml new file mode 100644 index 0000000000..f1d2010837 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + Component.onCompleted: Qt.exit(0) +} diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qm b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qm Binary files differnew file mode 100644 index 0000000000..e35ee63f89 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qm diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts new file mode 100644 index 0000000000..f8d478f056 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="es_ES"> +<context> + <name>QObject</name> + <message> + <location filename="../main.cpp" line="17"/> + <source>Hello</source> + <translation>Hola</translation> + </message> +</context> +</TS> diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp new file mode 100644 index 0000000000..326a1397dc --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + QLocale::setDefault(QLocale(QLocale::Language(qEnvironmentVariableIntValue("qtlang")))); + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; + + QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, + &app, []() { QCoreApplication::exit(-1); }, + Qt::QueuedConnection); + engine.loadFromModule("TranslatedCpp", "Main"); + app.exec(); + + QString expected = qgetenv("LOADFROMMODULE_TEST_EXPECTED_OUTPUT"); + QString actual = QObject::tr("Hello"); + + if (actual == expected) + return 0; + + return actual[0].toLatin1(); +} diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt new file mode 100644 index 0000000000..088648040d --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_standard_project_setup(REQUIRES 6.5 I18N_LANGUAGES fr) + +qt_internal_add_executable(i18nLoadFromModuleQmlType + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + SOURCES + main.cpp + LIBRARIES + Qt::Gui + Qt::Qml +) + +qt_add_qml_module(i18nLoadFromModuleQmlType + URI TranslatedQml + QML_FILES Main.qml +) + +qt_internal_extend_target(i18nLoadFromModuleQmlType + ENABLE_AUTOGEN_TOOLS + uic +) + +qt_add_resources(i18nLoadFromModuleQmlType "loadFromModuleQmlTypeQmFile" + PREFIX + /qt/qml/TranslatedQml/ + FILES + i18n/qml_fr.qm +) diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml new file mode 100644 index 0000000000..9f8e1984e9 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml @@ -0,0 +1,13 @@ +import QtQml + +QtObject { + property string expected: "placeholder" + property string actual: qsTr("Hello") + + function f() { + if (expected === actual) + Qt.exit(0) + else + Qt.exit(actual.charCodeAt(0)) + } +} diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qm b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qm Binary files differnew file mode 100644 index 0000000000..a53cf121a2 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qm diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts new file mode 100644 index 0000000000..87b46be9ca --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="fr_FR"> +<context> + <name>Main</name> + <message> + <location filename="../Main.qml" line="5"/> + <source>Hello</source> + <translation>Salut</translation> + </message> +</context> +</TS> diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp new file mode 100644 index 0000000000..6f28ec8148 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp @@ -0,0 +1,23 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + QLocale::setDefault(QLocale(QLocale::Language(qEnvironmentVariableIntValue("qtlang")))); + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; + + QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, + &app, []() { QCoreApplication::exit(-1); }, + Qt::QueuedConnection); + engine.loadFromModule("TranslatedQml", "Main"); + + QString expected = qgetenv("LOADFROMMODULE_TEST_EXPECTED_OUTPUT"); + auto *root = engine.rootObjects().first(); + root->setProperty("expected", expected); + root->metaObject()->invokeMethod(root, "f"); + return app.exec(); +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp index 468c8428ba..6ccd256cca 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research In Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QQmlApplicationEngine> diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index 5774b68c32..3bf83d81e1 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research In Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QQmlApplicationEngine> #include <QScopedPointer> @@ -28,6 +28,8 @@ private slots: void removeObjectsWhenDestroyed(); void loadTranslation_data(); void loadTranslation(); + void loadFromModuleTranslation_data(); + void loadFromModuleTranslation(); void translationChange(); void setInitialProperties(); void failureToLoadTriggersWarningSignal(); @@ -144,7 +146,7 @@ void tst_qqmlapplicationengine::application() #if QT_CONFIG(process) QDir::setCurrent(buildDir); - QProcess *testProcess = new QProcess(this); + std::unique_ptr<QProcess> testProcess = std::make_unique<QProcess>(this); #ifdef Q_OS_QNX QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("QT_FORCE_STDERR_LOGGING", "1"); // QTBUG-76546 @@ -164,7 +166,7 @@ void tst_qqmlapplicationengine::application() QVERIFY2(QString(testStdErr).endsWith(QString(expectedStdErr)), QByteArray("\nExpected ending:\n") + expectedStdErr + QByteArray("\nActual output:\n") + testStdErr); - delete testProcess; + testProcess.reset(); QDir::setCurrent(srcDir); #else // process QSKIP("No process support"); @@ -191,7 +193,7 @@ void tst_qqmlapplicationengine::applicationProperties() QCoreApplication::setOrganizationName(firstOrganization); QCoreApplication::setOrganizationDomain(firstDomain); - QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("applicationTest.qml")); + std::unique_ptr<QQmlApplicationEngine> test = std::make_unique<QQmlApplicationEngine>(testFileUrl("applicationTest.qml")); QObject* root = test->rootObjects().at(0); QVERIFY(root); QCOMPARE(root->property("originalName").toString(), firstName); @@ -223,8 +225,6 @@ void tst_qqmlapplicationengine::applicationProperties() QCOMPARE(versionChanged.size(), 1); QCOMPARE(organizationChanged.size(), 1); QCOMPARE(domainChanged.size(), 1); - - delete test; } void tst_qqmlapplicationengine::removeObjectsWhenDestroyed() @@ -268,6 +268,53 @@ void tst_qqmlapplicationengine::loadTranslation() QCOMPARE(rootObject->property("translation").toString(), translation); } +void tst_qqmlapplicationengine::loadFromModuleTranslation_data() +{ + QTest::addColumn<QString>("executable"); + QTest::addColumn<QLocale::Language>("LANG"); + QTest::addColumn<QString>("output"); + + QString qmlTypeExecutable = "loadFromModuleTranslationsQmlType/i18nLoadFromModuleQmlType"; + QString cppTypeExecutable = "loadFromModuleTranslationsCppType/i18nLoadFromModuleCppType"; + + QTest::newRow("Qml: en -> en") << qmlTypeExecutable << QLocale::English << "Hello"; + QTest::newRow("Qml: en -> fr") << qmlTypeExecutable << QLocale::French << "Salut"; + QTest::newRow("Cpp: en -> en") << cppTypeExecutable << QLocale::English << "Hello"; + QTest::newRow("Cpp: en -> es") << cppTypeExecutable << QLocale::Spanish << "Hola"; +} + +void tst_qqmlapplicationengine::loadFromModuleTranslation() +{ +#if defined(Q_OS_ANDROID) + QSKIP("Test doesn't currently run on Android"); + return; +#endif + +#if QT_CONFIG(process) + QFETCH(QString, executable); + QFETCH(QLocale::Language, LANG); + QFETCH(QString, output); + + QDir::setCurrent(buildDir); + QProcess app; + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("qtlang", QString::number(int(LANG))); + env.insert("LOADFROMMODULE_TEST_EXPECTED_OUTPUT", output); + app.setProcessEnvironment(env); + app.start(executable); + QVERIFY(app.waitForStarted()); + QVERIFY(app.waitForFinished()); + + auto status = app.exitStatus(); + auto code = app.exitCode(); + QVERIFY2(code == 0, + QStringLiteral("status: %1, exitCode: %2").arg(status).arg(code).toStdString().c_str()); + app.kill(); +#else + QSKIP("No process support"); +#endif +} + void tst_qqmlapplicationengine::translationChange() { if (QLocale().language() == QLocale::SwissGerman) { diff --git a/tests/auto/qml/qqmlbinding/CMakeLists.txt b/tests/auto/qml/qqmlbinding/CMakeLists.txt index bcddd51730..0249bb0f25 100644 --- a/tests/auto/qml/qqmlbinding/CMakeLists.txt +++ b/tests/auto/qml/qqmlbinding/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlbinding Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlbinding LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -22,9 +28,27 @@ qt_internal_add_test(tst_qqmlbinding Qt::Gui Qt::GuiPrivate Qt::QmlPrivate + Qt::QmlMetaPrivate + Qt::QuickPrivate + Qt::QuickTestUtilsPrivate + TESTDATA ${test_data} +) + +qt_internal_add_test(tst_qqmlbinding_no_deferred_properties + SOURCES + tst_qqmlbinding.cpp + WithBindableProperties.h + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QmlMetaPrivate Qt::QuickPrivate Qt::QuickTestUtilsPrivate TESTDATA ${test_data} + DEFINES + QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES ) set_target_properties(tst_qqmlbinding PROPERTIES @@ -34,9 +58,13 @@ set_target_properties(tst_qqmlbinding PROPERTIES _qt_internal_qml_type_registration(tst_qqmlbinding) +set_target_properties(tst_qqmlbinding_no_deferred_properties PROPERTIES + QT_QML_MODULE_URI "test" + QT_QML_MODULE_VERSION 1.0 +) + +_qt_internal_qml_type_registration(tst_qqmlbinding_no_deferred_properties) -## Scopes: -##################################################################### qt_internal_extend_target(tst_qqmlbinding CONDITION ANDROID OR IOS DEFINES @@ -47,3 +75,13 @@ qt_internal_extend_target(tst_qqmlbinding CONDITION NOT ANDROID AND NOT IOS DEFINES QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) + +qt_internal_extend_target(tst_qqmlbinding_no_deferred_properties CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=":/data" +) + +qt_internal_extend_target(tst_qqmlbinding_no_deferred_properties CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) diff --git a/tests/auto/qml/qqmlbinding/WithBindableProperties.h b/tests/auto/qml/qqmlbinding/WithBindableProperties.h index 17fa3cd9b3..8837098d81 100644 --- a/tests/auto/qml/qqmlbinding/WithBindableProperties.h +++ b/tests/auto/qml/qqmlbinding/WithBindableProperties.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WithBindableProperties_H #define WithBindableProperties_H diff --git a/tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml b/tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml new file mode 100644 index 0000000000..8d15185506 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml @@ -0,0 +1,21 @@ +pragma ComponentBehavior: Bound +import QtQuick + +ListView { + id: list + property int i: 0 + + model: 1 + delegate: Item { + id: cellRootID + required property int index + Timer { + interval: 1 + running: true + onTriggered: { + cellRootID.index = index + 123 + list.i = cellRootID.index + } + } + } +} diff --git a/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml b/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml new file mode 100644 index 0000000000..98b3aa6606 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml @@ -0,0 +1,18 @@ +import QtQml.Models +import QtQuick + +Instantiator { + id: inst + model: 1 + property int check: 0 + + delegate: Binding { + ListView.delayRemove: true + Component.onCompleted: inst.check += 1 + } + + Component.onCompleted: { + if (inst.objectAt(0)) + inst.check += 2 + } +} diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml index 8d89989613..40d4806b8c 100644 --- a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQml 2.14 diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml index 9fd5fc77d0..5742a849b9 100644 --- a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQml 2.14 diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml index ee183a0d10..e9d90b5e8b 100644 --- a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import QtQml 2.14 diff --git a/tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml b/tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml new file mode 100644 index 0000000000..251e71f771 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml @@ -0,0 +1,13 @@ +import QtQml + +QtObject { + id: root + property bool enabled: false + property var func: function() { return 1 } + property var arr: [1, 2] + property Binding b: Binding { + root.func: function() { return 2 }; + root.arr: [1, 2, 3] + when: root.enabled + } +} diff --git a/tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml b/tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml new file mode 100644 index 0000000000..6245270e14 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml @@ -0,0 +1,23 @@ +import QtQuick + +Item { + id: root + property bool toggle: true + property bool forceEnable: false + + Item { + id: item1 + property int i + } + + Item { + id: item2 + } + + Binding { + target: root.toggle ? item1 : item2 + when: root.forceEnable || (root.toggle ? item1 : item2).hasOwnProperty("i") + property: "i" + value: 42 + } +} diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index c78763cf5f..b13379a103 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -1,13 +1,17 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <qtest.h> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "WithBindableProperties.h" + +#include <private/qmlutils_p.h> +#include <private/qqmlbind_p.h> +#include <private/qqmlcomponentattached_p.h> +#include <private/qquickrectangle_p.h> + +#include <QtTest/qtest.h> + #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> -#include <QtQml/private/qqmlbind_p.h> -#include <QtQml/private/qqmlcomponentattached_p.h> -#include <QtQuick/private/qquickrectangle_p.h> -#include <QtQuickTestUtils/private/qmlutils_p.h> -#include "WithBindableProperties.h" class tst_qqmlbinding : public QQmlDataTest { @@ -38,6 +42,9 @@ private slots: void intOverflow(); void generalizedGroupedProperties(); void localSignalHandler(); + void whenEvaluatedEarlyEnough(); + void propertiesAttachedToBindingItself(); + void toggleEnableProperlyRemembersValues(); private: QQmlEngine engine; @@ -46,6 +53,9 @@ private: tst_qqmlbinding::tst_qqmlbinding() : QQmlDataTest(QT_QMLTEST_DATADIR) { +#ifdef QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES + qputenv("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "1"); +#endif } void tst_qqmlbinding::binding() @@ -365,10 +375,6 @@ void tst_qqmlbinding::disabledOnReadonlyProperty() void tst_qqmlbinding::delayed() { -#ifdef Q_OS_ANDROID - QSKIP("This test crashes on Android. QTBUG-103310"); -#endif - QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("delayed.qml")); QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())}; @@ -455,9 +461,15 @@ void tst_qqmlbinding::bindingOverwriting() QQmlComponent c(&engine, testFileUrl("bindingOverwriting.qml")); QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())}; QVERIFY(item); + QCOMPARE(messageHandler.messages().size(), 2); + + QQmlComponent c2(&engine, testFileUrl("bindingOverwriting2.qml")); + QScopedPointer<QObject> o(c2.create()); + QVERIFY(o); + QTRY_COMPARE(o->property("i").toInt(), 123); + QCOMPARE(messageHandler.messages().size(), 3); QLoggingCategory::setFilterRules(QString()); - QCOMPARE(messageHandler.messages().size(), 2); } void tst_qqmlbinding::bindToQmlComponent() @@ -593,6 +605,55 @@ void tst_qqmlbinding::localSignalHandler() QCOMPARE(o->property("output").toString(), QStringLiteral("abc")); } +void tst_qqmlbinding::whenEvaluatedEarlyEnough() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("whenEvaluatedEarlyEnough.qml")); + QTest::failOnWarning(QRegularExpression(".*")); + std::unique_ptr<QObject> root { c.create() }; + root->setProperty("toggle", false); // should not cause warnings + // until "when" is actually true + QTest::ignoreMessage(QtMsgType::QtWarningMsg, + QRegularExpression(".*QML Binding: Property 'i' does not exist on Item.*")); + root->setProperty("forceEnable", true); +} + +void tst_qqmlbinding::propertiesAttachedToBindingItself() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("propertiesAttachedToBindingItself.qml")); + QTest::failOnWarning(QRegularExpression(".*")); + std::unique_ptr<QObject> root { c.create() }; + QVERIFY2(root, qPrintable(c.errorString())); + // 0 => everything broken; 1 => normal attached properties broken; + // 2 => Component.onCompleted broken, 3 => everything works + QTRY_COMPARE(root->property("check").toInt(), 3); +} + +void tst_qqmlbinding::toggleEnableProperlyRemembersValues() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("toggleEnableProperlyRemembersValues.qml")); + std::unique_ptr<QObject> root { c.create() }; + QVERIFY2(root, qPrintable(c.errorString())); + for (int i = 0; i < 3; ++i) { + { + QJSManagedValue arr(root->property("arr"), &e); + QJSManagedValue func(root->property("func"), &e); + QCOMPARE(arr.property("length").toInt(), 2); + QCOMPARE(func.call().toInt(), 1); + } + root->setProperty("enabled", true); + { + QJSManagedValue arr(root->property("arr"), &e); + QJSManagedValue func(root->property("func"), &e); + QCOMPARE(arr.property("length").toInt(), 3); + QCOMPARE(func.call().toInt(), 2); + } + root->setProperty("enabled", false); + } +} + QTEST_MAIN(tst_qqmlbinding) #include "tst_qqmlbinding.moc" diff --git a/tests/auto/qml/qqmlchangeset/CMakeLists.txt b/tests/auto/qml/qqmlchangeset/CMakeLists.txt index c4038b0eaf..32a20d7edf 100644 --- a/tests/auto/qml/qqmlchangeset/CMakeLists.txt +++ b/tests/auto/qml/qqmlchangeset/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlchangeset Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlchangeset LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlchangeset SOURCES tst_qqmlchangeset.cpp diff --git a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp index 1aec5dcac3..8712d4953d 100644 --- a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp +++ b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtest.h> #include <qrandom.h> #include <private/qqmlchangeset_p.h> @@ -94,7 +95,7 @@ public: bool applyChanges(QVector<int> &list, const QVector<QVector<Signal> > &changes) { - foreach (const SignalList &sl, changes) { + for (const SignalList &sl : changes) { if (!applyChanges(list, sl)) return false; } @@ -104,7 +105,7 @@ public: bool applyChanges(QVector<int> &list, const QVector<Signal> &changes) { QHash<QQmlChangeSet::MoveKey, int> removedValues; - foreach (const Signal &signal, changes) { + for (const Signal &signal : changes) { if (signal.isInsert()) { if (signal.index < 0 || signal.index > list.size()) { qDebug() << "insert out of range" << signal.index << list.size(); @@ -1133,7 +1134,7 @@ void tst_qqmlchangeset::sequence() QQmlChangeSet set; - foreach (const Signal &signal, input) { + for (const Signal &signal : std::as_const(input)) { if (signal.isRemove()) set.remove(signal.index, signal.count); else if (signal.isInsert()) @@ -1145,11 +1146,11 @@ void tst_qqmlchangeset::sequence() } SignalList changes; - foreach (const QQmlChangeSet::Change &remove, set.removes()) + for (const QQmlChangeSet::Change &remove : set.removes()) changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQmlChangeSet::Change &insert, set.inserts()) + for (const QQmlChangeSet::Change &insert : set.inserts()) changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); - foreach (const QQmlChangeSet::Change &change, set.changes()) + for (const QQmlChangeSet::Change &change : set.changes()) changes << Change(change.index, change.count); VERIFY_EXPECTED_OUTPUT @@ -1267,9 +1268,9 @@ void tst_qqmlchangeset::apply() QQmlChangeSet set; QQmlChangeSet linearSet; - foreach (const SignalList &list, input) { + for (const SignalList &list : std::as_const(input)) { QQmlChangeSet intermediateSet; - foreach (const Signal &signal, list) { + for (const Signal &signal : list) { if (signal.isRemove()) { intermediateSet.remove(signal.index, signal.count); linearSet.remove(signal.index, signal.count); @@ -1285,15 +1286,15 @@ void tst_qqmlchangeset::apply() } SignalList changes; - foreach (const QQmlChangeSet::Change &remove, set.removes()) + for (const QQmlChangeSet::Change &remove : set.removes()) changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQmlChangeSet::Change &insert, set.inserts()) + for (const QQmlChangeSet::Change &insert : set.inserts()) changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); SignalList linearChanges; - foreach (const QQmlChangeSet::Change &remove, linearSet.removes()) + for (const QQmlChangeSet::Change &remove : linearSet.removes()) linearChanges << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQmlChangeSet::Change &insert, linearSet.inserts()) + for (const QQmlChangeSet::Change &insert : linearSet.inserts()) linearChanges << Insert(insert.index, insert.count, insert.moveId, insert.offset); // The output in the failing tests isn't incorrect, merely sub-optimal. @@ -1328,7 +1329,7 @@ void tst_qqmlchangeset::removeConsecutive() QFETCH(SignalList, output); QVector<QQmlChangeSet::Change> removes; - foreach (const Signal &signal, input) { + for (const Signal &signal : std::as_const(input)) { QVERIFY(signal.isRemove()); removes.append(QQmlChangeSet::Change(signal.index, signal.count, signal.moveId, signal.offset)); } @@ -1337,7 +1338,7 @@ void tst_qqmlchangeset::removeConsecutive() set.remove(removes); SignalList changes; - foreach (const QQmlChangeSet::Change &remove, set.removes()) + for (const QQmlChangeSet::Change &remove : set.removes()) changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); QVERIFY(set.inserts().isEmpty()); QVERIFY(set.changes().isEmpty()); @@ -1368,7 +1369,7 @@ void tst_qqmlchangeset::insertConsecutive() QFETCH(SignalList, output); QVector<QQmlChangeSet::Change> inserts; - foreach (const Signal &signal, input) { + for (const Signal &signal : std::as_const(input)) { QVERIFY(signal.isInsert()); inserts.append(QQmlChangeSet::Change(signal.index, signal.count, signal.moveId, signal.offset)); } @@ -1377,7 +1378,7 @@ void tst_qqmlchangeset::insertConsecutive() set.insert(inserts); SignalList changes; - foreach (const QQmlChangeSet::Change &insert, set.inserts()) + for (const QQmlChangeSet::Change &insert : set.inserts()) changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); QVERIFY(set.removes().isEmpty()); QVERIFY(set.changes().isEmpty()); @@ -1496,9 +1497,9 @@ void tst_qqmlchangeset::random() } SignalList output; - foreach (const QQmlChangeSet::Change &remove, accumulatedSet.removes()) + for (const QQmlChangeSet::Change &remove : accumulatedSet.removes()) output << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQmlChangeSet::Change &insert, accumulatedSet.inserts()) + for (const QQmlChangeSet::Change &insert : accumulatedSet.inserts()) output << Insert(insert.index, insert.count, insert.moveId, insert.offset); QVector<int> inputList; diff --git a/tests/auto/qml/qqmlcomponent/CMakeLists.txt b/tests/auto/qml/qqmlcomponent/CMakeLists.txt index cab87ac08d..874c7f0cc3 100644 --- a/tests/auto/qml/qqmlcomponent/CMakeLists.txt +++ b/tests/auto/qml/qqmlcomponent/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlcomponent Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlcomponent LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -36,10 +42,13 @@ if(TARGET Qt::QuickControls2) target_compile_definitions(tst_qqmlcomponent PRIVATE HAS_CONTROLS) endif() +qt_policy(SET QTP0001 NEW) + qt_add_qml_module( tst_qqmlcomponent + SOURCES + lifecyclewatcher.h URI test - AUTO_RESOURCE_PREFIX QML_FILES "data/TestComponentWithIC.qml" "data/withAot.qml" diff --git a/tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml b/tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml new file mode 100644 index 0000000000..71676a4415 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml @@ -0,0 +1,28 @@ +import QtQml 2.15 + +QtObject { + id: root + Component.onCompleted: { + function WithPrototype(refMsgSeqNr) { + this.init(refMsgSeqNr) + }; + + WithPrototype.prototype = { + init: function(refMsgSeqNr) { + this.testObj = { + has: function(a) { return a === refMsgSeqNr } + } + + this.protocolSubTypeID = 2 + this.messageControl = 0 + this.referredMsgSequenceNumber = refMsgSeqNr + } + }; + + let comp = Qt.createComponent("dynamic.qml"); + let inst1 = comp.createObject(root, { testObj: new Set(), }); + let inst2 = comp.createObject(root, new WithPrototype(1)); + + objectName = inst1.use() + " - " + inst2.use(); + } +} diff --git a/tests/auto/qml/qqmlcomponent/data/createObject.qml b/tests/auto/qml/qqmlcomponent/data/createObject.qml index afd9e71229..c9ca605f8f 100644 --- a/tests/auto/qml/qqmlcomponent/data/createObject.qml +++ b/tests/auto/qml/qqmlcomponent/data/createObject.qml @@ -1,5 +1,4 @@ -import QtQuick 2.0 -import QtQuick.Window 2.0 +import QtQuick Item { property QtObject qtobjectParent: QtObject { } diff --git a/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml b/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml index 3391b3a266..63aeb9415e 100644 --- a/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml +++ b/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml @@ -11,6 +11,9 @@ Item{ property QtObject badRequired: null property QtObject goodRequired: null + property QtObject bindingAsInitial: null + property bool bindingUsed: false + Component{ id: a Rectangle { @@ -21,7 +24,7 @@ Item{ id: b Item{ property bool testBool: false - property int testInt: null + property int testInt: { return null; } property QtObject testObject: null } } @@ -43,6 +46,11 @@ Item{ } } + Component { + id: e + Rectangle {} + } + Component.onCompleted: { root.declarativerectangle = a.createObject(root, {"x":17,"y":17, "color":"white", "border.width":3, "innerRect.border.width": 20}); root.declarativeitem = b.createObject(root, {"x":17,"y":17,"testBool":true,"testInt":17,"testObject":root}); @@ -52,5 +60,9 @@ Item{ root.badRequired = d.createObject(root, { "not_i": 42 }); root.goodRequired = d.createObject(root, { "i": 42 }); + + root.bindingAsInitial = e.createObject(root, {color: Qt.binding(() => { + root.bindingUsed = true; return '#ff0000' + })}); } } diff --git a/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml b/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml index 282ab509f0..480835a5b1 100644 --- a/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml +++ b/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml @@ -1,5 +1,4 @@ -import QtQuick 2.0 -import QtQuick.Window 2.0 +import QtQuick Item { property QtObject qtobjectParent: QtObject { } @@ -19,14 +18,14 @@ Item { property QtObject window_window : null Component.onCompleted: { - qtobject_qtobject = Qt.createQmlObject("import QtQuick 2.0; QtObject{}", qtobjectParent); - qtobject_item = Qt.createQmlObject("import QtQuick 2.0; Item{}", qtobjectParent); - qtobject_window = Qt.createQmlObject("import QtQuick.Window 2.0; Window{}", qtobjectParent); - item_qtobject = Qt.createQmlObject("import QtQuick 2.0; QtObject{}", itemParent); - item_item = Qt.createQmlObject("import QtQuick 2.0; Item{}", itemParent); - item_window = Qt.createQmlObject("import QtQuick.Window 2.0; Window{}", itemParent); - window_qtobject = Qt.createQmlObject("import QtQuick 2.0; QtObject{}", windowParent); - window_item = Qt.createQmlObject("import QtQuick 2.0; Item{}", windowParent); - window_window = Qt.createQmlObject("import QtQuick.Window 2.0; Window{}", windowParent); + qtobject_qtobject = Qt.createQmlObject("import QtQuick; QtObject{}", qtobjectParent); + qtobject_item = Qt.createQmlObject("import QtQuick; Item{}", qtobjectParent); + qtobject_window = Qt.createQmlObject("import QtQuick; Window{}", qtobjectParent); + item_qtobject = Qt.createQmlObject("import QtQuick; QtObject{}", itemParent); + item_item = Qt.createQmlObject("import QtQuick; Item{}", itemParent); + item_window = Qt.createQmlObject("import QtQuick; Window{}", itemParent); + window_qtobject = Qt.createQmlObject("import QtQuick; QtObject{}", windowParent); + window_item = Qt.createQmlObject("import QtQuick; Item{}", windowParent); + window_window = Qt.createQmlObject("import QtQuick; Window{}", windowParent); } } diff --git a/tests/auto/qml/qqmlcomponent/data/dynamic.qml b/tests/auto/qml/qqmlcomponent/data/dynamic.qml new file mode 100644 index 0000000000..b9a54d53ff --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/dynamic.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + property var testObj + function use() { return testObj.has(1) ? 25 : 26; } +} diff --git a/tests/auto/qml/qqmlcomponent/data/removeBinding.qml b/tests/auto/qml/qqmlcomponent/data/removeBinding.qml new file mode 100644 index 0000000000..091f6991be --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/removeBinding.qml @@ -0,0 +1,32 @@ +import QtQml 2.15 + +QtObject { + id: root + objectName: "400" + + property Component c: Component { + id: customItem + QtObject { + objectName: root.objectName + } + } + + property string result: { + const properties = { + "objectName": "42", + } + const item = customItem.createObject(root, properties) + return item.objectName; + } + + property string result2: { + const properties = { + "objectName": "43", + } + + // add some junk argument to trigger the QQmlV4Function overload + const item = customItem.createObject(root, properties, 13) + + return item.objectName; + } +} diff --git a/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h b/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h new file mode 100644 index 0000000000..e974681d25 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h @@ -0,0 +1,24 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LIFECYCLEWATCHER_H +#define LIFECYCLEWATCHER_H + +#include <QtQml/qqmlparserstatus.h> +#include <private/qqmlfinalizer_p.h> +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class LifeCycleWatcher : public QObject, public QQmlParserStatus, public QQmlFinalizerHook +{ + Q_OBJECT + QML_ELEMENT + Q_INTERFACES(QQmlParserStatus) + Q_INTERFACES(QQmlFinalizerHook) +public: + void classBegin() override {states.push_back(1); } + void componentComplete() override {states.push_back(2);}; + void componentFinalized() override { states.push_back(3); } + QList<int> states; +}; +#endif diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 5203ba9615..ea06a11006 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> @@ -21,7 +21,7 @@ #include <private/qv4executablecompilationunit_p.h> #include <qcolor.h> #include <qsignalspy.h> - +#include "lifecyclewatcher.h" #include <algorithm> using namespace Qt::StringLiterals; @@ -90,13 +90,6 @@ public slots: } }; -static void gc(QQmlEngine &engine) -{ - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); -} - class tst_qqmlcomponent : public QQmlDataTest { Q_OBJECT @@ -140,11 +133,16 @@ private slots: void boundComponent(); void loadFromModule_data(); void loadFromModule(); + void loadFromModuleLifecycle(); void loadFromModuleThenCreateWithIncubator(); void loadFromModuleFailures_data(); void loadFromModuleFailures(); void loadFromModuleRequired(); void loadFromQrc(); + void removeBinding(); + void complexObjectArgument(); + void bindingEvaluationOrder(); + void compilationUnitsWithSameUrl(); private: QQmlEngine engine; @@ -182,14 +180,12 @@ void tst_qqmlcomponent::loadEmptyUrl() void tst_qqmlcomponent::qmlIncubateObject() { QQmlComponent component(&engine, testFileUrl("incubateObject.qml")); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), false); QTRY_VERIFY(object->property("test2").toBool()); - - delete object; } void tst_qqmlcomponent::qmlCreateWindow() @@ -282,7 +278,7 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties() QTest::ignoreMessage( QtMsgType::QtWarningMsg, QRegularExpression( - ".*createObjectWithScript.qml:42:13: Required property i was not initialized")); + ".*createObjectWithScript.qml:45:13: Required property i was not initialized")); QQmlComponent component(&engine, testFileUrl("createObjectWithScript.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); @@ -342,6 +338,12 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties() QCOMPARE(goodRequired->parent(), object.data()); QCOMPARE(goodRequired->property("i").value<int>(), 42); } + + { + QScopedPointer<QObject> bindingAsInitial(object->property("bindingAsInitial").value<QObject *>()); + QVERIFY(bindingAsInitial); + QVERIFY(object->property("bindingUsed").toBool()); + } } void tst_qqmlcomponent::qmlCreateObjectClean() @@ -390,11 +392,11 @@ void tst_qqmlcomponent::qmlCreateParentReference() QQmlComponent component(&engine, testFileUrl("createParentReference.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); - QVERIFY(QMetaObject::invokeMethod(object, "createChild")); - delete object; + QVERIFY(QMetaObject::invokeMethod(object.get(), "createChild")); + object.reset(); engine.setOutputWarningsToStandardError(false); QCOMPARE(engine.outputWarningsToStandardError(), false); @@ -416,10 +418,8 @@ void tst_qqmlcomponent::async() QCOMPARE(watcher.ready, 1); QCOMPARE(watcher.error, 0); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); - - delete object; } void tst_qqmlcomponent::asyncHierarchy() @@ -437,7 +437,7 @@ void tst_qqmlcomponent::asyncHierarchy() QCOMPARE(watcher.ready, 1); QCOMPARE(watcher.error, 0); - QObject *root = component.create(); + std::unique_ptr<QObject> root { component.create() }; QVERIFY(root != nullptr); // ensure that the parent-child relationship hierarchy is correct @@ -461,8 +461,6 @@ void tst_qqmlcomponent::asyncHierarchy() // ensure that values and bindings are assigned correctly QVERIFY(root->property("success").toBool()); - - delete root; } void tst_qqmlcomponent::asyncForceSync() @@ -1245,12 +1243,14 @@ void tst_qqmlcomponent::boundComponent() { QQmlComponent component(&engine, testFileUrl("nestedBoundComponent.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QVERIFY(component.isBound()); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); QQmlComponent *nestedComponent = o->property("c").value<QQmlComponent *>(); QVERIFY(nestedComponent != nullptr); + QVERIFY(nestedComponent->isBound()); QObject *nestedObject = o->property("o").value<QObject *>(); QVERIFY(nestedObject != nullptr); @@ -1269,6 +1269,7 @@ void tst_qqmlcomponent::boundComponent() { QQmlComponent component(&engine, testFileUrl("BoundInlineComponent.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QVERIFY(component.isBound()); QScopedPointer<QObject> o(component.create()); QVERIFY2(!o.isNull(), qPrintable(component.errorString())); @@ -1282,11 +1283,22 @@ void tst_qqmlcomponent::boundComponent() { QQmlComponent component(&engine, testFileUrl("boundInlineComponentUser.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QVERIFY(!component.isBound()); QScopedPointer<QObject> o(component.create()); QVERIFY(o.isNull()); QVERIFY(component.errorString().contains( QLatin1String("Cannot instantiate bound inline component in different file"))); + + } + + { + QQmlComponent component(&engine); + QVERIFY(!component.isBound()); + + component.setData("pragma ComponentBehavior: Bound\nsyntax error", QUrl()); + QCOMPARE(component.errorString(), ":2 Syntax error\n"_L1); + QVERIFY(!component.isBound()); } } @@ -1334,6 +1346,34 @@ void tst_qqmlcomponent::loadFromModule() name); } +void tst_qqmlcomponent::loadFromModuleLifecycle() +{ + QQmlEngine engine; + QList<int> loadFromModuleOrder; + QList<int> plainLoadOrder; + const QList<int> expected {1, 2, 3}; + { + QQmlComponent component(&engine); + component.loadFromModule("test", "LifeCycleWatcher"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> root{ component.create() }; + LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get()); + QVERIFY(watcher); + loadFromModuleOrder = watcher->states; + QCOMPARE(loadFromModuleOrder, expected); + } + { + QQmlComponent component(&engine); + component.setData("import test; LifeCycleWatcher {}", {}); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> root{ component.create() }; + LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get()); + QVERIFY(watcher); + plainLoadOrder = watcher->states; + } + QCOMPARE(loadFromModuleOrder, plainLoadOrder); +} + struct CallVerifyingIncubtor : QQmlIncubator { void setInitialState(QObject *) override { setInitialStateCalled = true; } @@ -1373,6 +1413,10 @@ void tst_qqmlcomponent::loadFromModuleFailures_data() QTest::addRow("CppSingleton") << u"QtQuick"_s << u"Application"_s << u"Application is a singleton, and cannot be loaded"_s; + QTest::addRow("passedFileName") << "plainqml" + << "Plain.qml" + << R"(Type "Plain" from module "plainqml" contains no inline component named "qml". )" + R"(To load the type "Plain", drop the ".qml" extension.)"; } void tst_qqmlcomponent::loadFromModuleFailures() @@ -1384,7 +1428,11 @@ void tst_qqmlcomponent::loadFromModuleFailures() QQmlEngine engine; QQmlComponent component(&engine); QSignalSpy errorSpy(&component, &QQmlComponent::statusChanged); + QSignalSpy progressSpy(&component, &QQmlComponent::progressChanged); component.loadFromModule(uri, typeName); + // verify that we changed the progress correctly to 1 + QTRY_VERIFY(!progressSpy.isEmpty()); + QTRY_COMPARE(progressSpy.last().at(0).toDouble(), 1.0); QVERIFY(!errorSpy.isEmpty()); QCOMPARE(errorSpy.first().first().value<QQmlComponent::Status>(), QQmlComponent::Error); @@ -1422,7 +1470,99 @@ void tst_qqmlcomponent::loadFromQrc() QQmlComponentPrivate *p = QQmlComponentPrivate::get(&component); QVERIFY(p); QVERIFY(p->compilationUnit); - QVERIFY(p->compilationUnit->aotCompiledFunctions); + QVERIFY(p->compilationUnit->baseCompilationUnit()->aotCompiledFunctions); +} + +void tst_qqmlcomponent::removeBinding() +{ + QQmlEngine e; + const QUrl url = testFileUrl("removeBinding.qml"); + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + QStringLiteral(":7:27: QML Component: Unsuitable arguments " + "passed to createObject(). The first argument " + "should be a QObject* or null, and the second " + "argument should be a JavaScript object or a " + "QVariantMap"))); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("result"), QStringLiteral("42")); + QCOMPARE(o->property("result2"), QStringLiteral("43")); +} + +void tst_qqmlcomponent::complexObjectArgument() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("complexObjectArgument.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), QStringLiteral("26 - 25")); +} + +void tst_qqmlcomponent::bindingEvaluationOrder() +{ + // Note: This test explicitly tests the order in which bindings are + // evaluated, which is generally unspecified. This, however, exists + // as a regression test for QQmlObjectCreator code that is supposed + // to *not* mess with the QmlIR given to it. + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import QtQml + QtObject { + property var myList: ["dummy"] + property int p1: { myList.push("p1"); return 0; } + property int p2: { myList.push("p2"); return 0; } + })", QUrl()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + const QList<QVariant> myList = o->property("myList").toList(); + QCOMPARE(myList.size(), 3); + QCOMPARE(myList[0].toString(), u"dummy"_s); + QCOMPARE(myList[1].toString(), u"p1"_s); + QCOMPARE(myList[2].toString(), u"p2"_s); +} + +void tst_qqmlcomponent::compilationUnitsWithSameUrl() +{ + QQmlEngine engine; + engine.setUiLanguage("de_CH"); + + std::vector<std::unique_ptr<QObject>> objects; + for (int i = 0; i < 10; ++i) { + QQmlComponent component(&engine); + component.setData(R"( + import QtQml + QtObject { + function returnThing() : string { return Qt.uiLanguage } + } + )", QUrl("duplicate.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + + std::unique_ptr<QObject> o(component.create()); + QVERIFY(o.get()); + + QString result; + QMetaObject::invokeMethod(o.get(), "returnThing", Q_RETURN_ARG(QString, result)); + QCOMPARE(result, "de_CH"); + + objects.push_back(std::move(o)); + } + + gc(engine); + + for (const auto &o: objects) { + QString result; + QMetaObject::invokeMethod(o.get(), "returnThing", Q_RETURN_ARG(QString, result)); + QCOMPARE(result, "de_CH"); + } } QTEST_MAIN(tst_qqmlcomponent) diff --git a/tests/auto/qml/qqmlconnections/CMakeLists.txt b/tests/auto/qml/qqmlconnections/CMakeLists.txt index d75c86f417..0e6947370d 100644 --- a/tests/auto/qml/qqmlconnections/CMakeLists.txt +++ b/tests/auto/qml/qqmlconnections/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlconnections Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlconnections LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -21,6 +27,7 @@ qt_internal_add_test(tst_qqmlconnections Qt::Gui Qt::GuiPrivate Qt::QmlPrivate + Qt::QmlMetaPrivate Qt::QuickPrivate Qt::QuickTestUtilsPrivate TESTDATA ${test_data} diff --git a/tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml b/tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml new file mode 100644 index 0000000000..921787aa36 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml @@ -0,0 +1,15 @@ +import QtQml + +QtObject { + id: root + signal _foo + + property int handled: 0 + + property Connections c: Connections { + target: root + function on_Foo() { root.handled += 1 } + function on_foo() { root.handled += 2 } + } +} + diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp index f23c474907..88441e9dac 100644 --- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp +++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -55,6 +55,7 @@ private slots: void bindToPropertyWithUnderscoreChangeHandler(); void invalidTarget(); + void badSignalHandlerName(); private: QQmlEngine engine; void prefixes(); @@ -76,26 +77,22 @@ void tst_qqmlconnections::defaultValues() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("test-connection3.qml")); - QQmlConnections *item = qobject_cast<QQmlConnections*>(c.create()); + std::unique_ptr<QQmlConnections> item { qobject_cast<QQmlConnections*>(c.create()) }; QVERIFY(item != nullptr); QVERIFY(!item->target()); - - delete item; } void tst_qqmlconnections::properties() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("test-connection2.qml")); - QQmlConnections *item = qobject_cast<QQmlConnections*>(c.create()); + std::unique_ptr<QQmlConnections> item { qobject_cast<QQmlConnections*>(c.create()) }; QVERIFY(item != nullptr); QVERIFY(item != nullptr); - QCOMPARE(item->target(), item); - - delete item; + QCOMPARE(item->target(), item.get()); } void tst_qqmlconnections::connection() @@ -103,7 +100,7 @@ void tst_qqmlconnections::connection() QFETCH(QString, prefix); QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) }; QVERIFY(item != nullptr); @@ -112,8 +109,6 @@ void tst_qqmlconnections::connection() emit item->setWidth(100.); QCOMPARE(item->width(), 100.); QCOMPARE(item->property("tested").toBool(), true); - - delete item; } void tst_qqmlconnections::trimming() @@ -121,20 +116,18 @@ void tst_qqmlconnections::trimming() QFETCH(QString, prefix); QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/trimming.qml")); - QObject *object = c.create(); + std::unique_ptr<QObject> object { c.create() }; QVERIFY(object != nullptr); QCOMPARE(object->property("tested").toString(), QString("")); int index = object->metaObject()->indexOfSignal("testMe(int,QString)"); QMetaMethod method = object->metaObject()->method(index); - method.invoke(object, + method.invoke(object.get(), Qt::DirectConnection, Q_ARG(int, 5), Q_ARG(QString, "worked")); QCOMPARE(object->property("tested").toString(), QString("worked5")); - - delete object; } // Confirm that target can be changed by one of our signal handlers @@ -143,7 +136,7 @@ void tst_qqmlconnections::targetChanged() QFETCH(QString, prefix); QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/connection-targetchange.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) }; QVERIFY(item != nullptr); QQmlConnections *connections = item->findChild<QQmlConnections*>("connections"); @@ -159,8 +152,6 @@ void tst_qqmlconnections::targetChanged() QCOMPARE(connections->target(), item2); // If we don't crash then we're OK - - delete item; } void tst_qqmlconnections::unknownSignals_data() @@ -193,7 +184,7 @@ void tst_qqmlconnections::unknownSignals() QQmlEngine engine; QQmlComponent c(&engine, url); - QObject *object = c.create(); + std::unique_ptr<QObject> object { c.create() }; QVERIFY(object != nullptr); // check that connection is created (they are all runtime errors) @@ -202,8 +193,6 @@ void tst_qqmlconnections::unknownSignals() if (file == "connection-unknownsignals-ignored.qml") QVERIFY(connections->ignoreUnknownSignals()); - - delete object; } void tst_qqmlconnections::errors_data() @@ -260,25 +249,21 @@ void tst_qqmlconnections::rewriteErrors() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-unnamed.qml")); QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal uses unnamed parameter followed by named parameter.").toLatin1()); - TestObject *obj = qobject_cast<TestObject*>(c.create()); + std::unique_ptr<TestObject> obj { qobject_cast<TestObject*>(c.create()) }; QVERIFY(obj != nullptr); obj->unnamedArgumentSignal(1, .5, "hello"); QCOMPARE(obj->ran(), false); - - delete obj; } { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-global.qml")); QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal parameter \"parseInt\" hides global variable.").toLatin1()); - TestObject *obj = qobject_cast<TestObject*>(c.create()); + std::unique_ptr<TestObject> obj { qobject_cast<TestObject*>(c.create()) }; QVERIFY(obj != nullptr); obj->signalWithGlobalName(10); QCOMPARE(obj->ran(), false); - - delete obj; } } @@ -324,26 +309,24 @@ void tst_qqmlconnections::singletonTypeTarget() QFETCH(QString, prefix); qmlRegisterSingletonType<MyTestSingletonType>("MyTestSingletonType", 1, 0, "Api", module_api_factory); QQmlComponent component(&engine, testFileUrl(prefix + "/singletontype-target.qml")); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 0); QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); - QMetaObject::invokeMethod(object, "setModuleIntProp"); + QMetaObject::invokeMethod(object.get(), "setModuleIntProp"); QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 1); QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); - QMetaObject::invokeMethod(object, "setModuleIntProp"); + QMetaObject::invokeMethod(object.get(), "setModuleIntProp"); QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 2); QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); // the singleton Type emits otherSignal every 3 times the int property changes. - QMetaObject::invokeMethod(object, "setModuleIntProp"); + QMetaObject::invokeMethod(object.get(), "setModuleIntProp"); QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 3); QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 1); - - delete object; } void tst_qqmlconnections::enableDisable_QTBUG_36350() @@ -351,7 +334,7 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350() QFETCH(QString, prefix); QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) }; QVERIFY(item != nullptr); QQmlConnections *connections = item->findChild<QQmlConnections*>("connections"); @@ -370,8 +353,6 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350() emit item->setWidth(50.); QCOMPARE(item->width(), 50.); QCOMPARE(item->property("tested").toBool(), true); //Should have received signal to change property - - delete item; } void tst_qqmlconnections::disabledAtStart() @@ -379,17 +360,15 @@ void tst_qqmlconnections::disabledAtStart() QFETCH(QString, prefix); QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/disabled-at-start.qml")); - QObject * const object = c.create(); + std::unique_ptr<QObject> object { c.create() }; QVERIFY(object != nullptr); QCOMPARE(object->property("tested").toBool(), false); const int index = object->metaObject()->indexOfSignal("testMe()"); const QMetaMethod method = object->metaObject()->method(index); - method.invoke(object, Qt::DirectConnection); + method.invoke(object.get(), Qt::DirectConnection); QCOMPARE(object->property("tested").toBool(), false); - - delete object; } //QTBUG-56499 @@ -398,7 +377,7 @@ void tst_qqmlconnections::clearImplicitTarget() QFETCH(QString, prefix); QQmlEngine engine; QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection-implicit.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) }; QVERIFY(item != nullptr); @@ -415,8 +394,6 @@ void tst_qqmlconnections::clearImplicitTarget() // target cleared: no longer fire Connections item->setWidth(150.); QCOMPARE(item->property("tested").toBool(), false); - - delete item; } void tst_qqmlconnections::onWithoutASignal() @@ -485,6 +462,26 @@ void tst_qqmlconnections::invalidTarget() QTRY_VERIFY(root->objectName().isEmpty()); } +void tst_qqmlconnections::badSignalHandlerName() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("badSignalHandlerName.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + "\"on_foo\" is not a properly capitalized signal handler name. " + "\"on_Foo\" would be correct."); + + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + + QCOMPARE(root->property("handled").toInt(), 0); + QMetaObject::invokeMethod(root.data(), "_foo"); + QCOMPARE(root->property("handled").toInt(), 3); +} + + QTEST_MAIN(tst_qqmlconnections) #include "tst_qqmlconnections.moc" diff --git a/tests/auto/qml/qqmlconsole/CMakeLists.txt b/tests/auto/qml/qqmlconsole/CMakeLists.txt index a4eb33f1df..121b0863d0 100644 --- a/tests/auto/qml/qqmlconsole/CMakeLists.txt +++ b/tests/auto/qml/qqmlconsole/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlconsole Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlconsole LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlconsole/data/assert.qml b/tests/auto/qml/qqmlconsole/data/assert.qml index 38a103e233..04ea18a3e5 100644 --- a/tests/auto/qml/qqmlconsole/data/assert.qml +++ b/tests/auto/qml/qqmlconsole/data/assert.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml index 9593abbaa9..dd03c0c773 100644 --- a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml +++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 Pelagicore AG -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/qml/qqmlconsole/data/exception.qml b/tests/auto/qml/qqmlconsole/data/exception.qml index 769b196c10..d3d022ca22 100644 --- a/tests/auto/qml/qqmlconsole/data/exception.qml +++ b/tests/auto/qml/qqmlconsole/data/exception.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlconsole/data/logging.qml b/tests/auto/qml/qqmlconsole/data/logging.qml index af1c97ee01..309534615e 100644 --- a/tests/auto/qml/qqmlconsole/data/logging.qml +++ b/tests/auto/qml/qqmlconsole/data/logging.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlconsole/data/profiling.qml b/tests/auto/qml/qqmlconsole/data/profiling.qml index fc655ab26c..528726b268 100644 --- a/tests/auto/qml/qqmlconsole/data/profiling.qml +++ b/tests/auto/qml/qqmlconsole/data/profiling.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlconsole/data/tracing.qml b/tests/auto/qml/qqmlconsole/data/tracing.qml index 750e4f5688..52be9d84de 100644 --- a/tests/auto/qml/qqmlconsole/data/tracing.qml +++ b/tests/auto/qml/qqmlconsole/data/tracing.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp index 4bffdb0dd7..40d849969c 100644 --- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp +++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> #include <QQmlEngine> @@ -95,7 +95,7 @@ void tst_qqmlconsole::categorized_logging() QVERIFY(testCategory.isCriticalEnabled()); QQmlComponent component(&engine, testUrl); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY2(object != nullptr, component.errorString().toUtf8()); QVERIFY(messageHandler.messages().contains("qt.test: console.info")); @@ -131,8 +131,6 @@ void tst_qqmlconsole::categorized_logging() QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(42) + "Error: A QmlLoggingCatgory was provided without a valid name"; QVERIFY(messageHandler.messages().contains(useEmptyCategory)); - - delete object; } void tst_qqmlconsole::tracing() @@ -147,9 +145,17 @@ void tst_qqmlconsole::tracing() QTest::ignoreMessage(QtDebugMsg, qPrintable(traceText)); QQmlComponent component(&engine, testUrl); - QObject *object = component.create(); - QVERIFY(object != nullptr); - delete object; + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); + + const QString traceText2 + = QLatin1String("qml: tracing (%1:%2)\nexpression for onCompleted (%1:%3)") + .arg(testUrl.toString()).arg(12).arg(16); + + QQmlTestMessageHandler messageHandler; + messageHandler.setIncludeCategoriesEnabled(true); + std::unique_ptr<QObject> object2 { component.create() }; + QCOMPARE(messageHandler.messageString(), traceText2); } void tst_qqmlconsole::profiling() @@ -161,9 +167,8 @@ void tst_qqmlconsole::profiling() QTest::ignoreMessage(QtWarningMsg, "Ignoring console.profileEnd(): the debug service is disabled."); QQmlComponent component(&engine, testUrl); - QObject *object = component.create(); - QVERIFY(object != nullptr); - delete object; + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); } void tst_qqmlconsole::testAssert() @@ -186,9 +191,8 @@ void tst_qqmlconsole::testAssert() QTest::ignoreMessage(QtCriticalMsg, qPrintable(assert2)); QQmlComponent component(&engine, testUrl); - QObject *object = component.create(); - QVERIFY(object != nullptr); - delete object; + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); } void tst_qqmlconsole::exception() @@ -211,9 +215,8 @@ void tst_qqmlconsole::exception() QTest::ignoreMessage(QtCriticalMsg, qPrintable(exception2)); QQmlComponent component(&engine, testUrl); - QObject *object = component.create(); - QVERIFY(object != nullptr); - delete object; + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); } QTEST_MAIN(tst_qqmlconsole) diff --git a/tests/auto/qml/qqmlcontext/CMakeLists.txt b/tests/auto/qml/qqmlcontext/CMakeLists.txt index c8d5ec6810..b50e3ff7fe 100644 --- a/tests/auto/qml/qqmlcontext/CMakeLists.txt +++ b/tests/auto/qml/qqmlcontext/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlcontext Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlcontext LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlcontext/data/A.qml b/tests/auto/qml/qqmlcontext/data/A.qml new file mode 100644 index 0000000000..1a44f005af --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/A.qml @@ -0,0 +1,6 @@ +import QtQml + +B { + id: b + property int y: 2 +} diff --git a/tests/auto/qml/qqmlcontext/data/B.qml b/tests/auto/qml/qqmlcontext/data/B.qml new file mode 100644 index 0000000000..7754728304 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/B.qml @@ -0,0 +1,6 @@ +import QtQml + +C { + id: z + property int z: 3 +} diff --git a/tests/auto/qml/qqmlcontext/data/C.qml b/tests/auto/qml/qqmlcontext/data/C.qml new file mode 100644 index 0000000000..6afd23aa6c --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/C.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + id: outer + objectName: "the" + "C" +} diff --git a/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml new file mode 100644 index 0000000000..7b1f46d7d1 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property A a: A {} +} diff --git a/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml new file mode 100644 index 0000000000..a478a587df --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml @@ -0,0 +1,5 @@ +import QtQml +QtObject { + property Component c: MyItem {} + property QtObject o: c.createObject() +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index 8398f8b9d7..8e726857cc 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> @@ -49,6 +49,10 @@ private slots: void outerContextObject(); void contextObjectHierarchy(); void destroyContextProperty(); + void destroyContextObject(); + + void numericContextProperty(); + void gcDeletesContextObject(); private: QQmlEngine engine; @@ -92,13 +96,13 @@ void tst_qqmlcontext::resolvedUrl() // Relative to a deleted parent { - QQmlContext *ctxt = new QQmlContext(&engine); + std::unique_ptr<QQmlContext> ctxt = std::make_unique<QQmlContext>(&engine); ctxt->setBaseUrl(QUrl("http://www.qt-project.org/")); - QQmlContext ctxt2(ctxt); + QQmlContext ctxt2(ctxt.get()); QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.qt-project.org/main2.qml")); - delete ctxt; ctxt = nullptr; + ctxt.reset(); QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl()); } @@ -114,58 +118,58 @@ void tst_qqmlcontext::resolvedUrl() void tst_qqmlcontext::engineMethod() { - QQmlEngine *engine = new QQmlEngine; + std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>(); - QQmlContext ctxt(engine); + QQmlContext ctxt(engine.get()); QQmlContext ctxt2(&ctxt); QQmlContext ctxt3(&ctxt2); QQmlContext ctxt4(&ctxt2); - QCOMPARE(ctxt.engine(), engine); - QCOMPARE(ctxt2.engine(), engine); - QCOMPARE(ctxt3.engine(), engine); - QCOMPARE(ctxt4.engine(), engine); + QCOMPARE(ctxt.engine(), engine.get()); + QCOMPARE(ctxt2.engine(), engine.get()); + QCOMPARE(ctxt3.engine(), engine.get()); + QCOMPARE(ctxt4.engine(), engine.get()); - delete engine; engine = nullptr; + engine.reset(); - QCOMPARE(ctxt.engine(), engine); - QCOMPARE(ctxt2.engine(), engine); - QCOMPARE(ctxt3.engine(), engine); - QCOMPARE(ctxt4.engine(), engine); + QCOMPARE(ctxt.engine(), engine.get()); + QCOMPARE(ctxt2.engine(), engine.get()); + QCOMPARE(ctxt3.engine(), engine.get()); + QCOMPARE(ctxt4.engine(), engine.get()); } void tst_qqmlcontext::parentContext() { - QQmlEngine *engine = new QQmlEngine; + std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>(); QCOMPARE(engine->rootContext()->parentContext(), (QQmlContext *)nullptr); - QQmlContext *ctxt = new QQmlContext(engine); - QQmlContext *ctxt2 = new QQmlContext(ctxt); - QQmlContext *ctxt3 = new QQmlContext(ctxt2); - QQmlContext *ctxt4 = new QQmlContext(ctxt2); - QQmlContext *ctxt5 = new QQmlContext(ctxt); - QQmlContext *ctxt6 = new QQmlContext(engine); - QQmlContext *ctxt7 = new QQmlContext(engine->rootContext()); + std::unique_ptr<QQmlContext> ctxt = std::make_unique<QQmlContext>(engine.get()); + std::unique_ptr<QQmlContext> ctxt2 = std::make_unique<QQmlContext>(ctxt.get()); + std::unique_ptr<QQmlContext> ctxt3 = std::make_unique<QQmlContext>(ctxt2.get()); + std::unique_ptr<QQmlContext> ctxt4 = std::make_unique<QQmlContext>(ctxt2.get()); + std::unique_ptr<QQmlContext> ctxt5 = std::make_unique<QQmlContext>(ctxt.get()); + std::unique_ptr<QQmlContext> ctxt6 = std::make_unique<QQmlContext>(engine.get()); + std::unique_ptr<QQmlContext> ctxt7 = std::make_unique<QQmlContext>(engine->rootContext()); QCOMPARE(ctxt->parentContext(), engine->rootContext()); - QCOMPARE(ctxt2->parentContext(), ctxt); - QCOMPARE(ctxt3->parentContext(), ctxt2); - QCOMPARE(ctxt4->parentContext(), ctxt2); - QCOMPARE(ctxt5->parentContext(), ctxt); + QCOMPARE(ctxt2->parentContext(), ctxt.get()); + QCOMPARE(ctxt3->parentContext(), ctxt2.get()); + QCOMPARE(ctxt4->parentContext(), ctxt2.get()); + QCOMPARE(ctxt5->parentContext(), ctxt.get()); QCOMPARE(ctxt6->parentContext(), engine->rootContext()); QCOMPARE(ctxt7->parentContext(), engine->rootContext()); - delete ctxt2; ctxt2 = nullptr; + ctxt2.reset(); QCOMPARE(ctxt->parentContext(), engine->rootContext()); QCOMPARE(ctxt3->parentContext(), (QQmlContext *)nullptr); QCOMPARE(ctxt4->parentContext(), (QQmlContext *)nullptr); - QCOMPARE(ctxt5->parentContext(), ctxt); + QCOMPARE(ctxt5->parentContext(), ctxt.get()); QCOMPARE(ctxt6->parentContext(), engine->rootContext()); QCOMPARE(ctxt7->parentContext(), engine->rootContext()); - delete engine; engine = nullptr; + engine.reset(); QCOMPARE(ctxt->parentContext(), (QQmlContext *)nullptr); QCOMPARE(ctxt3->parentContext(), (QQmlContext *)nullptr); @@ -173,13 +177,6 @@ void tst_qqmlcontext::parentContext() QCOMPARE(ctxt5->parentContext(), (QQmlContext *)nullptr); QCOMPARE(ctxt6->parentContext(), (QQmlContext *)nullptr); QCOMPARE(ctxt7->parentContext(), (QQmlContext *)nullptr); - - delete ctxt7; - delete ctxt6; - delete ctxt5; - delete ctxt4; - delete ctxt3; - delete ctxt; } class TestObject : public QObject @@ -229,11 +226,9 @@ private: QQmlComponent component(&engine); \ component.setData("import QtQuick 2.0; QtObject { property variant test: " #name " }", QUrl()); \ \ - QObject *obj = component.create(ctxt); \ + std::unique_ptr<QObject> obj { component.create(ctxt) }; \ \ QCOMPARE(obj->property("test"), value); \ -\ - delete obj; \ } void tst_qqmlcontext::setContextProperty() @@ -279,39 +274,33 @@ void tst_qqmlcontext::setContextProperty() QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl()); - QObject *obj = component.create(&ctxt2); + std::unique_ptr<QObject> obj { component.create(&ctxt2) }; QCOMPARE(obj->property("test"), QVariant(13)); ctxt.setContextProperty("a", QVariant(19)); QCOMPARE(obj->property("test"), QVariant(19)); - - delete obj; } { QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { property variant test: b }", QUrl()); - QObject *obj = component.create(&ctxt2); + std::unique_ptr<QObject> obj { component.create(&ctxt2) }; QCOMPARE(obj->property("test"), QVariant(8)); ctxt.setContextProperty("b", QVariant(5)); QCOMPARE(obj->property("test"), QVariant(8)); ctxt2.setContextProperty("b", QVariant(1912)); QCOMPARE(obj->property("test"), QVariant(1912)); - - delete obj; } { QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { property variant test: e.a }", QUrl()); - QObject *obj = component.create(&ctxt2); + std::unique_ptr<QObject> obj { component.create(&ctxt2) }; QCOMPARE(obj->property("test"), QVariant(12)); obj1.setA(13); QCOMPARE(obj->property("test"), QVariant(13)); - - delete obj; } // New context properties @@ -319,13 +308,11 @@ void tst_qqmlcontext::setContextProperty() QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl()); - QObject *obj = component.create(&ctxt2); + std::unique_ptr<QObject> obj { component.create(&ctxt2) }; QCOMPARE(obj->property("test"), QVariant(19)); ctxt2.setContextProperty("a", QVariant(1945)); QCOMPARE(obj->property("test"), QVariant(1945)); - - delete obj; } // Setting an object-variant context property @@ -337,15 +324,13 @@ void tst_qqmlcontext::setContextProperty() ctxt.setContextProperty("ctxtProp", QVariant()); QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1: TypeError: Cannot read property 'a' of undefined"); - QObject *obj = component.create(&ctxt); + std::unique_ptr<QObject> obj { component.create(&ctxt) }; QVariant v = obj->property("obj"); ctxt.setContextProperty("ctxtProp", v); QCOMPARE(obj->property("test"), QVariant(10)); - - delete obj; } } @@ -407,13 +392,11 @@ void tst_qqmlcontext::setContextObject() QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl()); - QObject *obj = component.create(&ctxt); + std::unique_ptr<QObject> obj { component.create(&ctxt) }; QCOMPARE(obj->property("test"), QVariant(12)); to.setA(14); QCOMPARE(obj->property("test"), QVariant(14)); - - delete obj; } // Change of context object @@ -431,19 +414,19 @@ void tst_qqmlcontext::setContextObject() void tst_qqmlcontext::destruction() { - QQmlContext *ctxt = new QQmlContext(&engine); + std::unique_ptr<QQmlContext> ctxt = std::make_unique<QQmlContext>(&engine); QObject obj; - QQmlEngine::setContextForObject(&obj, ctxt); - QQmlExpression expr(ctxt, nullptr, "a"); + QQmlEngine::setContextForObject(&obj, ctxt.get()); + QQmlExpression expr(ctxt.get(), nullptr, "a"); - QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj)); - QCOMPARE(ctxt, expr.context()); + QCOMPARE(ctxt.get(), QQmlEngine::contextForObject(&obj)); + QCOMPARE(ctxt.get(), expr.context()); - delete ctxt; ctxt = nullptr; + ctxt.reset(); - QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj)); - QCOMPARE(ctxt, expr.context()); + QCOMPARE(ctxt.get(), QQmlEngine::contextForObject(&obj)); + QCOMPARE(ctxt.get(), expr.context()); } void tst_qqmlcontext::idAsContextProperty() @@ -451,18 +434,16 @@ void tst_qqmlcontext::idAsContextProperty() QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { property variant a; a: QtObject { id: myObject } }", QUrl()); - QObject *obj = component.create(); - QVERIFY(obj); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get()); QVariant a = obj->property("a"); QCOMPARE(a.userType(), int(QMetaType::QObjectStar)); - QVariant ctxt = qmlContext(obj)->contextProperty("myObject"); + QVariant ctxt = qmlContext(obj.get())->contextProperty("myObject"); QCOMPARE(ctxt.userType(), int(QMetaType::QObjectStar)); QCOMPARE(a, ctxt); - - delete obj; } // Internal contexts should be read-only @@ -471,28 +452,26 @@ void tst_qqmlcontext::readOnlyContexts() QQmlComponent component(&engine); component.setData("import QtQuick 2.0; QtObject { id: me }", QUrl()); - QObject *obj = component.create(); - QVERIFY(obj); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get()); - QQmlContext *context = qmlContext(obj); + QQmlContext *context = qmlContext(obj.get()); QVERIFY(context); - QCOMPARE(qvariant_cast<QObject*>(context->contextProperty("me")), obj); - QCOMPARE(context->contextObject(), obj); + QCOMPARE(qvariant_cast<QObject*>(context->contextProperty("me")), obj.get()); + QCOMPARE(context->contextObject(), obj.get()); QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set property on internal context."); context->setContextProperty("hello", 12); QCOMPARE(context->contextProperty("hello"), QVariant()); QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set property on internal context."); - context->setContextProperty("hello", obj); + context->setContextProperty("hello", obj.get()); QCOMPARE(context->contextProperty("hello"), QVariant()); QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set context object for internal context."); context->setContextObject(nullptr); - QCOMPARE(context->contextObject(), obj); - - delete obj; + QCOMPARE(context->contextObject(), obj.get()); } void tst_qqmlcontext::objectsAndNames() @@ -624,14 +603,12 @@ void tst_qqmlcontext::refreshExpressionsCrash() component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl()); QVERIFY(component.isReady()); - QObject *o1 = component.create(&ctxt); + std::unique_ptr<QObject> o1 { component.create(&ctxt) }; QObject *o2 = component.create(&ctxt); command.object = o2; QQmlContextData::get(&ctxt)->refreshExpressions(); - - delete o1; } { QQmlEngine engine; @@ -647,13 +624,11 @@ void tst_qqmlcontext::refreshExpressionsCrash() QVERIFY(component.isReady()); QObject *o1 = component.create(&ctxt); - QObject *o2 = component.create(&ctxt); + std::unique_ptr<QObject> o2 { component.create(&ctxt) }; command.object = o1; QQmlContextData::get(&ctxt)->refreshExpressions(); - - delete o2; } } @@ -685,19 +660,15 @@ void tst_qqmlcontext::refreshExpressions() QQmlContext context(engine.rootContext()); QQmlContext context2(&context); - QObject *o1 = component.create(&context); - QObject *o2 = component.create(&context2); - QObject *o3 = component2.create(&context); + std::unique_ptr<QObject> o1 { component.create(&context) }; + std::unique_ptr<QObject> o2 { component.create(&context2) }; + std::unique_ptr<QObject> o3 { component2.create(&context) }; QCOMPARE(command.count, 5); QQmlContextData::get(&context)->refreshExpressions(); QCOMPARE(command.count, 10); - - delete o3; - delete o2; - delete o1; } // Test that updating the root context, only causes expressions in contexts with an @@ -717,10 +688,10 @@ void tst_qqmlcontext::refreshExpressionsRootContext() QString warning = component2.url().toString() + QLatin1String(":4: ReferenceError: unresolvedName is not defined"); - QObject *o1 = component.create(&context); + std::unique_ptr<QObject> o1 { component.create(&context) }; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - QObject *o2 = component2.create(&context2); + std::unique_ptr<QObject> o2 { component2.create(&context2) }; QCOMPARE(command.count, 3); @@ -728,9 +699,6 @@ void tst_qqmlcontext::refreshExpressionsRootContext() QQmlContextData::get(engine.rootContext())->refreshExpressions(); QCOMPARE(command.count, 4); - - delete o2; - delete o1; } void tst_qqmlcontext::skipExpressionRefresh_qtbug_53431() @@ -752,10 +720,9 @@ void tst_qqmlcontext::qtbug_22535() QQmlComponent component(&engine, testFileUrl("qtbug_22535.qml")); QQmlContext context(engine.rootContext()); - QObject *o = component.create(&context); + std::unique_ptr<QObject> o { component.create(&context) }; // Don't crash! - delete o; } void tst_qqmlcontext::evalAfterInvalidate() @@ -858,7 +825,7 @@ void tst_qqmlcontext::contextLeak() scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext(); } - engine.collectGarbage(); + gc(engine); // Each time a JS file (non-pragma-shared) is imported, we create a QQmlContext(Data) for it. // Make sure that context does not leak. @@ -979,6 +946,66 @@ void tst_qqmlcontext::destroyContextProperty() // TODO: Or are we? } +void tst_qqmlcontext::destroyContextObject() +{ + QQmlEngine engine; + QList<QQmlRefPointer<QQmlContextData>> contexts; + QQmlComponent component(&engine, testFileUrl("destroyContextObject.qml")); + QScopedPointer<QObject> root(component.create()); + + QPointer<QObject> a = root->property("a").value<QObject *>(); + QVERIFY(a); + + for (QQmlRefPointer<QQmlContextData> context = QQmlData::get(a)->ownContext; + context; context = context->parent()) { + contexts.append(context); + } + + QObject *deleted = a.data(); + root.reset(); + + QVERIFY(a.isNull()); + + for (const auto &context : contexts) + QVERIFY(context->contextObject() != deleted); +} + +void tst_qqmlcontext::numericContextProperty() +{ + QQmlEngine engine; + auto context = engine.rootContext(); + QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Using numbers as context properties will be disallowed in a future Qt version."); + context->setContextProperty(QLatin1String("11"), 42); + QCOMPARE(context->contextProperty(QLatin1String("11")).toInt(), 42); +} + +void tst_qqmlcontext::gcDeletesContextObject() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("gcDeletesContextObject.qml")); + + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QVERIFY(!o.isNull()); + + QPointer<QObject> contextObject = o->property("o").value<QObject *>(); + QVERIFY(contextObject != nullptr); + + QQmlData *data = QQmlData::get(contextObject); + QVERIFY(data); + QQmlRefPointer<QQmlContextData> context = data->ownContext; + QVERIFY(context); + QCOMPARE(context->contextObject(), contextObject); + + o->setProperty("o", QVariant::fromValue<QObject *>(nullptr)); + QCOMPARE(o->property("o").value<QObject *>(), nullptr); + engine.collectGarbage(); + + QTRY_VERIFY(contextObject.isNull()); + QCOMPARE(context->contextObject(), nullptr); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" diff --git a/tests/auto/qml/qqmlcpputils/CMakeLists.txt b/tests/auto/qml/qqmlcpputils/CMakeLists.txt index 3eb58640c7..ee809e09b5 100644 --- a/tests/auto/qml/qqmlcpputils/CMakeLists.txt +++ b/tests/auto/qml/qqmlcpputils/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlcpputils Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlcpputils LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlcpputils SOURCES tst_qqmlcpputils.cpp diff --git a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp index 11d79707d0..d858fd2ff0 100644 --- a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp +++ b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <qsignalspy.h> @@ -35,13 +35,11 @@ public slots: void tst_qqmlcpputils::fastConnect() { { - MyObject *obj = new MyObject; - qmlobject_connect(obj, MyObject, SIGNAL(signal1()), obj, MyObject, SLOT(slot1())); + std::unique_ptr<MyObject> obj = std::make_unique<MyObject>(); + qmlobject_connect(obj.get(), MyObject, SIGNAL(signal1()), obj.get(), MyObject, SLOT(slot1())); obj->signal1(); QCOMPARE(obj->slotCount, 1); - - delete obj; } { @@ -53,27 +51,24 @@ void tst_qqmlcpputils::fastConnect() } { - MyObject *obj = new MyObject; - QSignalSpy spy(obj, SIGNAL(signal2())); - qmlobject_connect(obj, MyObject, SIGNAL(signal1()), obj, MyObject, SIGNAL(signal2())); + std::unique_ptr<MyObject> obj = std::make_unique<MyObject>(); + QSignalSpy spy(obj.get(), SIGNAL(signal2())); + qmlobject_connect(obj.get(), MyObject, SIGNAL(signal1()), obj.get(), MyObject, SIGNAL(signal2())); obj->signal1(); QCOMPARE(spy.size(), 1); - - delete obj; } } void tst_qqmlcpputils::fastCast() { { - QObject *myObj = new MyObject; - MyObject *obj = qmlobject_cast<MyObject*>(myObj); + std::unique_ptr<QObject> myObj = std::make_unique<MyObject>(); + MyObject *obj = qmlobject_cast<MyObject*>(myObj.get()); QVERIFY(obj); QCOMPARE(obj->metaObject(), myObj->metaObject()); obj->slot1(); QCOMPARE(obj->slotCount, 1); - delete myObj; } { diff --git a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt index 705ee6f357..8d8a90e0a7 100644 --- a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt +++ b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldelegatemodel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldelegatemodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml b/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml index a61e94ea6d..5ef502601c 100644 --- a/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml +++ b/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml.Models 2.15 import QtQuick 2.15 diff --git a/tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml b/tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml new file mode 100644 index 0000000000..37e508302e --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml @@ -0,0 +1,138 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Window +import QtQuick.Controls +import QtQuick.Layouts +import QtQml.Models + +Window { + id: root + width: 640 + height: 480 + visible: true + color: "#111111" + + Column { + spacing: 1 + Repeater { + model: 1000 + + Rectangle { + width: 100 + height: 100 + color: "grey" + DelegateModel { + id: delegateModel + delegate: Rectangle { + width: 100 + height: 20 + color: "black" + Text { + anchors.centerIn: parent + text: "Name: " + model.name + color: "white" + } + } + + property int length: 0 + property var filterAcceptsItem: function(item) { return true; } + + model: ListModel { + id: myModel + + ListElement { + name: "tomato" + classifications: [ + ListElement { classification: "fruit" }, + ListElement { classification: "veg" } + ] + nutritionFacts: [ + ListElement { + calories: "45" + } + ] + } + + ListElement { + name: "apple" + classifications: [ + ListElement { classification: "fruit" } + ] + nutritionFacts: [ + ListElement { + calories: "87" + } + ] + } + + ListElement { + name: "broccoli" + classifications: [ + ListElement { classification: "veg" } + ] + nutritionFacts: [ + ListElement { + calories: "12" + } + ] + } + + ListElement { + name: "squash" + classifications: [ + ListElement { classification: "veg" } + ] + nutritionFacts: [ + ListElement { + calories: "112" + } + ] + } + } + + groups: [ + DelegateModelGroup { id: visibleItems; name: "visible" }, + DelegateModelGroup { name: "veg" }, + DelegateModelGroup { name: "fruit"; includeByDefault: true } + ] + + function update() { + + // Step 1: Filter items + var visible = []; + for (var i = 0; i < items.count; ++i) { + var item = items.get(i); + if (filterAcceptsItem(item.model)) { + visible.push(item); + } + } + + // Step 2: Add all items to the visible group: + for (i = 0; i < visible.length; ++i) { + items.insert(visible[i], delegateModel.filterOnGroup) + } + delegateModel.length = visible.length + } + + items.onChanged: update() + onFilterAcceptsItemChanged: update() + + filterOnGroup: "visible" + Component.onCompleted: { + for(var i = 0; i < myModel.count; i++) { + var temp = 0; + var entry = myModel.get(i); + + for (var j = 0; j < entry.classifications.count; j++) { + temp = entry.classifications.get(j) + items.insert(entry, temp.classification) + } + } + } + } + } + } + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml b/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml new file mode 100644 index 0000000000..b01b14f7b8 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml @@ -0,0 +1,50 @@ +import QtQuick +import QtQml.Models + +Item { + DelegateModel { + id: delegateModel + model: ListModel { + id: sourceModel + + ListElement { title: "foo" } + ListElement { title: "bar" } + + function clear() { + if (count > 0) + remove(0, count); + } + } + + groups: [ + DelegateModelGroup { name: "selectedItems" } + ] + + delegate: Text { + height: DelegateModel.inSelectedItems ? implicitHeight * 2 : implicitHeight + Component.onCompleted: { + if (index === 0) + DelegateModel.inSelectedItems = true; + } + } + + Component.onCompleted: { + items.create(0) + items.create(1) + } + } + + ListView { + anchors.fill: parent + model: delegateModel + } + + Timer { + running: true + interval: 10 + onTriggered: sourceModel.clear() + } + + property int count: delegateModel.items.count +} + diff --git a/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml b/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml index 42bf882fda..bcc7a2786a 100644 --- a/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml +++ b/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml.Models 2.15 import QtQuick 2.15 diff --git a/tests/auto/qml/qqmldelegatemodel/data/listModel.qml b/tests/auto/qml/qqmldelegatemodel/data/listModel.qml index f74abb2e9a..b4ea7a283f 100644 --- a/tests/auto/qml/qqmldelegatemodel/data/listModel.qml +++ b/tests/auto/qml/qqmldelegatemodel/data/listModel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml.Models 2.15 import QtQuick 2.15 diff --git a/tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml b/tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml new file mode 100644 index 0000000000..f21577e395 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml @@ -0,0 +1,18 @@ +import QtQuick + +Window { + id: window + width: 640 + height: 480 + visible: true + property alias testModel: repeater.model + + Repeater { + id: repeater + model: 1 + delegate: Item { + Component.onCompleted: repeater.model = 0 + } + } +} + diff --git a/tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml new file mode 100644 index 0000000000..e392b2e5c9 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml @@ -0,0 +1,55 @@ +import QtQml + +DelegateModel { + id: root + + property ListModel listModel: ListModel { + ListElement { + modelData: "a" + row: "b" + column: "c" + model: "d" + hasModelChildren: "e" + index: "f" + } + } + + property var array: [{ + modelData: "a", + row: "b", + column: "c", + model: "d", + hasModelChildren: "e", + index: "f" + }] + + property QtObject object: QtObject { + property string modelData: "a" + property string row: "b" + property string column: "c" + property string model: "d" + property string hasModelChildren: "e" + property string index: "f" + } + + property int n: -1 + + model: { + switch (n) { + case 0: return listModel + case 1: return array + case 2: return object + } + return undefined; + } + + delegate: QtObject { + required property string modelData + required property string row + required property string column + required property string model + required property string hasModelChildren + required property string index + objectName: [modelData, row, column, model, hasModelChildren, index].join(" ") + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml b/tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml new file mode 100644 index 0000000000..5ae2038e1f --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml @@ -0,0 +1,62 @@ +import QtQuick +import QtQuick.Window +import QtQml.Models + +Window { + id: win + visible: true + width: 640 + height: 480 + property int destroyCount : 0; + property int createCount : 0; + property alias testListModel: mdl + + DelegateModel { + id: visualModel + model: ListModel { + id: mdl + ListElement { + name: "a" + hidden: false + } + ListElement { + name: "b" + hidden: true + } + ListElement { + name: "c" + hidden: false + } + } + + filterOnGroup: "selected" + + groups: [ + DelegateModelGroup { + name: "selected" + includeByDefault: true + } + ] + + delegate: Text { + visible: DelegateModel.inSelected + property var idx + Component.onCompleted: { + ++createCount + idx = index + DelegateModel.inPersistedItems = true + DelegateModel.inSelected = !model.hidden + } + Component.onDestruction: ++destroyCount + text: model.name + } + } + + ListView { + id: listView + model: visualModel + anchors.fill: parent + focus: true + } + +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml new file mode 100644 index 0000000000..467d60dff8 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml @@ -0,0 +1,50 @@ +import QtQml + +DelegateModel { + id: root + + property ListModel singularModel: ListModel { + ListElement { + a: "a" + } + ListElement { + a: "a" + } + } + + property ListModel listModel: ListModel { + ListElement { + a: "a" + b: "b" + } + ListElement { + a: "a" + b: "b" + } + } + + property var array: [ + {a: "a", b: "b"}, {a: "b", b: "b"} + ] + + property QtObject object: QtObject { + property string a: "a" + property string b: "b" + } + + property int n: -1 + + model: { + switch (n) { + case 0: return singularModel + case 1: return listModel + case 2: return array + case 3: return object + } + return undefined; + } + + delegate: QtObject { + required property string a + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml new file mode 100644 index 0000000000..1fe6168c79 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml @@ -0,0 +1,16 @@ +import QtQuick + +ListView { + id: root + anchors.fill: parent + property bool success: (currentItem?.mydata ?? 0) === 42 + height: 300 + width: 200 + + delegate: Rectangle { + required property var model + implicitWidth: 100 + implicitHeight: 50 + property var mydata: model?.foo ?? model.bar + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml new file mode 100644 index 0000000000..08f1c7d68e --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml @@ -0,0 +1,64 @@ +import QtQml + +DelegateModel { + id: root + + // useful object as model, int as modelData + property ListModel singularModel: ListModel { + ListElement { + x: 11 + } + ListElement { + x: 12 + } + } + + // same, useful, object as model and modelData + property ListModel listModel: ListModel { + ListElement { + x: 13 + y: 14 + } + ListElement { + x: 15 + y: 16 + } + } + + // useful but different objects as modelData and model + // This is how the array accessor works. We can live with it. + property var array: [ + {x: 17, y: 18}, {x: 19, y: 20} + ] + + // useful but different objects as modelData and model + // This is how the object accessor works. We can live with it. + property QtObject object: QtObject { + property int x: 21 + property int y: 22 + } + + property int n: -1 + + model: { + switch (n) { + case 0: return singularModel + case 1: return listModel + case 2: return array + case 3: return object + } + return undefined; + } + + delegate: QtObject { + required property point modelData + required property QtObject model + + property real modelX: model.x + property real modelDataX: modelData.x + property point modelSelf: model + property point modelDataSelf: modelData + property point modelModelData: model.modelData + property point modelAnonymous: model[""] + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml new file mode 100644 index 0000000000..f24009f873 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml @@ -0,0 +1,72 @@ +import QtQml + +DelegateModel { + id: root + + // useful object as model, string as modelData + property ListModel singularModel: ListModel { + ListElement { + a: "a" + } + ListElement { + a: "b" + } + } + + // same, useful, object as model and modelData + property ListModel listModel: ListModel { + ListElement { + a: "a" + b: "a" + } + ListElement { + a: "b" + b: "b" + } + } + + // useful but different objects as modelData and model + // This is how the array accessor works. We can live with it. + property var array: [ + {a: "a", b: "a"}, {a: "b", b: "a"} + ] + + // string as modelData + // object with anonymous string property as model. + property var stringList: ["a", "b"] + + // useful but different objects as modelData and model + // This is how the object accessor works. We can live with it. + property QtObject object: QtObject { + property string a: "a" + property string b: "a" + } + + // number as modelData + // object with anonymous number property as model + property int n: -1 + + model: { + switch (n) { + case 0: return singularModel + case 1: return listModel + case 2: return array + case 3: return stringList + case 4: return object + case 5: return n + } + return undefined; + } + + delegate: QtObject { + required property var modelData + required property var model + + property var modelA: model.a + property var modelDataA: modelData.a + property var modelSelf: model + property var modelDataSelf: modelData + property var modelModelData: model.modelData + property var modelAnonymous: model[""] + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml b/tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml new file mode 100644 index 0000000000..b2367b3a6e --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml @@ -0,0 +1,93 @@ +import QtQuick +import Qt.labs.qmlmodels +import QtQml.Models + +Item { + id: root + property bool triggered: false + onTriggeredChanged: { + rootLM.setProperty(1, "currentRole", "first"); + } + width: 800 + height: 680 + + function verify(): bool { + rootLV.currentIndex = 1; // needed for itemAtIndex to work + if (root.triggered) + return rootLV.itemAtIndex(0).isFirst && rootLV.itemAtIndex(1).isFirst; + else + return rootLV.itemAtIndex(0).isFirst && !rootLV.itemAtIndex(1).isFirst; + } + + ListModel { + id: rootLM + ListElement { + currentRole: "first" + firstText: "TEXT_FIRST_1" + secondText: "TEXT_SECOND_1" + } + ListElement { + currentRole: "second" + firstText: "TEXT_FIRST_2" + secondText: "TEXT_SECOND_2" + } + } + + DelegateModel { + id: delModel + model: rootLM + delegate: DelegateChooser { + id: delegateChooser + role: "currentRole" + DelegateChoice { + roleValue: "first" + Rectangle { + property bool isFirst: true + height: 30 + width: rootLV.width + color: "yellow" + Text { + anchors.centerIn: parent + text: firstText + " " + currentRole + } + } + } + DelegateChoice { + roleValue: "second" + Rectangle { + property bool isFirst: false + height: 30 + width: rootLV.width + color: "red" + Text { + anchors.centerIn: parent + text: secondText + " " + currentRole + } + } + } + } + } + + TapHandler { + // for manual testing + onTapped: root.triggered = true + } + + Rectangle { + width: 200 + height: 300 + anchors.centerIn: parent + border.color: "black" + border.width: 1 + color: "blue" + + ListView { + id: rootLV + objectName: "listview" + anchors.margins: 30 + anchors.fill: parent + cacheBuffer: 0 + model: delModel + } + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp index ca66ddb618..2cacda5513 100644 --- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -1,16 +1,21 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/qtest.h> +#include <QtCore/qjsonobject.h> #include <QtCore/QConcatenateTablesProxyModel> #include <QtGui/QStandardItemModel> #include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlapplicationengine.h> #include <QtQmlModels/private/qqmldelegatemodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtTest/QSignalSpy> +#include <forward_list> + class tst_QQmlDelegateModel : public QQmlDataTest { Q_OBJECT @@ -19,6 +24,7 @@ public: tst_QQmlDelegateModel(); private slots: + void resettingRolesRespected(); void valueWithoutCallingObjectFirst_data(); void valueWithoutCallingObjectFirst(); void qtbug_86017(); @@ -26,6 +32,16 @@ private slots: void contextAccessedByHandler(); void redrawUponColumnChange(); void nestedDelegates(); + void universalModelData(); + void typedModelData(); + void requiredModelData(); + void overriddenModelData(); + void deleteRace(); + void persistedItemsStayInCache(); + void unknownContainersAsModel(); + void doNotUnrefObjectUnderConstruction(); + void clearCacheDuringInsertion(); + void viewUpdatedOnDelegateChoiceAffectingRoleChange(); }; class AbstractItemModel : public QAbstractItemModel @@ -85,6 +101,61 @@ tst_QQmlDelegateModel::tst_QQmlDelegateModel() qmlRegisterType<AbstractItemModel>("Test", 1, 0, "AbstractItemModel"); } +class TableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + int rowCount(const QModelIndex & = QModelIndex()) const override + { + return 1; + } + + int columnCount(const QModelIndex & = QModelIndex()) const override + { + return 1; + } + + QVariant data(const QModelIndex &index, int role) const override + { + switch (role) { + case 0: + return QString("foo: %1, %2").arg(index.column()).arg(index.row()); + case 1: + return 42; + default: + break; + } + + return QVariant(); + } + + Q_INVOKABLE void change() { beginResetModel(); toggle = !toggle; endResetModel(); } + + QHash<int, QByteArray> roleNames() const override + { + if (toggle) + return { {0, "foo"} }; + else + return { {1, "bar"} }; + } + + bool toggle = true; +}; + +void tst_QQmlDelegateModel::resettingRolesRespected() +{ + auto model = std::make_unique<TableModel>(); + QQmlApplicationEngine engine; + engine.setInitialProperties({ {"model", QVariant::fromValue(model.get()) }} ); + engine.load(testFileUrl("resetModelData.qml")); + QTRY_VERIFY(!engine.rootObjects().isEmpty()); + QObject *root = engine.rootObjects().constFirst(); + QVERIFY(!root->property("success").toBool()); + model->change(); + QTRY_VERIFY(root->property("success").toBool()); +} + void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst_data() { QTest::addColumn<QUrl>("qmlFileUrl"); @@ -207,6 +278,344 @@ void tst_QQmlDelegateModel::nestedDelegates() QFAIL("Loader not found"); } +void tst_QQmlDelegateModel::universalModelData() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("universalModelData.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data()); + QVERIFY(delegateModel); + + for (int i = 0; i < 6; ++i) { + delegateModel->setProperty("n", i); + QObject *delegate = delegateModel->object(0); + QObject *modelItem = delegate->property("modelSelf").value<QObject *>(); + QVERIFY(modelItem != nullptr); + switch (i) { + case 0: { + // list model with 1 role + QCOMPARE(delegate->property("modelA"), QStringLiteral("a")); + QVERIFY(!delegate->property("modelDataA").isValid()); + QCOMPARE(delegate->property("modelDataSelf"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelModelData"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelAnonymous"), QStringLiteral("a")); + break; + } + case 1: { + // list model with 2 roles + QCOMPARE(delegate->property("modelA"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelDataA"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelDataSelf"), QVariant::fromValue(modelItem)); + QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelItem)); + QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelItem)); + break; + } + case 2: { + // JS array of objects + QCOMPARE(delegate->property("modelA"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelDataA"), QStringLiteral("a")); + + // Do the comparison in QVariantMap. The values get converted back and forth a + // few times, making any JavaScript equality comparison impossible. + // This is only due to test setup, though. + const QVariantMap modelData = delegate->property("modelDataSelf").value<QVariantMap>(); + QVERIFY(!modelData.isEmpty()); + QCOMPARE(delegate->property("modelModelData").value<QVariantMap>(), modelData); + QCOMPARE(delegate->property("modelAnonymous").value<QVariantMap>(), modelData); + break; + } + case 3: { + // string list + QVERIFY(!delegate->property("modelA").isValid()); + QVERIFY(!delegate->property("modelDataA").isValid()); + QCOMPARE(delegate->property("modelDataSelf"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelModelData"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelAnonymous"), QStringLiteral("a")); + break; + } + case 4: { + // single object + QCOMPARE(delegate->property("modelA"), QStringLiteral("a")); + QCOMPARE(delegate->property("modelDataA"), QStringLiteral("a")); + QObject *modelData = delegate->property("modelDataSelf").value<QObject *>(); + QVERIFY(modelData != nullptr); + QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelData)); + QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelData)); + break; + } + case 5: { + // a number + QVERIFY(!delegate->property("modelA").isValid()); + QVERIFY(!delegate->property("modelDataA").isValid()); + const QVariant modelData = delegate->property("modelDataSelf"); + + // This is int on 32bit systems because qsizetype fits into int there. + // On 64bit systems it's double because qsizetype doesn't fit into int. + if (sizeof(qsizetype) > sizeof(int)) + QCOMPARE(modelData.metaType(), QMetaType::fromType<double>()); + else + QCOMPARE(modelData.metaType(), QMetaType::fromType<int>()); + + QCOMPARE(modelData.value<int>(), 0); + QCOMPARE(delegate->property("modelModelData"), modelData); + QCOMPARE(delegate->property("modelAnonymous"), modelData); + break; + } + default: + QFAIL("wrong model number"); + break; + } + } + +} + +void tst_QQmlDelegateModel::typedModelData() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("typedModelData.qml"); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data()); + QVERIFY(delegateModel); + + for (int i = 0; i < 4; ++i) { + if (i == 0) { + for (int j = 0; j < 3; ++j) { + QTest::ignoreMessage( + QtWarningMsg, + "Could not find any constructor for value type QQmlPointFValueType " + "to call with value QVariant(double, 11)"); + } + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":62:9: Unable to assign double to QPointF")); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":61:9: Unable to assign double to QPointF")); + } + + delegateModel->setProperty("n", i); + QObject *delegate = delegateModel->object(0); + QVERIFY(delegate); + const QPointF modelItem = delegate->property("modelSelf").value<QPointF>(); + switch (i) { + case 0: { + // list model with 1 role. + // Does not work, for the most part, because the model is singular + QCOMPARE(delegate->property("modelX"), 11.0); + QCOMPARE(delegate->property("modelDataX"), 0.0); + QCOMPARE(delegate->property("modelSelf"), QPointF(11.0, 0.0)); + QCOMPARE(delegate->property("modelDataSelf"), QPointF()); + QCOMPARE(delegate->property("modelModelData"), QPointF()); + QCOMPARE(delegate->property("modelAnonymous"), QPointF()); + break; + } + case 1: { + // list model with 2 roles + QCOMPARE(delegate->property("modelX"), 13.0); + QCOMPARE(delegate->property("modelDataX"), 13.0); + QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelItem)); + QCOMPARE(delegate->property("modelDataSelf"), QVariant::fromValue(modelItem)); + QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelItem)); + QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelItem)); + break; + } + case 2: { + // JS array of objects + QCOMPARE(delegate->property("modelX"), 17.0); + QCOMPARE(delegate->property("modelDataX"), 17.0); + + const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>(); + QCOMPARE(modelData, QPointF(17, 18)); + QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelData)); + QCOMPARE(delegate->property("modelModelData").value<QPointF>(), modelData); + QCOMPARE(delegate->property("modelAnonymous").value<QPointF>(), modelData); + break; + } + case 3: { + // single object + QCOMPARE(delegate->property("modelX"), 21); + QCOMPARE(delegate->property("modelDataX"), 21); + const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>(); + QCOMPARE(modelData, QPointF(21, 22)); + QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelData)); + QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelData)); + QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelData)); + break; + } + default: + QFAIL("wrong model number"); + break; + } + } + +} + +void tst_QQmlDelegateModel::requiredModelData() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("requiredModelData.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data()); + QVERIFY(delegateModel); + + for (int i = 0; i < 4; ++i) { + delegateModel->setProperty("n", i); + QObject *delegate = delegateModel->object(0); + QVERIFY(delegate); + const QVariant a = delegate->property("a"); + QCOMPARE(a.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(a.toString(), QLatin1String("a")); + } +} + +void tst_QQmlDelegateModel::overriddenModelData() +{ + QTest::failOnWarning(QRegularExpression( + "Final member [^ ]+ is overridden in class [^\\.]+. The override won't be used.")); + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("overriddenModelData.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + + QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data()); + QVERIFY(delegateModel); + + for (int i = 0; i < 3; ++i) { + delegateModel->setProperty("n", i); + QObject *delegate = delegateModel->object(0); + QVERIFY(delegate); + + if (i == 1 || i == 2) { + // You can actually not override if the model is a QObject or a JavaScript array. + // Someone is certainly relying on this. + // We need to find a migration mechanism to fix it. + QCOMPARE(delegate->objectName(), QLatin1String(" 0 0 e 0")); + } else { + QCOMPARE(delegate->objectName(), QLatin1String("a b c d e f")); + } + } +} + +void tst_QQmlDelegateModel::deleteRace() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("deleteRace.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QTRY_COMPARE(o->property("count").toInt(), 2); + QTRY_COMPARE(o->property("count").toInt(), 0); +} + +void tst_QQmlDelegateModel::persistedItemsStayInCache() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("persistedItemsCache.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> object(component.create()); + QVERIFY(object); + const QVariant properyListModel = object->property("testListModel"); + QQmlListModel *listModel = qvariant_cast<QQmlListModel *>(properyListModel); + QVERIFY(listModel); + QTRY_COMPARE(object->property("createCount").toInt(), 3); + listModel->clear(); + QTRY_COMPARE(object->property("destroyCount").toInt(), 3); +} + +Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::forward_list) +void tst_QQmlDelegateModel::unknownContainersAsModel() +{ + QQmlEngine engine; + + QQmlComponent modelComponent(&engine); + modelComponent.setData("import QtQml.Models\nDelegateModel {}\n", QUrl()); + QCOMPARE(modelComponent.status(), QQmlComponent::Ready); + + QScopedPointer<QObject> o(modelComponent.create()); + QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(o.data()); + QVERIFY(delegateModel); + + QQmlComponent delegateComponent(&engine); + delegateComponent.setData("import QtQml\nQtObject { objectName: modelData }\n", QUrl()); + QCOMPARE(delegateComponent.status(), QQmlComponent::Ready); + + delegateModel->setDelegate(&delegateComponent); + + QList<QJsonObject> json; + for (int i = 0; i < 10; ++i) + json.append(QJsonObject({{"foo", i}})); + + // Recognized as list + delegateModel->setModel(QVariant::fromValue(json)); + QObject *obj = delegateModel->object(0, QQmlIncubator::Synchronous); + QVERIFY(obj); + QCOMPARE(delegateModel->count(), 10); + QCOMPARE(delegateModel->model().metaType(), QMetaType::fromType<QList<QJsonObject>>()); + + QVERIFY(qMetaTypeId<std::forward_list<int>>() > 0); + std::forward_list<int> mess; + mess.push_front(4); + mess.push_front(5); + mess.push_front(6); + + // Converted into QVariantList + delegateModel->setModel(QVariant::fromValue(mess)); + obj = delegateModel->object(0, QQmlIncubator::Synchronous); + QVERIFY(obj); + QCOMPARE(delegateModel->count(), 3); + QCOMPARE(delegateModel->model().metaType(), QMetaType::fromType<QVariantList>()); +} + +void tst_QQmlDelegateModel::doNotUnrefObjectUnderConstruction() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("modifyObjectUnderConstruction.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> object(component.create()); + QVERIFY(object); + QTRY_COMPARE(object->property("testModel").toInt(), 0); +} + +void tst_QQmlDelegateModel::clearCacheDuringInsertion() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("clearCacheDuringInsertion.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> object(component.create()); + QVERIFY(object); + QTRY_COMPARE(object->property("testModel").toInt(), 0); +} + +void tst_QQmlDelegateModel::viewUpdatedOnDelegateChoiceAffectingRoleChange() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("viewUpdatedOnDelegateChoiceAffectingRoleChange.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> object(component.create()); + QVERIFY(object); + QQuickItem *listview = object->findChild<QQuickItem *>("listview"); + QVERIFY(listview); + QTRY_VERIFY(listview->property("count").toInt() > 0); + bool returnedValue = false; + QMetaObject::invokeMethod(object.get(), "verify", Q_RETURN_ARG(bool, returnedValue)); + QVERIFY(returnedValue); + returnedValue = false; + + object->setProperty("triggered", "true"); + QTRY_VERIFY(listview->property("count").toInt() > 0); + QMetaObject::invokeMethod(object.get(), "verify", Q_RETURN_ARG(bool, returnedValue)); + QVERIFY(returnedValue); +} + QTEST_MAIN(tst_QQmlDelegateModel) #include "tst_qqmldelegatemodel.moc" diff --git a/tests/auto/qml/qqmldirparser/CMakeLists.txt b/tests/auto/qml/qqmldirparser/CMakeLists.txt index 0ed28b63ee..d7738c08f8 100644 --- a/tests/auto/qml/qqmldirparser/CMakeLists.txt +++ b/tests/auto/qml/qqmldirparser/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmldirparser Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmldirparser LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir b/tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir new file mode 100644 index 0000000000..3a3e606099 --- /dev/null +++ b/tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir @@ -0,0 +1,3 @@ +module MyModule +internal InternalType 1.0 InternalType.qml + diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp index 55b776c6a2..4cbb7aaf47 100644 --- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp +++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QObject> @@ -60,7 +60,7 @@ namespace { { QStringList rv; - foreach (const QQmlDirParser::Plugin &p, plugins) + for (const QQmlDirParser::Plugin &p : plugins) rv.append(toString(p)); return rv; @@ -86,7 +86,8 @@ namespace { { QStringList rv; - foreach (const QQmlDirParser::Component &c, components.values()) + const auto values = components.values(); + for (const QQmlDirParser::Component &c : values) rv.append(toString(c)); std::sort(rv.begin(), rv.end()); @@ -97,7 +98,7 @@ namespace { { QStringList rv; - foreach (const QQmlDirParser::Import &c, components) + for (const QQmlDirParser::Import &c : components) rv.append(toString(c)); std::sort(rv.begin(), rv.end()); @@ -115,7 +116,7 @@ namespace { { QStringList rv; - foreach (const QQmlDirParser::Script &s, scripts) + for (const QQmlDirParser::Script &s : scripts) rv.append(toString(s)); return rv; @@ -421,6 +422,17 @@ void tst_qqmldirparser::parse_data() << QStringList() << QStringList() << false; + + QTest::newRow("versioned-internal") + << "versioned-internal/qmldir" + << QString() + << QStringList() + << QStringList() + << QStringList() + << QStringList({"InternalType|InternalType.qml|1|0|true"}) + << QStringList() + << QStringList() + << false; } void tst_qqmldirparser::parse() @@ -435,7 +447,7 @@ void tst_qqmldirparser::parse() QFETCH(bool, designerSupported); QFile f(testFile(file)); - f.open(QIODevice::ReadOnly); + QVERIFY(f.open(QIODevice::ReadOnly)); QQmlDirParser p; p.parse(f.readAll()); diff --git a/tests/auto/qml/qqmlecmascript/CMakeLists.txt b/tests/auto/qml/qqmlecmascript/CMakeLists.txt index f4b4169c82..12cab47a36 100644 --- a/tests/auto/qml/qqmlecmascript/CMakeLists.txt +++ b/tests/auto/qml/qqmlecmascript/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlecmascript Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlecmascript LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -24,6 +30,7 @@ qt_internal_add_test(tst_qqmlecmascript Qt::Network Qt::QmlPrivate Qt::QuickTestUtilsPrivate + Qt::QuickPrivate TESTDATA ${test_data} ) diff --git a/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml new file mode 100644 index 0000000000..f8ee283f1e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml +import Qt.test + +QtObject { + property listPropertyAssignment_Gadget gadget + property ListPropertyAssignment_Object object: ListPropertyAssignment_Object { } + + Component.onCompleted: { + gadget.gadgetStringList = ["Element1", "Element2", "Element3"] + gadget.gadgetVariantList = [1, "foo", null, true] + object.qobjectStringList = ["Element1", "Element2", "Element3"] + + gadget.gadgetStringList[0] = "Completely new Element" + gadget.gadgetVariantList[0] = "Completely new Element" + object.qobjectStringList[0] = "Completely new Element" + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml index 9273a52f54..ff7fe4434c 100644 --- a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml @@ -22,7 +22,6 @@ Rectangle { if (component.status == Component.Ready) { text.vp = component.createObject(null); // has JavaScript ownership } - gc(); } } } diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml index f4307081c5..4e8da872f5 100644 --- a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml @@ -25,7 +25,6 @@ Rectangle { if (component.status == Component.Ready) { textTwo.vp = component.createObject(null); // has JavaScript ownership } - gc(); } function deassignVp() { diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml deleted file mode 100644 index d611e0fe30..0000000000 --- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.0 - -Item { - property int changeCount: 0 - - // invalid property name - we don't allow $ - property bool $nameWithDollarsign: false - - on$NameWithDollarsignChanged: { - changeCount = changeCount + 4; - } -} diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml deleted file mode 100644 index a6862517c6..0000000000 --- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.0 - -Item { - property int changeCount: 0 - - property bool _6nameWithUnderscoreNumber: false - - // invalid property name - the first character after an underscore must be a letter - on_6NameWithUnderscoreNumberChanged: { - changeCount = changeCount + 3; - } -} diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml index f91fb71f1f..9a3141e15a 100644 --- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml +++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml @@ -6,6 +6,8 @@ Item { property bool normalName: false property bool _nameWithUnderscore: false property bool ____nameWithUnderscores: false + property bool _6nameWithUnderscoreNumber: false + property bool $nameWithDollarsign: false onNormalNameChanged: { changeCount = changeCount + 1; @@ -19,9 +21,20 @@ Item { changeCount = changeCount + 3; } + on$NameWithDollarsignChanged: { + changeCount = changeCount + 4; + } + + on_6NameWithUnderscoreNumberChanged: { + changeCount = changeCount + 5; + } + Component.onCompleted: { normalName = true; _nameWithUnderscore = true; ____nameWithUnderscores = true; + $nameWithDollarsign = true; + _6nameWithUnderscoreNumber = true; } + } diff --git a/tests/auto/qml/qqmlecmascript/data/date.qml b/tests/auto/qml/qqmlecmascript/data/date.qml index 8e190b1f8f..33644b604d 100644 --- a/tests/auto/qml/qqmlecmascript/data/date.qml +++ b/tests/auto/qml/qqmlecmascript/data/date.qml @@ -26,8 +26,12 @@ Item { function check_value(date, tag, qdt) { var result = true; + if (isNaN(date)) { + console.warn("Invalid Date"); + return false; + } if (date.getFullYear() != 2014) { - console.warn("Wrong year (" + tag + "):", date.getFullYear(), "!= 2014") + console.warn("Wrong year (" + tag + "):", date.getFullYear(), "!= 2014"); result = false; } // July; JS's months are Jan 0 to 11 Dec, vs. Qt's 1 to 12. diff --git a/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml b/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml new file mode 100644 index 0000000000..51c321684e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml @@ -0,0 +1,30 @@ +import QtQml +import test + +QtObject { + id: root + + property FrozenObjects a: FrozenObjects { objectName: "a" } + property FrozenObjects b: FrozenObjects { objectName: "b" } + + // Create wrappers and immediately discard them + objectName: a.getConst().objectName + "/" + b.getNonConst().objectName + + // Create a non-const wrapper and retain it + property var objNonConst: a.getNonConst() + + // Create a const wrapper and retain it + property var objConst: b.getConst() + + property int gcs: 0 + + property Timer t: Timer { + interval: 1 + running: true + repeat: true + onTriggered: { + gc(); + ++root.gcs; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/getThis.qml b/tests/auto/qml/qqmlecmascript/data/getThis.qml index db9854e872..90c51caf4d 100644 --- a/tests/auto/qml/qqmlecmascript/data/getThis.qml +++ b/tests/auto/qml/qqmlecmascript/data/getThis.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.12 diff --git a/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml b/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml new file mode 100644 index 0000000000..321bd21ad8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml @@ -0,0 +1,29 @@ +import QtQml + +QtObject { + function test_proxy() { + let base = { + id: 'baseid', + name: 'basename', + length: 42 + }; + + let handler = { + get: function (ao, prop) { + return Reflect.get(ao, prop); + } + }; + + let r = new Proxy(base, handler); + let validCount = 0; + if (r.id === base.id) + ++validCount; + if (r.length === base.length) + ++validCount; + if (r.name === base.name) + ++validCount; + return validCount; + } + + property int result: test_proxy() +} diff --git a/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml b/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml new file mode 100644 index 0000000000..9d2ee433fd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml @@ -0,0 +1,6 @@ +import Qt.test +import QtQml + +QtObject { + Component.onCompleted: SingletonInheritanceTest.trackPage("test", {x: 42}) +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml index 6f5094de27..5f017c4880 100644 --- a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml @@ -16,7 +16,6 @@ Item { function assignVarProp() { vp = constructGarbage(); - gc(); } function deassignVarProp() { diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml index ecc4892334..144fcc8a93 100644 --- a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml @@ -16,7 +16,6 @@ Item { function assignCircular() { vp = constructGarbage(); - gc(); } function deassignCircular() { diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml new file mode 100644 index 0000000000..a50c8e0f46 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml @@ -0,0 +1,12 @@ +import QtQml +import Qt.test.singletonWithEnum + +QtObject { + id: root + required property QtObject invokableObject + + Component.onCompleted: { + root.invokableObject.method_typeWrapper(Component) + root.invokableObject.method_typeWrapper(SingletonWithEnum) + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml new file mode 100644 index 0000000000..490fec2dc8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml @@ -0,0 +1,8 @@ +import QtQuick + +Item { + property var val: undefined + property var observes: width + width: val + implicitWidth: 200 +} diff --git a/tests/auto/qml/qqmlecmascript/data/resetGadget.qml b/tests/auto/qml/qqmlecmascript/data/resetGadget.qml new file mode 100644 index 0000000000..2bc196da34 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/resetGadget.qml @@ -0,0 +1,9 @@ +import Qt.test + +ResettableGadgetHolder { + id: root + property bool trigger: false + onTriggerChanged: { + root.g.value = undefined + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml new file mode 100644 index 0000000000..2933d9b4d5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml @@ -0,0 +1,20 @@ +import QtQuick + +Item { + height: undefined + implicitHeight: 30 + property int steps: 0 + + Behavior on height { + NumberAnimation { + duration: 500 + } + } + + onHeightChanged: ++steps + + Component.onCompleted: { + height = Qt.binding(() => implicitHeight); + implicitHeight = 60; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml new file mode 100644 index 0000000000..7d43aa6c05 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml @@ -0,0 +1,21 @@ +import Qt.test +import QtQuick + +Item { + id: root + property int count: 0 + signal someSignal + + property Item item: Item { + id: contextItem + function test() { + count++; + } + } + + function itemDestroy() { + contextItem.destroy() + } + + Component.onCompleted: root.someSignal.connect(contextItem, contextItem.test); +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml new file mode 100644 index 0000000000..1123edf3f7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml @@ -0,0 +1,30 @@ +import Qt.test +import QtQuick + +MyQmlObject { + id: root + property int a: 0 + + signal someSignal + + function disconnectSignal() { + root.someSignal.disconnect(other.MyQmlObject, root.test) + } + + function destroyObj() { + other.destroy() + } + + function test() { + other.MyQmlObject.value2++ + root.a = other.MyQmlObject.value2 + } + + property MyQmlObject obj + obj: MyQmlObject { + id: other + MyQmlObject.value2: 0 + } + + Component.onCompleted: root.someSignal.connect(other.MyQmlObject, root.test) +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml new file mode 100644 index 0000000000..efbbc9fedc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml @@ -0,0 +1,36 @@ +import Qt.test +import QtQml + +QtObject { + id: root + + property int a: 0 + property int b: 0 + + signal someSignal + + function destroyObj() { + obj.destroy() + } + + function test() { + ++a + } + + component DestructionReceiver: QtObject { + // Has its own context and therefore can receive Component.onDestruction + } + + property QtObject obj: QtObject { + property QtObject inner: DestructionReceiver { + Component.onDestruction: { + // The outer obj is already queued for deletion. + // We don't want to see this signal delivered. + root.someSignal(); + ++root.b + } + } + } + + Component.onCompleted: someSignal.connect(obj, test) +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml new file mode 100644 index 0000000000..f666945b33 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml @@ -0,0 +1,21 @@ +import QtQuick +import Test + +Item { + id: root + + property int a: 0 + signal mySignal + + function test() { + MyInheritedQmlObjectSingleton.value++ + root.a = MyInheritedQmlObjectSingleton.value + } + + function disconnectSingleton() { + root.mySignal.disconnect(MyInheritedQmlObjectSingleton, root.test) + } + + Component.onCompleted: root.mySignal.connect(MyInheritedQmlObjectSingleton, + root.test) +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml new file mode 100644 index 0000000000..9d24fa85ae --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml @@ -0,0 +1,19 @@ +import Qt.test +import QtQuick + +Item { + id: root + property int count: 0 + signal someSignal + signal disconnectSignal + + property Item item: Item { + id: contextItem + function test() { + count++; + } + } + + Component.onCompleted: root.someSignal.connect(contextItem, contextItem.test); + onDisconnectSignal: { root.someSignal.disconnect(contextItem, contextItem.test); } +} diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml index b0e951b89a..f4bdcdbed3 100644 --- a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml +++ b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml @@ -1,5 +1,5 @@ // Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import Test 1.0 diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml index 5a5bad5b8c..5d6af4d67c 100644 --- a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml +++ b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import Test 1.0 diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index 40f5e5cf5c..5f7713392b 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include "testtypes.h" #ifndef QT_NO_WIDGETS # include <QWidget> @@ -8,6 +9,7 @@ #include <QQmlEngine> #include <QJSEngine> #include <QThread> +#include <QtQuickTestUtils/private/qmlutils_p.h> class BaseExtensionObject : public QObject { @@ -104,7 +106,7 @@ public: void setWidth(int) { } }; -void MyQmlObject::v8function(QQmlV4Function *function) +void MyQmlObject::v8function(QQmlV4FunctionPtr function) { function->v4engine()->throwError(QStringLiteral("Exception thrown from within QObject slot")); } @@ -393,9 +395,7 @@ void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject if (that->gcOnAppend) { QQmlEngine *engine = qmlEngine(that); - engine->collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(*engine); } } @@ -412,7 +412,7 @@ QObject *QObjectContainer::children_at(QQmlListProperty<QObject> *prop, qsizetyp void QObjectContainer::children_clear(QQmlListProperty<QObject> *prop) { QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); - foreach (QObject *c, that->dataChildren) + for (QObject *c : std::as_const(that->dataChildren)) QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); that->dataChildren.clear(); } @@ -436,6 +436,44 @@ void ClassWithQProperty2::callback() // Q_UNUSED(this->value.value()); // force evaluation } +ListPropertyAssignment_Gadget::ListPropertyAssignment_Gadget() { } + +QStringList ListPropertyAssignment_Gadget::gadgetStringList() const +{ + return m_gadgetStringList; +} + +void ListPropertyAssignment_Gadget::setGadgetStringList(const QStringList &list) +{ + if (m_gadgetStringList == list) + return; + m_gadgetStringList = list; +} + +QVariantList ListPropertyAssignment_Gadget::gadgetVariantList() const +{ + return m_gadgetVariantList; +} + +void ListPropertyAssignment_Gadget::setGadgetVariantList(const QVariantList &list) +{ + if (m_gadgetVariantList == list) + return; + m_gadgetVariantList = list; +} + +ListPropertyAssignment_Object::ListPropertyAssignment_Object(QObject *parent) + : QObject{ parent } { } + +void ListPropertyAssignment_Object::setQobjectStringList(const QStringList &newList) +{ + if (m_qobjectStringList == newList) + return; + m_qobjectStringList = newList; +} + +bool MetaCallInterceptor::didGetObjectDestroyedCallback = false; + void registerTypes() { qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObjectAlias"); @@ -542,6 +580,15 @@ void registerTypes() qmlRegisterType<Receiver>("Qt.test", 1,0, "Receiver"); qmlRegisterType<Sender>("Qt.test", 1,0, "Sender"); qmlRegisterTypesAndRevisions<ReadOnlyBindable>("Qt.test", 1); + qmlRegisterTypesAndRevisions<ResettableGadgetHolder>("Qt.test", 1); + + qmlRegisterTypesAndRevisions<ListPropertyAssignment_Gadget>("Qt.test", 1); + qmlRegisterTypesAndRevisions<ListPropertyAssignment_Object>("Qt.test", 1); + + qmlRegisterTypesAndRevisions<SingletonRegistrationWrapper>("Qt.test", 1); + + qmlRegisterExtendedType<TypeWithCustomMetaObject, TypeToTriggerProxyMetaObject>( + "Qt.test", 1,0, "TypeWithCustomMetaObject"); } #include "testtypes.moc" diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index ff9dda36d1..cc20437fff 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H @@ -31,6 +31,7 @@ #include <private/qqmlengine_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlcomponentattached_p.h> class MyQmlAttachedObject : public QObject { @@ -243,7 +244,7 @@ public slots: void myinvokable(MyQmlObject *o) { myinvokableObject = o; } void variantMethod(const QVariant &v) { m_variant = v; } void qjsvalueMethod(const QJSValue &v) { m_qjsvalue = v; } - void v8function(QQmlV4Function*); + void v8function(QQmlV4FunctionPtr); void registeredFlagMethod(Qt::MouseButtons v) { m_buttons = v; } QString slotWithReturnValue(const QString &arg) { return arg; } int resetCount() { return m_resetCount; } @@ -862,6 +863,17 @@ struct NonRegisteredType struct CompletelyUnknown; +class SingletonWithEnum : public QObject +{ + Q_OBJECT + Q_ENUMS(TestEnum) +public: + enum TestEnum { + TestValue = 42, + TestValue_MinusOne = -1 + }; +}; + class MyInvokableObject : public MyInvokableBaseObject { Q_OBJECT @@ -919,7 +931,7 @@ public: Q_INVOKABLE void method_unknown(NonRegisteredType) { invoke(28); } - Q_INVOKABLE void method_overload2(QQmlV4Function *v) + Q_INVOKABLE void method_overload2(QQmlV4FunctionPtr v) { invoke(31); QV4::Scope scope(v->v4engine()); @@ -955,12 +967,43 @@ public: invoke(40); m_actuals << f; } + Q_INVOKABLE void method_qobject(QObject *o) { invoke(41); m_actuals << QVariant::fromValue(o); } + Q_INVOKABLE QQmlComponent *someComponent() { return &m_someComponent; } + Q_INVOKABLE void method_component(QQmlComponent *c) + { + invoke(42); + m_actuals << QVariant::fromValue(c); + } + + Q_INVOKABLE MyTypeObject *someTypeObject() { return &m_someTypeObject; } + Q_INVOKABLE void method_component(MyTypeObject *c) + { + invoke(43); + m_actuals << QVariant::fromValue(c); + } + + Q_INVOKABLE void method_component(const QUrl &c) + { + invoke(44); + m_actuals << QVariant::fromValue(c); + } + + Q_INVOKABLE void method_typeWrapper(QQmlComponentAttached *attached) + { + m_actuals << QVariant::fromValue(attached); + } + + Q_INVOKABLE void method_typeWrapper(SingletonWithEnum *singleton) + { + m_actuals << QVariant::fromValue(singleton); + } + private: friend class MyInvokableBaseObject; void invoke(int idx) { if (m_invoked != -1) m_invokedError = true; m_invoked = idx;} @@ -969,6 +1012,8 @@ private: QVariantList m_actuals; QFont m_someFont; + QQmlComponent m_someComponent; + MyTypeObject m_someTypeObject; public: Q_SIGNALS: @@ -1807,17 +1852,6 @@ public: QML_DECLARE_TYPEINFO(FallbackBindingsTypeObject, QML_HAS_ATTACHED_PROPERTIES) QML_DECLARE_TYPEINFO(FallbackBindingsTypeDerived, QML_HAS_ATTACHED_PROPERTIES) -class SingletonWithEnum : public QObject -{ - Q_OBJECT - Q_ENUMS(TestEnum) -public: - enum TestEnum { - TestValue = 42, - TestValue_MinusOne = -1 - }; -}; - // Like QtObject, but with default property class QObjectContainer : public QObject { @@ -2000,6 +2034,161 @@ public: QBindable<int> bindableX() const { return &_xProp; } }; +class ResettableGadget +{ + Q_GADGET + Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue) + + qreal m_value = 0; + +public: + qreal value() const { return m_value; } + void setValue(qreal val) { m_value = val; } + void resetValue() { m_value = 42; } +}; + +class ResettableGadgetHolder : public QObject { + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(ResettableGadget g READ g WRITE setG NOTIFY gChanged) + ResettableGadget m_g; + +signals: + void gChanged(); + +public: + ResettableGadget g() const { return m_g; } + void setG(ResettableGadget newG) + { + if (m_g.value() == newG.value()) + return; + m_g = newG; + Q_EMIT gChanged(); + } +}; + +class ListPropertyAssignment_Gadget +{ + Q_GADGET + Q_PROPERTY(QStringList gadgetStringList READ gadgetStringList WRITE setGadgetStringList) + Q_PROPERTY(QVariantList gadgetVariantList READ gadgetVariantList WRITE setGadgetVariantList) + QML_VALUE_TYPE(listPropertyAssignment_Gadget) +public: + ListPropertyAssignment_Gadget(); + QStringList gadgetStringList() const; + void setGadgetStringList(const QStringList &list); + + QVariantList gadgetVariantList() const; + void setGadgetVariantList(const QVariantList &list); + +private: + QStringList m_gadgetStringList; + QVariantList m_gadgetVariantList; +}; + +class ListPropertyAssignment_Object : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QStringList qobjectStringList READ qobjectStringList WRITE setQobjectStringList) +public: + explicit ListPropertyAssignment_Object(QObject *parent = nullptr); + + QStringList qobjectStringList() const { return m_qobjectStringList; } + + void setQobjectStringList(const QStringList &newList); + +private: + QStringList m_qobjectStringList; +}; + +class SingletonBase : public QObject { + Q_OBJECT + +public: + Q_INVOKABLE virtual void trackPage(const QString&) {} + Q_INVOKABLE virtual void trackPage(const QString&, const QVariantMap&) {} + + bool m_okay = false; +}; + +class SingletonImpl : public SingletonBase { + Q_OBJECT + +public: + Q_INVOKABLE virtual void trackPage(const QString&) override {} + Q_INVOKABLE virtual void trackPage(const QString&, const QVariantMap&) override + { + m_okay = true; + } +}; + +class SingletonRegistrationWrapper { + Q_GADGET + QML_FOREIGN(SingletonBase) + QML_NAMED_ELEMENT(SingletonInheritanceTest) + QML_SINGLETON + +public: + static SingletonBase* create(QQmlEngine*, QJSEngine*) { + return new SingletonImpl(); + } + +private: + SingletonRegistrationWrapper() = default; +}; + +class MetaCallInterceptor : public QObject, public QDynamicMetaObjectData +{ + Q_OBJECT +public: + MetaCallInterceptor() + { + didGetObjectDestroyedCallback = false; + } + + void objectDestroyed(QObject *object) override + { + didGetObjectDestroyedCallback = true; + + // Deletes this meta object + QDynamicMetaObjectData::objectDestroyed(object); + } + + QMetaObject *toDynamicMetaObject(QObject *) override + { + return const_cast<QMetaObject *>(&MetaCallInterceptor::staticMetaObject); + } + + int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override + { + return o->qt_metacall(call, idx, argv); + } + + static bool didGetObjectDestroyedCallback; +}; + +struct TypeToTriggerProxyMetaObject +{ + Q_GADGET +}; + +class TypeWithCustomMetaObject : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(TypeWithCustomMetaObject) + QML_EXTENDED_NAMESPACE(TypeToTriggerProxyMetaObject) + +public: + TypeWithCustomMetaObject() + { + auto *p = QObjectPrivate::get(this); + Q_ASSERT(!p->metaObject); + p->metaObject = new MetaCallInterceptor; + } +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index edb1e9ba80..2d124da279 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2017 Crimson AS <info@crimson.no> // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <QtTest/QtTest> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlengine.h> @@ -27,6 +28,8 @@ #include <private/qqmlabstractbinding_p.h> #include <private/qqmlvaluetypeproxybinding_p.h> #include <QtCore/private/qproperty_p.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/private/qquickitem_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/testhttpserver_p.h> @@ -376,6 +379,8 @@ private slots: void qpropertyBindingHandlesUndefinedCorrectly(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly(); + void qpropertyBindingRestoresObserverAfterReset(); + void qpropertyBindingObserverCorrectlyLinkedAfterReset(); void hugeRegexpQuantifiers(); void singletonTypeWrapperLookup(); void getThisObject(); @@ -396,6 +401,7 @@ private slots: void sequenceConversionMethod(); void proxyIteration(); void proxyHandlerTraps(); + void lookupsDoNotBypassProxy(); void gcCrashRegressionTest(); void cmpInThrows(); void frozenQObject(); @@ -416,6 +422,13 @@ private slots: void doNotCrashOnReadOnlyBindable(); + void resetGadget(); + void assignListPropertyByIndexOnGadget(); + + void methodCallOnDerivedSingleton(); + + void proxyMetaObject(); + private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt); @@ -432,14 +445,6 @@ private: } }; -static void gc(QQmlEngine &engine) -{ - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); -} - - tst_qqmlecmascript::tst_qqmlecmascript() : QQmlDataTest(QT_QMLTEST_DATADIR) { @@ -3127,11 +3132,14 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->invoked(), -1); QCOMPARE(o->actuals().size(), 0); - o->reset(); - QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)")); - QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), -1); - QCOMPARE(o->actuals().size(), 0); + // This fails if the QtQml module is loaded but works if it's not. + // If QtQml is loaded, QPointF is a structured value type that can be created from any object. + // + // o->reset(); + // QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)")); + // QCOMPARE(o->error(), false); + // QCOMPARE(o->invoked(), -1); + // QCOMPARE(o->actuals().size(), 0); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", QV4::Primitive::undefinedValue())); @@ -3186,6 +3194,17 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments } + { + o->reset(); + QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs3.qml")); + QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) }; + QVERIFY(root); + QCOMPARE(o->error(), false); + QCOMPARE(o->actuals().size(), 2); + QCOMPARE(o->actuals().at(0).metaType(), QMetaType::fromType<QQmlComponentAttached *>()); + QCOMPARE(o->actuals().at(1).metaType(), QMetaType::fromType<SingletonWithEnum *>()); + } + o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); @@ -3517,6 +3536,27 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), -1); QCOMPARE(o->actuals(), QVariantList()); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_component(object.someComponent())", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 42); + QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someComponent())); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_component(object.someTypeObject())", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 43); + QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someTypeObject())); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_component('qrc:/somewhere/else')", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 44); + QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(QUrl("qrc:/somewhere/else"))); } void tst_qqmlecmascript::resolveClashingProperties() @@ -3815,6 +3855,82 @@ void tst_qqmlecmascript::scriptConnect() QScopedPointer<QObject> root { component.create() }; QVERIFY2(root, qPrintable(component.errorString())); } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.8.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QCOMPARE(obj.data()->property("count"), 0); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + + QMetaObject::invokeMethod(obj.data(), "itemDestroy"); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data()); + + QCOMPARE(object->property("a"), 0); + + QMetaObject::invokeMethod(object, "someSignal"); + QCOMPARE(object->property("a"), 1); + + QMetaObject::invokeMethod(object, "destroyObj", Qt::DirectConnection); + QApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QApplication::processEvents(); + + QMetaObject::invokeMethod(object, "someSignal"); + + QCOMPARE(object->property("a"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + engine.clearSingletons(); + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.deletion.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(!obj.isNull()); + + QCOMPARE(obj->property("a"), 0); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj->property("a"), 1); + + QCOMPARE(obj->property("b"), 0); + QMetaObject::invokeMethod(obj.data(), "destroyObj", Qt::DirectConnection); + + QTRY_COMPARE(obj->property("b"), 1); + QCOMPARE(obj->property("a"), 1); + } } void tst_qqmlecmascript::scriptDisconnect() @@ -3895,6 +4011,60 @@ void tst_qqmlecmascript::scriptDisconnect() emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); QCOMPARE(object->property("test").toInt(), 3); } + + { + QQmlComponent component(&engine, testFileUrl("scriptDisconnect.5.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QCOMPARE(obj.data()->property("count"), 0); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + + QMetaObject::invokeMethod(obj.data(), "disconnectSignal"); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data()); + + QCOMPARE(object->property("a"), 0); + + QMetaObject::invokeMethod(object, "someSignal"); + QCOMPARE(object->property("a"), 1); + + QMetaObject::invokeMethod(object, "disconnectSignal", Qt::DirectConnection); + + QMetaObject::invokeMethod(object, "someSignal"); + + QCOMPARE(object->property("a"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + + QMetaObject::invokeMethod(obj.data(), "disconnectSingleton", Qt::DirectConnection); + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + } } class OwnershipObject : public QObject @@ -3923,10 +4093,7 @@ void tst_qqmlecmascript::ownership() QScopedPointer<QObject> object(component.create(context.data())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object.isNull()); } @@ -3940,10 +4107,7 @@ void tst_qqmlecmascript::ownership() QScopedPointer<QObject> object(component.create(context.data())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object != nullptr); } @@ -4019,9 +4183,7 @@ void tst_qqmlecmascript::ownershipCustomReturnValue() QVERIFY(source.value != nullptr); } - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(source.value.isNull()); } @@ -4052,10 +4214,7 @@ void tst_qqmlecmascript::ownershipRootObject() QScopedPointer<QObject> object(component.create(context.data())); QVERIFY2(object, qPrintable(component.errorString())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object != nullptr); } @@ -4080,10 +4239,7 @@ void tst_qqmlecmascript::ownershipConsistency() QScopedPointer<QObject> object(component.create(context.data())); QVERIFY2(object, qPrintable(component.errorString())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object != nullptr); } @@ -4613,6 +4769,7 @@ void tst_qqmlecmascript::verifyContextLifetime(const QQmlRefPointer<QQmlContextD } ctxt->engine()->collectGarbage(); + QTRY_VERIFY(gcDone(ctxt->engine())); qml = scripts->get(i); newContext = qml ? qml->getContext() : nullptr; QCOMPARE(scriptContext.data(), newContext.data()); @@ -5257,6 +5414,7 @@ void tst_qqmlecmascript::propertyChangeSlots() QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); + QCOMPARE(object->property("changeCount"), 15); // ensure that invalid property names fail properly. QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); @@ -5272,20 +5430,6 @@ void tst_qqmlecmascript::propertyChangeSlots() QCOMPARE(e2.errors().at(0).toString(), expectedErrorString); object.reset(e2.create()); QVERIFY(!object); - - QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml")); - expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\""); - QCOMPARE(e3.errors().at(0).toString(), expectedErrorString); - object.reset(e3.create()); - QVERIFY(!object); - - QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml")); - expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\""); - QCOMPARE(e4.errors().at(0).toString(), expectedErrorString); - object.reset(e4.create()); - QVERIFY(!object); } void tst_qqmlecmascript::propertyVar_data() @@ -5453,7 +5597,9 @@ void tst_qqmlecmascript::propertyVarOwnership() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "createComponent"); - engine.collectGarbage(); + // This test only works if we don't deliver the pending delete later event + // that collectGarbage will post before calling runTest + gc(engine, GCFlags::DontSendPostedEvents); QMetaObject::invokeMethod(object.data(), "runTest"); QCOMPARE(object->property("test").toBool(), true); } @@ -5469,8 +5615,7 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "assignCircular"); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. - QCoreApplication::processEvents(); + gc(engine); QObject *rootObject = object->property("vp").value<QObject*>(); QVERIFY(rootObject != nullptr); QObject *childObject = rootObject->findChild<QObject*>("text"); @@ -5479,6 +5624,8 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership() QCOMPARE(childObject->property("textCanary").toInt(), 10); // Creates a reference to a constructed QObject: QMetaObject::invokeMethod(childObject, "constructQObject"); + // Don't send delete later events yet, we do it manually later + gc(engine, GCFlags::DontSendPostedEvents); QPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events. QVERIFY(!qobjectGuard.isNull()); QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. @@ -5497,8 +5644,7 @@ void tst_qqmlecmascript::propertyVarReparent() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "assignVarProp"); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. - QCoreApplication::processEvents(); + gc(engine); QObject *rect = object->property("vp").value<QObject*>(); QObject *text = rect->findChild<QObject*>("textOne"); QObject *text2 = rect->findChild<QObject*>("textTwo"); @@ -5512,6 +5658,7 @@ void tst_qqmlecmascript::propertyVarReparent() QCOMPARE(text2->property("textCanary").toInt(), 12); // now construct an image which we will reparent. QMetaObject::invokeMethod(text2, "constructQObject"); + gc(engine, GCFlags::DontSendPostedEvents); QObject *image = text2->property("vp").value<QObject*>(); QPointer<QObject> imageGuard(image); QVERIFY(!imageGuard.isNull()); @@ -5539,8 +5686,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "assignVarProp"); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. - QCoreApplication::processEvents(); + gc(engine); QObject *rect = object->property("vp").value<QObject*>(); QObject *text = rect->findChild<QObject*>("textOne"); QObject *text2 = rect->findChild<QObject*>("textTwo"); @@ -5554,6 +5700,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext() QCOMPARE(text2->property("textCanary").toInt(), 12); // now construct an image which we will reparent. QMetaObject::invokeMethod(text2, "constructQObject"); + gc(engine); QObject *image = text2->property("vp").value<QObject*>(); QPointer<QObject> imageGuard(image); QVERIFY(!imageGuard.isNull()); @@ -5780,9 +5927,7 @@ void tst_qqmlecmascript::handleReferenceManagement() gc(hrmEngine); QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference object.reset(); - hrmEngine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(hrmEngine); QCOMPARE(dtorCount, 3); } @@ -5799,9 +5944,7 @@ void tst_qqmlecmascript::handleReferenceManagement() gc(hrmEngine); QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive. object.reset(); - hrmEngine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(hrmEngine); QCOMPARE(dtorCount, 3); } @@ -7892,7 +8035,7 @@ public: void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr) { - QV4::QObjectWrapper::wrap(v4, this); + (void) QV4::QObjectWrapper::wrap(v4, this); // Intentionally drop the wrapper QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); this->resultPtr = resultPtr; @@ -7980,12 +8123,9 @@ void tst_qqmlecmascript::onDestructionViaGC() v4->memoryManager->allocate<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult); } gc(engine); - + QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared"); QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC"); - QVERIFY2(!sentinelResult, "The weak value was cleared on first GC run"); - QVERIFY2(!weakRef->isNullOrUndefined(), "The weak value was cleared on first GC run"); - gc(engine); - QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared on second gc run"); + QVERIFY2(sentinelResult, "The weak reference was not cleared properly"); } struct EventProcessor : public QObject @@ -8078,7 +8218,9 @@ void tst_qqmlecmascript::qqmldataDestroyed() QVERIFY2(object, qPrintable(c.errorString())); // now gc causing the collection of the dynamically constructed object. engine.collectGarbage(); + QTRY_VERIFY(gcDone(&engine)); engine.collectGarbage(); + QTRY_VERIFY(gcDone(&engine)); // now process events to allow deletion (calling qqmldata::destroyed()) QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); @@ -9389,6 +9531,32 @@ void tst_qqmlecmascript::qpropertyBindingHandlesUndefinedWithoutResetCorrectly() QCOMPARE(root->property("value2").toInt(), 2); } +void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreObserverAfterReset.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QTRY_COMPARE(o->property("height").toDouble(), 60.0); + QVERIFY(o->property("steps").toInt() > 3); +} + +void tst_qqmlecmascript::qpropertyBindingObserverCorrectlyLinkedAfterReset() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("qpropertyResetCorrectlyLinked.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + std::unique_ptr<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("width"), 200); + auto item = qobject_cast<QQuickItem *>(o.get()); + auto itemPriv = QQuickItemPrivate::get(item); + QBindingStorage *storage = qGetBindingStorage(itemPriv); + QPropertyBindingDataPointer ptr { storage->bindingData(&itemPriv->width) }; + QCOMPARE(ptr.observerCount(), 1); +} + void tst_qqmlecmascript::hugeRegexpQuantifiers() { QJSEngine engine; @@ -9987,6 +10155,17 @@ void tst_qqmlecmascript::proxyHandlerTraps() QVERIFY(value.isString() && value.toString() == QStringLiteral("SUCCESS")); } +void tst_qqmlecmascript::lookupsDoNotBypassProxy() +{ + QQmlEngine engine; + // we need a component to have a proper compilation to byte code; + // otherwise, we don't actually end up with lookups + QQmlComponent comp(&engine, testFileUrl("lookupsDoNotBypassProxy.qml")); + QVERIFY(comp.isReady()); + std::unique_ptr<QObject> obj { comp.create() }; + QCOMPARE(obj->property("result").toInt(), 3); +} + void tst_qqmlecmascript::cmpInThrows() { QJSEngine engine; @@ -10027,6 +10206,9 @@ public: Q_INVOKABLE void triggerSignal() { emit fooMember2Emitted(&m_fooMember2); } + Q_INVOKABLE const FrozenFoo *getConst() { return createFloating(); } + Q_INVOKABLE FrozenFoo *getNonConst() { return createFloating(); } + FrozenFoo *fooMember() { return &m_fooMember; } FrozenFoo *fooMember2() { return &m_fooMember2; } @@ -10036,6 +10218,16 @@ signals: private: const FrozenFoo *fooMemberConst() const { return &m_fooMember; } + FrozenFoo *createFloating() + { + if (!m_floating) { + m_floating = new FrozenFoo; + m_floating->setObjectName(objectName()); + } + return m_floating; + } + + FrozenFoo *m_floating = nullptr; FrozenFoo m_fooMember; FrozenFoo m_fooMember2; }; @@ -10058,6 +10250,17 @@ void tst_qqmlecmascript::frozenQObject() QVERIFY(frozenObjects->property("caughtSignal").toBool()); QCOMPARE(frozenObjects->fooMember()->name(), QStringLiteral("Jane")); QCOMPARE(frozenObjects->fooMember2()->name(), QStringLiteral("Jane")); + + QQmlComponent component3(&engine, testFileUrl("frozenQObject3.qml")); + QScopedPointer<QObject> root3(component3.create()); + QCOMPARE(root3->objectName(), QLatin1String("a/b")); + QVERIFY(root3->property("objConst").value<QObject *>()); + QVERIFY(root3->property("objNonConst").value<QObject *>()); + + QTRY_VERIFY(root3->property("gcs").toInt() > 8); + + QVERIFY(root3->property("objConst").value<QObject *>()); + QVERIFY(root3->property("objNonConst").value<QObject *>()); } struct ConstPointer : QObject @@ -10373,6 +10576,80 @@ void tst_qqmlecmascript::doNotCrashOnReadOnlyBindable() QCOMPARE(o->property("x").toInt(), 7); } +void tst_qqmlecmascript::resetGadget() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("resetGadget.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + auto resettableGadgetHolder = qobject_cast<ResettableGadgetHolder *>(o.get()); + QVERIFY(resettableGadgetHolder); + QCOMPARE(resettableGadgetHolder->g().value(), 0); + resettableGadgetHolder->setProperty("trigger", QVariant::fromValue(true)); + QCOMPARE(resettableGadgetHolder->g().value(), 42); +} + +void tst_qqmlecmascript::assignListPropertyByIndexOnGadget() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFile("AssignListPropertyByIndexOnGadget.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + const auto &gadget = o->property("gadget").value<ListPropertyAssignment_Gadget>(); + const auto *object = o->property("object").value<ListPropertyAssignment_Object *>(); + QVERIFY(object); + + QStringList expected{ "Completely new Element", "Element2", "Element3" }; + QVariantList variants { + u"Completely new Element"_s, + u"foo"_s, + QVariant::fromValue<std::nullptr_t>(nullptr), + QVariant::fromValue<bool>(true) + }; + + QCOMPARE(gadget.gadgetStringList(), expected); + QCOMPARE(gadget.gadgetVariantList(), variants); + QCOMPARE(object->qobjectStringList(), expected); +} + +void tst_qqmlecmascript::methodCallOnDerivedSingleton() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFile("methodCallOnDerivedSingleton.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + auto singleton = engine.singletonInstance<SingletonBase *>("Qt.test", "SingletonInheritanceTest"); + QVERIFY(singleton); + QVERIFY(singleton->m_okay); +} + +void tst_qqmlecmascript::proxyMetaObject() +{ + // Verify that TypeWithCustomMetaObject, that extends another type, + // thereby triggering a QQmlProxyMetaObject, is still proxied the + // QDynamicMetaObjectData::objectDestroyed callback. + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import QtQuick + import QtQml + import Qt.test + Rectangle { + TypeWithCustomMetaObject {} + } + )", QUrl("testData")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QVERIFY(!MetaCallInterceptor::didGetObjectDestroyedCallback); + o.reset(nullptr); + QVERIFY(MetaCallInterceptor::didGetObjectDestroyedCallback); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt index 0869fe7ccc..9745f31bdb 100644 --- a/tests/auto/qml/qqmlengine/CMakeLists.txt +++ b/tests/auto/qml/qqmlengine/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlengine Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlengine LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -34,6 +40,7 @@ qt_add_qml_module(tst_qqmlengine_qml SOURCES "declarativelyregistered.h" "declarativelyregistered.cpp" + "variantlistQJsonConversion.h" RESOURCE_PREFIX "/" OUTPUT_DIRECTORY diff --git a/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml b/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml new file mode 100644 index 0000000000..596ab10ee7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml @@ -0,0 +1,19 @@ +import QtQuick + +Item { + visible: false + + property int test: 1 + + Component { + id: comp + Item { + width: { width = test * 100 } + } + } + + Loader { + sourceComponent: comp + width: 100 + } +} diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml new file mode 100644 index 0000000000..3be706ee29 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property string s: SingletonA.name +} diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml new file mode 100644 index 0000000000..dc3bfd23fd --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml @@ -0,0 +1,11 @@ +pragma Singleton +import QtQuick + +Item { + readonly property string name: "SingletonA" + + readonly property TestItem itemA: TestItem{} + + property TestItem crossRef: SingletonB.itemB + property int testItemInt: crossRef.i +} diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml new file mode 100644 index 0000000000..7f0335ee89 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml @@ -0,0 +1,11 @@ +pragma Singleton +import QtQuick + +Item { + readonly property string name: "SingletonB" + + readonly property TestItem itemB: TestItem{} + + property TestItem crossRef: SingletonA.itemA + property int testItemInt: crossRef.i +} diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml new file mode 100644 index 0000000000..81d931f14c --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property int i: 3 +} diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir new file mode 100644 index 0000000000..8bf691d10d --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir @@ -0,0 +1,3 @@ +module Module +singleton SingletonA 1.0 SingletonA.qml +singleton SingletonB 1.0 SingletonB.qml diff --git a/tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml b/tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml new file mode 100644 index 0000000000..a16e9c483f --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml @@ -0,0 +1,7 @@ +import QtQml +import i18ntest + +QtObject { + property I18nAware aware: I18nAware {} + property string result: aware.text +} diff --git a/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs b/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs index fd1080fccb..8f5bdde82a 100644 --- a/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs +++ b/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import { name } from "info.mjs"; diff --git a/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml b/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml index ae51210944..ffcb0e42ce 100644 --- a/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml +++ b/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml diff --git a/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml b/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml new file mode 100644 index 0000000000..fd0820a3c5 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml @@ -0,0 +1,18 @@ +import QtQuick +import OnlyDeclarative + +Item { + + MiscUtils { + id: miscUtils + } + + Component.onCompleted: { + const varlist = miscUtils.createVariantList(); + const obj = { test: varlist }; + const listProperty = miscUtils.createQmlListProperty(); + miscUtils.logArray(varlist); + miscUtils.logObject(obj); + miscUtils.logArray(listProperty); + } +} diff --git a/tests/auto/qml/qqmlengine/declarativelyregistered.cpp b/tests/auto/qml/qqmlengine/declarativelyregistered.cpp index 9893fa0311..4a469c4795 100644 --- a/tests/auto/qml/qqmlengine/declarativelyregistered.cpp +++ b/tests/auto/qml/qqmlengine/declarativelyregistered.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "declarativelyregistered.h" diff --git a/tests/auto/qml/qqmlengine/declarativelyregistered.h b/tests/auto/qml/qqmlengine/declarativelyregistered.h index 98f7250d76..edb783e3aa 100644 --- a/tests/auto/qml/qqmlengine/declarativelyregistered.h +++ b/tests/auto/qml/qqmlengine/declarativelyregistered.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DECLARATIVELYREGISTERED_H #define DECLARATIVELYREGISTERED_H diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 895513d822..3c25d29dfb 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QQmlEngine> #include <QQmlContext> @@ -36,8 +36,10 @@ public: private slots: void initTestCase() override; void rootContext(); +#if QT_CONFIG(qml_network) void networkAccessManager(); void synchronousNetworkAccessManager(); +#endif void baseUrl(); void contextForObject(); void offlineStoragePath(); @@ -67,6 +69,7 @@ private slots: void cachedGetterLookup_qtbug_75335(); void createComponentOnSingletonDestruction(); void uiLanguage(); + void markCurrentFunctionAsTranslationBinding(); void executeRuntimeFunction(); void captureQProperty(); void listWrapperAsListReference(); @@ -77,6 +80,9 @@ private slots: void qtNamespaceInQtObject(); void nativeModuleImport(); void lockedRootObject(); + void crossReferencingSingletonsDeletion(); + void bindingInstallUseAfterFree(); + void variantListQJsonConversion(); public slots: QObject *createAQObjectForOwnershipTest () @@ -149,6 +155,7 @@ void tst_qqmlengine::rootContext() QVERIFY(!engine.rootContext()->parentContext()); } +#if QT_CONFIG(qml_network) class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { public: @@ -164,21 +171,19 @@ public: void tst_qqmlengine::networkAccessManager() { - QQmlEngine *engine = new QQmlEngine; + std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>(); // Test QQmlEngine created manager QPointer<QNetworkAccessManager> manager = engine->networkAccessManager(); QVERIFY(manager != nullptr); - delete engine; // Test factory created manager - engine = new QQmlEngine; + engine.reset(new QQmlEngine); NetworkAccessManagerFactory factory; engine->setNetworkAccessManagerFactory(&factory); QCOMPARE(engine->networkAccessManagerFactory(), &factory); QNetworkAccessManager *engineNam = engine->networkAccessManager(); // calls NetworkAccessManagerFactory::create() QCOMPARE(engineNam, factory.manager); - delete engine; } class ImmediateReply : public QNetworkReply { @@ -226,7 +231,7 @@ void tst_qqmlengine::synchronousNetworkAccessManager() // reply is finished, so should not be in loading state. QVERIFY(!c.isLoading()); } - +#endif void tst_qqmlengine::baseUrl() { @@ -267,7 +272,7 @@ void tst_qqmlengine::baseUrl() void tst_qqmlengine::contextForObject() { - QQmlEngine *engine = new QQmlEngine; + std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>(); // Test null-object QVERIFY(!QQmlEngine::contextForObject(nullptr)); @@ -294,7 +299,7 @@ void tst_qqmlengine::contextForObject() QCOMPARE(QQmlEngine::contextForObject(&object), engine->rootContext()); // Delete context - delete engine; engine = nullptr; + engine.reset(); QVERIFY(!QQmlEngine::contextForObject(&object)); } @@ -372,10 +377,9 @@ void tst_qqmlengine::clearComponentCache() // Test "test" property { QQmlComponent component(&engine, fileUrl); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get() != nullptr); QCOMPARE(obj->property("test").toInt(), 10); - delete obj; } // Modify qml file @@ -395,10 +399,9 @@ void tst_qqmlengine::clearComponentCache() // Test cache hit { QQmlComponent component(&engine, fileUrl); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get() != nullptr); QCOMPARE(obj->property("test").toInt(), 10); - delete obj; } // Clear cache @@ -407,10 +410,9 @@ void tst_qqmlengine::clearComponentCache() // Test cache refresh { QQmlComponent component(&engine, fileUrl); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get() != nullptr); QCOMPARE(obj->property("test").toInt(), 11); - delete obj; } // Regular Synchronous loading will leave us with an event posted @@ -437,7 +439,7 @@ public: // 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(); + gc(*engine); engine->trimComponentCache(); } @@ -496,22 +498,25 @@ void tst_qqmlengine::trimComponentCache_data() // empty apart from their inherited elements, and those that define new properties. // For each there are five types of composition: extension, aggregation, // aggregation via component, property and object-created-via-transient-component. - foreach (const QString &test, (QStringList() << "EmptyComponent" - << "VMEComponent" - << "EmptyExtendEmptyComponent" - << "VMEExtendEmptyComponent" - << "EmptyExtendVMEComponent" - << "VMEExtendVMEComponent" - << "EmptyAggregateEmptyComponent" - << "VMEAggregateEmptyComponent" - << "EmptyAggregateVMEComponent" - << "VMEAggregateVMEComponent" - << "EmptyPropertyEmptyComponent" - << "VMEPropertyEmptyComponent" - << "EmptyPropertyVMEComponent" - << "VMEPropertyVMEComponent" - << "VMETransientEmptyComponent" - << "VMETransientVMEComponent")) { + const QStringList components = { + "EmptyComponent", + "VMEComponent", + "EmptyExtendEmptyComponent", + "VMEExtendEmptyComponent", + "EmptyExtendVMEComponent", + "VMEExtendVMEComponent", + "EmptyAggregateEmptyComponent", + "VMEAggregateEmptyComponent", + "EmptyAggregateVMEComponent", + "VMEAggregateVMEComponent", + "EmptyPropertyEmptyComponent", + "VMEPropertyEmptyComponent", + "EmptyPropertyVMEComponent", + "VMEPropertyVMEComponent", + "VMETransientEmptyComponent", + "VMETransientVMEComponent", + }; + for (const QString &test : components) { // For these cases, we first test that the component instance keeps the components // referenced, and then that the instantiated object keeps the components referenced for (int i = 1; i <= 2; ++i) { @@ -903,7 +908,7 @@ void tst_qqmlengine::qtqmlModule() QFETCH(QString, expectedError); QFETCH(QStringList, expectedWarnings); - foreach (const QString &w, expectedWarnings) + for (const QString &w : std::as_const(expectedWarnings)) QTest::ignoreMessage(QtWarningMsg, qPrintable(w)); QQmlEngine e; @@ -1272,9 +1277,9 @@ void tst_qqmlengine::singletonInstance() { // deleted object - auto dayfly = new Dayfly{}; - auto id = qmlRegisterSingletonInstance("Vanity", 1, 0, "Dayfly", dayfly); - delete dayfly; + auto dayfly = std::make_unique<Dayfly>(); + auto id = qmlRegisterSingletonInstance("Vanity", 1, 0, "Dayfly", dayfly.get()); + dayfly.reset(); QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: The registered singleton has already been deleted. Ensure that it outlives the engine."); QObject *instance = engine.singletonInstance<QObject*>(id); QVERIFY(!instance); @@ -1378,6 +1383,35 @@ void tst_qqmlengine::uiLanguage() } } +class I18nAwareClass : public QObject { + Q_OBJECT + QML_NAMED_ELEMENT(I18nAware) + + Q_PROPERTY(QString text READ text NOTIFY textChanged) +signals: + void textChanged(); +public: + int counter = 0; + + QString text() + { + if (auto engine = qmlEngine(this)) + engine->markCurrentFunctionAsTranslationBinding(); + return QLatin1String("Hello, %1").arg(QString::number(counter++)); + } +}; + +void tst_qqmlengine::markCurrentFunctionAsTranslationBinding() +{ + QQmlEngine engine; + qmlRegisterTypesAndRevisions<I18nAwareClass>("i18ntest", 1); + QQmlComponent comp(&engine, testFileUrl("markCurrentFunctionAsTranslationBinding.qml")); + std::unique_ptr<QObject> root { comp.create() }; + QCOMPARE(root->property("result"), "Hello, 0"); + engine.retranslate(); + QCOMPARE(root->property("result"), "Hello, 1"); +} + void tst_qqmlengine::executeRuntimeFunction() { QQmlEngine engine; @@ -1683,6 +1717,42 @@ void tst_qqmlengine::lockedRootObject() QCOMPARE(o->property("defineProperty2").toBool(), false); } +void tst_qqmlengine::crossReferencingSingletonsDeletion() +{ + QQmlEngine engine; + engine.addImportPath(testFileUrl("crossReferencingSingletonsDeletion").url()); + QQmlComponent c(&engine, testFileUrl("crossReferencingSingletonsDeletion/Module/Main.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + std::unique_ptr<QObject> o{ c.create() }; + QVERIFY(o); + QCOMPARE(o->property("s").toString(), "SingletonA"); +} + +void tst_qqmlengine::bindingInstallUseAfterFree() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindingInstallUseAfterFree.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + std::unique_ptr<QObject> o{ c.create() }; + QVERIFY(o); +} + +void tst_qqmlengine::variantListQJsonConversion() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("variantListQJsonConversion.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage(QtMsgType::QtDebugMsg, R"(["cpp","variant","list"])"); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, R"({"test":["cpp","variant","list"]})"); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, + R"([{"objectName":"o0"},{"objectName":"o1"},{"objectName":"o2"}])"); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" diff --git a/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h b/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h new file mode 100644 index 0000000000..edf2174a18 --- /dev/null +++ b/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h @@ -0,0 +1,53 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef VARIANTLIST_QJSON_CONVERSION_HPP +#define VARIANTLIST_QJSON_CONVERSION_HPP + +#include "qqmlintegration.h" +#include <QJsonObject> +#include <QJsonArray> +#include <QObject> +#include <QJsonDocument> +#include <QDebug> +#include <private/qjsvalue_p.h> +#include <private/qqmllistwrapper_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4jsonobject_p.h> + +class MiscUtils : public QObject +{ + Q_OBJECT + QML_ELEMENT + +public: + Q_INVOKABLE QVariantList createVariantList() const + { + return { QString("cpp"), QString("variant"), QString("list") }; + } + + Q_INVOKABLE QQmlListProperty<QObject> createQmlListProperty() + { + QV4::ExecutionEngine engine(qmlEngine(this)); + static QObject objects[] = { QObject{}, QObject{}, QObject{} }; + objects[0].setObjectName("o0"); + objects[1].setObjectName("o1"); + objects[2].setObjectName("o2"); + static QList<QObject *> list{ &objects[0], &objects[1], &objects[2] }; + return QQmlListProperty<QObject>(this, &list); + } + + Q_INVOKABLE void logArray(const QJsonArray &arr) const + { + const auto str = QString(QJsonDocument(arr).toJson(QJsonDocument::Compact)); + qDebug().noquote() << str; + } + + Q_INVOKABLE void logObject(const QJsonObject &obj) const + { + const auto str = QString(QJsonDocument(obj).toJson(QJsonDocument::Compact)); + qDebug().noquote() << str; + } +}; + +#endif // VARIANTLIST_QJSON_CONVERSION_HPP diff --git a/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt b/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt index d91fc98f8a..87b0874774 100644 --- a/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt +++ b/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlenginecleanup Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlenginecleanup LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml index 6dfe0d52e8..083e8f2e7c 100644 --- a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml +++ b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.12 diff --git a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp index c53f9e674b..52ab367d10 100644 --- a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp +++ b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqmlengine.h> diff --git a/tests/auto/qml/qqmlenginecleanup/data/TestType.qml b/tests/auto/qml/qqmlenginecleanup/data/TestType.qml index 9f98ec971f..0699ac894f 100644 --- a/tests/auto/qml/qqmlenginecleanup/data/TestType.qml +++ b/tests/auto/qml/qqmlenginecleanup/data/TestType.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research In Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.0 QtObject{property int notJustAStandardQtObject: 10 } diff --git a/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml b/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml index 5298dea961..233684633f 100644 --- a/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml +++ b/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml b/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml index 0bd2c54e16..02d231d375 100644 --- a/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml +++ b/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml b/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml index be95e2ff74..10e2b2888a 100644 --- a/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml +++ b/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlenginecleanup/data/types.qml b/tests/auto/qml/qqmlenginecleanup/data/types.qml index f726a37404..4de1e6e43d 100644 --- a/tests/auto/qml/qqmlenginecleanup/data/types.qml +++ b/tests/auto/qml/qqmlenginecleanup/data/types.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research In Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.0 import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp index c9049c7ca4..04e8389c87 100644 --- a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp +++ b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research In Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QObject> #include <QtQml/qqml.h> @@ -47,8 +47,8 @@ public: void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations() { //Test for preventing memory leaks is in tests/manual/qmltypememory - QQmlEngine* engine; - CleanlyLoadingComponent* component; + std::unique_ptr<QQmlEngine> engine; + std::unique_ptr<CleanlyLoadingComponent> component; QUrl testFile = testFileUrl("types.qml"); const auto qmlTypeForTestType = []() { @@ -60,12 +60,12 @@ void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations() qmlRegisterType<QObject>("Test", 2, 0, "TestTypeCpp"); QVERIFY(qmlTypeForTestType().isValid()); - engine = new QQmlEngine; - component = new CleanlyLoadingComponent(engine, testFile); + engine = std::make_unique<QQmlEngine>(); + component = std::make_unique<CleanlyLoadingComponent>(engine.get(), testFile); QVERIFY(component->isReady()); - delete component; - delete engine; + component.reset(); + engine.reset(); { auto cppType = qmlTypeForTestType(); @@ -81,24 +81,21 @@ void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations() //2nd run verifies that types can reload after a qmlClearTypeRegistrations qmlRegisterType<QObject>("Test", 2, 0, "TestTypeCpp"); QVERIFY(qmlTypeForTestType().isValid()); - engine = new QQmlEngine; - component = new CleanlyLoadingComponent(engine, testFile); + engine = std::make_unique<QQmlEngine>(); + component = std::make_unique<CleanlyLoadingComponent>(engine.get(), testFile); QVERIFY(component->isReady()); - delete component; - delete engine; + component.reset(); + engine.reset(); qmlClearTypeRegistrations(); QVERIFY(!qmlTypeForTestType().isValid()); //3nd run verifies that TestTypeCpp is no longer registered - engine = new QQmlEngine; - component = new CleanlyLoadingComponent(engine, testFile); + engine = std::make_unique<QQmlEngine>(); + component = std::make_unique<CleanlyLoadingComponent>(engine.get(), testFile); QVERIFY(component->isError()); QCOMPARE(component->errorString(), testFile.toString() +":8 module \"Test\" is not installed\n"); - - delete component; - delete engine; } static void cleanState(QQmlEngine **e) diff --git a/tests/auto/qml/qqmlerror/CMakeLists.txt b/tests/auto/qml/qqmlerror/CMakeLists.txt index be85347051..11b084e88d 100644 --- a/tests/auto/qml/qqmlerror/CMakeLists.txt +++ b/tests/auto/qml/qqmlerror/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlerror Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlerror LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp index b0e8846e59..d4d483fb27 100644 --- a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp +++ b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlError> diff --git a/tests/auto/qml/qqmlexpression/CMakeLists.txt b/tests/auto/qml/qqmlexpression/CMakeLists.txt index 79dccd484d..6adb01b3b1 100644 --- a/tests/auto/qml/qqmlexpression/CMakeLists.txt +++ b/tests/auto/qml/qqmlexpression/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlexpression Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlexpression LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp index 71c186f3e4..59023391cd 100644 --- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp +++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp @@ -1,11 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlfile.h> #include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> #include <QtQml/qqmlexpression.h> +#include <QtQml/qqmlfile.h> #include <QtQml/qqmlscriptstring.h> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -20,6 +21,7 @@ private slots: void syntaxError(); void exception(); void expressionFromDataComponent(); + void emptyScriptString(); }; class TestObject : public QObject @@ -121,6 +123,36 @@ void tst_qqmlexpression::expressionFromDataComponent() QCOMPARE(result.toString(), QStringLiteral("success")); } +void tst_qqmlexpression::emptyScriptString() +{ + QQmlEngine engine; + QQmlContext *context = engine.rootContext(); + QVERIFY(context); + QVERIFY(context->isValid()); + + QQmlScriptString empty; + QVERIFY(empty.isEmpty()); + + QQmlExpression expression(empty, context, this); + QCOMPARE(expression.context(), context); + QCOMPARE(expression.scopeObject(), this); + QCOMPARE(expression.expression(), QString()); + + const QVariant result = expression.evaluate(); + QVERIFY(!result.isValid()); + + QQmlComponent c(&engine, testFileUrl("scriptString.qml")); + std::unique_ptr<QObject> root { c.create() }; + TestObject *testObj = qobject_cast<TestObject*>(root.get()); + QVERIFY(testObj != nullptr); + + QQmlScriptString script = testObj->scriptString(); + QVERIFY(!script.isEmpty()); + + // verify that comparing against an empty script string does not crash + QVERIFY(script != empty); +} + QTEST_MAIN(tst_qqmlexpression) #include "tst_qqmlexpression.moc" diff --git a/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt b/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt index de108af085..4a32e3a0e4 100644 --- a/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt +++ b/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlextensionplugin Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlextensionplugin LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlextensionplugin SOURCES tst_qqmlextensionplugin.cpp diff --git a/tests/auto/qml/qqmlextensionplugin/data/dummy.qml b/tests/auto/qml/qqmlextensionplugin/data/dummy.qml index f8169bd6aa..e555405535 100644 --- a/tests/auto/qml/qqmlextensionplugin/data/dummy.qml +++ b/tests/auto/qml/qqmlextensionplugin/data/dummy.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp index b8e0b01f6d..5f299f7b91 100644 --- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp +++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> #include <QtTest> @@ -75,7 +75,7 @@ void tst_qqmlextensionplugin::iidCheck_data() files = removeDuplicates(std::move(files)); QTest::addColumn<QString>("filePath"); - foreach (const QString &file, files) { + for (const QString &file: std::as_const(files)) { QFileInfo fileInfo(file); QTest::newRow(fileInfo.baseName().toLatin1().data()) << fileInfo.absoluteFilePath(); } diff --git a/tests/auto/qml/qqmlfile/CMakeLists.txt b/tests/auto/qml/qqmlfile/CMakeLists.txt index c573b53b1a..d8e23f410c 100644 --- a/tests/auto/qml/qqmlfile/CMakeLists.txt +++ b/tests/auto/qml/qqmlfile/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlfile Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlfile LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlfile SOURCES tst_qqmlfile.cpp diff --git a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp index 3f75a14bf6..3ce87ce048 100644 --- a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp +++ b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> #include <QtTest> diff --git a/tests/auto/qml/qqmlfileselector/CMakeLists.txt b/tests/auto/qml/qqmlfileselector/CMakeLists.txt index d169ffc607..b3dec0ff3d 100644 --- a/tests/auto/qml/qqmlfileselector/CMakeLists.txt +++ b/tests/auto/qml/qqmlfileselector/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlfileselector Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlfileselector LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml index d6dd2c9b90..4e09798a84 100644 --- a/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml @@ -1,10 +1,8 @@ import QtQuick import qmldirtest -Window { - width: 640 - height: 480 - visible: true +Item { + objectName: Name.name property color color: mybutton.color MyButton { diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js new file mode 100644 index 0000000000..91ca4f129d --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js @@ -0,0 +1 @@ +var name = "linux" diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js new file mode 100644 index 0000000000..12e5058285 --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js @@ -0,0 +1 @@ +var name = "macos" diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js new file mode 100644 index 0000000000..916a232eb4 --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js @@ -0,0 +1 @@ +var name = "base" diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir index ac68d9097d..a2efdbf27d 100644 --- a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir @@ -2,4 +2,7 @@ module qmldirtest MyButton 1.0 qml/MyButton.qml MyButton 1.0 qml/+linux/MyButton.qml MyButton 1.0 qml/+macos/MyButton.qml +Name 1.0 qml/Name.js +Name 1.0 qml/+linux/Name.js +Name 1.0 qml/+macos/Name.js diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml new file mode 100644 index 0000000000..5bf632c48d --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml @@ -0,0 +1,7 @@ +import QtQuick + +Rectangle { + width: 300 + height: 50 + color: "yellow" +} diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js new file mode 100644 index 0000000000..8591795d37 --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js @@ -0,0 +1 @@ +var name = "bar" diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml new file mode 100644 index 0000000000..cc6eb967da --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml @@ -0,0 +1,7 @@ +import QtQuick + +Rectangle { + width: 300 + height: 50 + color: "blue" +} diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js new file mode 100644 index 0000000000..b224ed15ec --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js @@ -0,0 +1 @@ +var name = "foo" diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml new file mode 100644 index 0000000000..32db428c4f --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml @@ -0,0 +1,7 @@ +import QtQuick + +Rectangle { + width: 300 + height: 50 + color: "green" +} diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js new file mode 100644 index 0000000000..916a232eb4 --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js @@ -0,0 +1 @@ +var name = "base" diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml new file mode 100644 index 0000000000..5fb8afc660 --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml @@ -0,0 +1,9 @@ +import QtQuick + +Item { + objectName: Name.name + property color color: mybutton.color + MyButton { + id: mybutton + } +} diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir new file mode 100644 index 0000000000..92fefb9806 --- /dev/null +++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir @@ -0,0 +1,5 @@ +module qmldirtest2 +MyButton 1.0 +foo/MyButton.qml +MyButton 1.0 MyButton.qml +MyButton 1.0 +bar/MyButton.qml +Name 1.0 Name.js diff --git a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp index 46df20378c..98774ffe64 100644 --- a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp +++ b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 BlackBerry Limited. All rights reserved. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -32,11 +32,9 @@ void tst_qqmlfileselector::basicTest() selector.setExtraSelectors(QStringList() << "basic"); QQmlComponent component(&engine, testFileUrl("basicTest.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(object->property("value").toString(), QString("selected")); - - delete object; } void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) @@ -72,19 +70,38 @@ void tst_qqmlfileselector::applicationEngineTest() void tst_qqmlfileselector::qmldirCompatibility() { - QQmlApplicationEngine engine; - engine.addImportPath(dataDirectory()); - engine.load(testFileUrl("qmldirtest/main.qml")); - QVERIFY(!engine.rootObjects().isEmpty()); - QObject *object = engine.rootObjects().at(0); - auto color = object->property("color").value<QColor>(); + { + // No error for multiple files with different selectors, and the matching one is chosen + // for +macos and +linux selectors. + QQmlApplicationEngine engine; + engine.addImportPath(dataDirectory()); + engine.load(testFileUrl("qmldirtest/main.qml")); + QVERIFY(!engine.rootObjects().isEmpty()); + QObject *object = engine.rootObjects().at(0); + auto color = object->property("color").value<QColor>(); #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - QCOMPARE(color, QColorConstants::Svg::blue); + QCOMPARE(object->objectName(), "linux"); + QCOMPARE(color, QColorConstants::Svg::blue); #elif defined(Q_OS_DARWIN) - QCOMPARE(color, QColorConstants::Svg::yellow); + QCOMPARE(object->objectName(), "macos"); + QCOMPARE(color, QColorConstants::Svg::yellow); #else - QCOMPARE(color, QColorConstants::Svg::green); + QCOMPARE(object->objectName(), "base"); + QCOMPARE(color, QColorConstants::Svg::green); #endif + } + + { + // If nothing matches, the _base_ file is chosen, not the first or the last one. + // This also holds when using the implicit import. + QQmlApplicationEngine engine; + engine.addImportPath(dataDirectory()); + engine.load(testFileUrl("qmldirtest2/main.qml")); + QVERIFY(!engine.rootObjects().isEmpty()); + QObject *object = engine.rootObjects().at(0); + QCOMPARE(object->property("color").value<QColor>(), QColorConstants::Svg::green); + QCOMPARE(object->objectName(), "base"); + } } QTEST_MAIN(tst_qqmlfileselector) diff --git a/tests/auto/qml/qqmlglobal/CMakeLists.txt b/tests/auto/qml/qqmlglobal/CMakeLists.txt index ff57005dc3..ce600c7607 100644 --- a/tests/auto/qml/qqmlglobal/CMakeLists.txt +++ b/tests/auto/qml/qqmlglobal/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlglobal Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlglobal LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlglobal SOURCES tst_qqmlglobal.cpp diff --git a/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp b/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp index 81d61976a3..884ea180a9 100644 --- a/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp +++ b/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <qqml.h> diff --git a/tests/auto/qml/qqmlimport/CMakeLists.txt b/tests/auto/qml/qqmlimport/CMakeLists.txt index 32aeec0966..b8b720f5dc 100644 --- a/tests/auto/qml/qqmlimport/CMakeLists.txt +++ b/tests/auto/qml/qqmlimport/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlimport Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlimport LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -29,6 +35,7 @@ qt_internal_add_test(tst_qqmlimport SOURCES tst_qqmlimport.cpp LIBRARIES + Qt::CorePrivate Qt::Gui Qt::Qml Qt::QmlPrivate @@ -44,6 +51,21 @@ qt_internal_add_resource(tst_qqmlimport "preferred" "Preferred.qml" ) +qt_internal_add_resource(tst_qqmlimport "preferred2" + PREFIX + "/qqmlimport/ModuleWithPrefer2/" + FILES + "Preferred.qml" + "qmldir" +) + +qt_internal_add_resource(tst_qqmlimport "qtconf" + PREFIX + "/" + FILES + "qmlimports.qt.conf" +) + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml b/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml index 943ce3309f..960b9b474b 100644 --- a/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml +++ b/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml b/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml index 943ce3309f..960b9b474b 100644 --- a/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml +++ b/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml new file mode 100644 index 0000000000..a4af255b56 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + objectName: "wrong" +} diff --git a/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir new file mode 100644 index 0000000000..6b04c0eb5f --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir @@ -0,0 +1,3 @@ +module ModuleWithPrefer2 +prefer :/qqmlimport/ModuleWithPrefer2/ +Preferred 1.0 Preferred.qml diff --git a/tests/auto/qml/qqmlimport/data/MyModuleName/Font.js b/tests/auto/qml/qqmlimport/data/MyModuleName/Font.js new file mode 100644 index 0000000000..7036c0a739 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/MyModuleName/Font.js @@ -0,0 +1,4 @@ +.pragma library +.import QtQuick as QtQuick + +var exampleVar = 12; diff --git a/tests/auto/qml/qqmlimport/data/MyModuleName/qmldir b/tests/auto/qml/qqmlimport/data/MyModuleName/qmldir new file mode 100644 index 0000000000..43add34163 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/MyModuleName/qmldir @@ -0,0 +1,2 @@ +module MyModuleName +Font 0.1 Font.js diff --git a/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml new file mode 100644 index 0000000000..83dbd566f3 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml @@ -0,0 +1,6 @@ +import QtQml +import 'file://./MyModuleName' as MyModuleName + +QtObject { + objectName: MyModuleName.Font.exampleVar +} diff --git a/tests/auto/qml/qqmlimport/data/noimport/Main.qml b/tests/auto/qml/qqmlimport/data/noimport/Main.qml new file mode 100644 index 0000000000..d473b43d7b --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/noimport/Main.qml @@ -0,0 +1,5 @@ +pragma Strict + +TheThing { + width: 640 +} diff --git a/tests/auto/qml/qqmlimport/data/noimport/qmldir b/tests/auto/qml/qqmlimport/data/noimport/qmldir new file mode 100644 index 0000000000..3034edba51 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/noimport/qmldir @@ -0,0 +1,3 @@ +module noimport +Main 1.0 Main.qml + diff --git a/tests/auto/qml/qqmlimport/data/prefer2.qml b/tests/auto/qml/qqmlimport/data/prefer2.qml new file mode 100644 index 0000000000..c0727dc13d --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/prefer2.qml @@ -0,0 +1,3 @@ +import ModuleWithPrefer2 + +Pickles {} diff --git a/tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml b/tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml new file mode 100644 index 0000000000..de4b02afbb --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml @@ -0,0 +1,8 @@ +import QtQuick +import 'MyModuleName' as MyModuleName + +Item { + property var a: MyModuleName.Font.exampleVar + property var b: Font.SmallCaps + property var c: Font.exampleVar +} diff --git a/tests/auto/qml/qqmlimport/data/testfile_supported.qml b/tests/auto/qml/qqmlimport/data/testfile_supported.qml index eb4995bacf..dc20a91d06 100644 --- a/tests/auto/qml/qqmlimport/data/testfile_supported.qml +++ b/tests/auto/qml/qqmlimport/data/testfile_supported.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import MyPluginSupported 1.0 diff --git a/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml b/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml index 81156f1624..38ad7ad258 100644 --- a/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml +++ b/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import MyPluginUnsupported 1.0 diff --git a/tests/auto/qml/qqmlimport/qmldir b/tests/auto/qml/qqmlimport/qmldir new file mode 100644 index 0000000000..ca29b16cfb --- /dev/null +++ b/tests/auto/qml/qqmlimport/qmldir @@ -0,0 +1,2 @@ +module ModuleWithPrefer2 +Pickles 1.0 Preferred.qml diff --git a/tests/auto/qml/qqmlimport/qmlimports.qt.conf b/tests/auto/qml/qqmlimport/qmlimports.qt.conf new file mode 100644 index 0000000000..3a63cc797f --- /dev/null +++ b/tests/auto/qml/qqmlimport/qmlimports.qt.conf @@ -0,0 +1,3 @@ +[Paths] +Prefix = "" +QmlImports = ":/a/path", ":/another/path", ":/even/more/path" diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index 38970a3b99..fe14281387 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -1,23 +1,50 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <private/qmlutils_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlimport_p.h> -#include <QtCore/qscopeguard.h> -#include <QtTest/QtTest> -#include <QQmlApplicationEngine> -#include <QQmlAbstractUrlInterceptor> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> -#include <private/qqmlimport_p.h> -#include <private/qqmlengine_p.h> -#include <QtQuickTestUtils/private/qmlutils_p.h> -#include <QQmlComponent> + +#include <QtTest/qsignalspy.h> + +#include <QtQml/qqmlabstracturlinterceptor.h> +#include <QtQml/qqmlapplicationengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlmoduleregistration.h> + +#include <QtCore/qscopeguard.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/private/qlibraryinfo_p.h> + +class TheThing : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int width MEMBER m_width FINAL) + +public: + int m_width = 12; +}; + +void qml_register_types_noimport() +{ + qmlRegisterTypesAndRevisions<TheThing>("noimport", 1); + qmlRegisterModule("noimport", 1, 0); +} class tst_QQmlImport : public QQmlDataTest { Q_OBJECT public: - tst_QQmlImport(); + tst_QQmlImport() + : QQmlDataTest(QT_QMLTEST_DATADIR) + , noimportRegistration("noimport", qml_register_types_noimport) + { + } private slots: void importPathOrder(); @@ -34,10 +61,18 @@ private slots: void importDependenciesPrecedence(); void cleanup(); void envResourceImportPath(); + void preferResourcePath_data(); void preferResourcePath(); void invalidFileImport_data(); void invalidFileImport(); void implicitWithDependencies(); + void qualifiedScriptImport(); + void invalidImportUrl(); + void registerTypesFromImplicitImport(); + void containsAllQtConfEntries(); + +private: + QQmlModuleRegistration noimportRegistration; }; void tst_QQmlImport::cleanup() @@ -72,12 +107,21 @@ void tst_QQmlImport::envResourceImportPath() QVERIFY((importPaths.contains(path.startsWith(u':') ? QLatin1String("qrc") + path : path))); } +void tst_QQmlImport::preferResourcePath_data() +{ + QTest::addColumn<QUrl>("file"); + QTest::addRow("without qmldir") << testFileUrl("prefer.qml"); + QTest::addRow("with qmldir") << testFileUrl("prefer2.qml"); +} + void tst_QQmlImport::preferResourcePath() { + QFETCH(QUrl, file); + QQmlEngine engine; engine.addImportPath(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("prefer.qml")); + QQmlComponent component(&engine, file); QVERIFY2(component.isReady(), component.errorString().toUtf8()); QScopedPointer<QObject> o(component.create()); QCOMPARE(o->objectName(), "right"); @@ -123,9 +167,62 @@ void tst_QQmlImport::implicitWithDependencies() QCOMPARE(o->objectName(), QStringLiteral("notARectangle")); } +void tst_QQmlImport::qualifiedScriptImport() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("qualifiedScriptImport.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("a"), QVariant::fromValue<double>(12)); + QCOMPARE(o->property("b"), QVariant::fromValue<int>(3)); + QCOMPARE(o->property("c"), QVariant()); +} + +void tst_QQmlImport::invalidImportUrl() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("fileDotSlashImport.qml"); + QQmlComponent component(&engine, url); + QVERIFY(component.isError()); + QCOMPARE( + component.errorString(), + url.toString() + QLatin1String( + ":2 Cannot resolve URL for import \"file://./MyModuleName\"\n")); +} + +void tst_QQmlImport::registerTypesFromImplicitImport() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("noimport/Main.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + TheThing *t = qobject_cast<TheThing *>(o.data()); + QVERIFY(t); + QCOMPARE(t->m_width, 640); +} + +void tst_QQmlImport::containsAllQtConfEntries() +{ + QString qtConfPath(u":/qmlimports.qt.conf"); + QLibraryInfoPrivate::setQtconfManualPath(&qtConfPath); + QLibraryInfoPrivate::reload(); + auto cleanup = qScopeGuard([](){ + QLibraryInfoPrivate::setQtconfManualPath(nullptr); + QLibraryInfoPrivate::reload(); + }); + QQmlEngine engine; + auto importPaths = engine.importPathList(); + QVERIFY(importPaths.contains(u"qrc:/a/path")); + QVERIFY(importPaths.contains(u"qrc:/another/path")); + QVERIFY(importPaths.contains(u"qrc:/even/more/path")); +} + void tst_QQmlImport::testDesignerSupported() { - QQuickView *window = new QQuickView(); + std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>(); window->engine()->addImportPath(directory()); window->setSource(testFileUrl("testfile_supported.qml")); @@ -137,8 +234,7 @@ void tst_QQmlImport::testDesignerSupported() QQmlImports::setDesignerSupportRequired(true); //imports are cached so we create a new window - delete window; - window = new QQuickView(); + window = std::make_unique<QQuickView>(); window->engine()->addImportPath(directory()); window->engine()->clearComponentCache(); @@ -154,21 +250,19 @@ void tst_QQmlImport::testDesignerSupported() QTest::ignoreMessage(QtWarningMsg, warningString.toLocal8Bit()); window->setSource(testFileUrl("testfile_unsupported.qml")); QVERIFY(!window->errors().isEmpty()); - - delete window; } void tst_QQmlImport::uiFormatLoading() { int size = 0; - QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("TestForm.ui.qml")); + std::unique_ptr<QQmlApplicationEngine> test = std::make_unique<QQmlApplicationEngine>(testFileUrl("TestForm.ui.qml")); test->addImportPath(directory()); QCOMPARE(test->rootObjects().size(), ++size); QVERIFY(test->rootObjects()[size -1]); QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); - QSignalSpy objectCreated(test, SIGNAL(objectCreated(QObject*,QUrl))); + QSignalSpy objectCreated(test.get(), SIGNAL(objectCreated(QObject*,QUrl))); test->load(testFileUrl("TestForm.ui.qml")); QCOMPARE(objectCreated.size(), size);//one less than rootObjects().size() because we missed the first one QCOMPARE(test->rootObjects().size(), ++size); @@ -193,13 +287,6 @@ void tst_QQmlImport::uiFormatLoading() QCOMPARE(test->rootObjects().size(), ++size); QVERIFY(test->rootObjects()[size -1]); QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); - - delete test; -} - -tst_QQmlImport::tst_QQmlImport() - : QQmlDataTest(QT_QMLTEST_DATADIR) -{ } void tst_QQmlImport::importPathOrder() @@ -503,6 +590,25 @@ void tst_QQmlImport::registerModuleImport() qmlUnregisterModuleImport("MyPluginSupported", 2, "QtQuick", 2); qmlUnregisterModuleImport("MyPluginSupported", 2, "ShadowQuick", 1); + + qmlRegisterTypesAndRevisions<NotItem>("NoQmldir", 2); + qmlRegisterModuleImport("NoQmldir", QQmlModuleImportModuleAny, "QtQml", QQmlModuleImportAuto); + + { + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import NoQmldir 2.0 + QtObject { + property Item item: Item {} + } + )", QUrl::fromLocalFile("")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); + NotItem *item = object->property("item").value<NotItem *>(); + QVERIFY(item); + } } void tst_QQmlImport::importDependenciesPrecedence() diff --git a/tests/auto/qml/qqmlincubator/CMakeLists.txt b/tests/auto/qml/qqmlincubator/CMakeLists.txt index 230609bb1d..3a38868e76 100644 --- a/tests/auto/qml/qqmlincubator/CMakeLists.txt +++ b/tests/auto/qml/qqmlincubator/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlincubator Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlincubator LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml b/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml new file mode 100644 index 0000000000..b5ba531ede --- /dev/null +++ b/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml @@ -0,0 +1,12 @@ +import QtQml +QtObject { + property Component comp: Component { + QtObject {} + } + + property QtObject incubated: { + var i = comp.incubateObject(null, {}, Qt.Synchronous); + gc(); + return i.object + } +} diff --git a/tests/auto/qml/qqmlincubator/testtypes.cpp b/tests/auto/qml/qqmlincubator/testtypes.cpp index c94ef73c20..4e449a1b5a 100644 --- a/tests/auto/qml/qqmlincubator/testtypes.cpp +++ b/tests/auto/qml/qqmlincubator/testtypes.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testtypes.h" #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlincubator/testtypes.h b/tests/auto/qml/qqmlincubator/testtypes.h index e4c48d9d13..4942957986 100644 --- a/tests/auto/qml/qqmlincubator/testtypes.h +++ b/tests/auto/qml/qqmlincubator/testtypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp index 1baf61574e..948dc66e71 100644 --- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp +++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testtypes.h" #include <QUrl> @@ -1148,7 +1148,7 @@ void tst_qqmlincubator::garbageCollection() QQmlComponent component(&engine, testFileUrl("garbageCollection.qml")); QScopedPointer<QObject> obj(component.create()); - engine.collectGarbage(); + gc(engine); std::atomic<bool> b{true}; controller.incubateWhile(&b); @@ -1166,8 +1166,14 @@ void tst_qqmlincubator::garbageCollection() incubatorVariant.clear(); // verify incubator is correctly collected now that incubation is complete and all references are gone - engine.collectGarbage(); + gc(engine); QVERIFY(weakIncubatorRef.isNullOrUndefined()); + + QQmlComponent component2(&engine, testFileUrl("garbageCollection2.qml")); + QVERIFY2(component2.isReady(), qPrintable(component2.errorString())); + QScopedPointer<QObject> obj2(component2.create()); + QVERIFY(!obj2.isNull()); + QVERIFY(obj2->property("incubated").value<QObject *>() != nullptr); } void tst_qqmlincubator::requiredProperties() diff --git a/tests/auto/qml/qqmlinfo/CMakeLists.txt b/tests/auto/qml/qqmlinfo/CMakeLists.txt index 32e6c5ebd9..db00af9ea8 100644 --- a/tests/auto/qml/qqmlinfo/CMakeLists.txt +++ b/tests/auto/qml/qqmlinfo/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlinfo Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlinfo LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlinfo/attached.cpp b/tests/auto/qml/qqmlinfo/attached.cpp index 5ced27f3c9..88ad5d88dd 100644 --- a/tests/auto/qml/qqmlinfo/attached.cpp +++ b/tests/auto/qml/qqmlinfo/attached.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "attached.h" diff --git a/tests/auto/qml/qqmlinfo/attached.h b/tests/auto/qml/qqmlinfo/attached.h index c719da8bcc..8d4018f88d 100644 --- a/tests/auto/qml/qqmlinfo/attached.h +++ b/tests/auto/qml/qqmlinfo/attached.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp index 4908ca210b..ebe1416c7d 100644 --- a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp +++ b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtTest/qsignalspy.h> @@ -213,7 +213,7 @@ void tst_qqmlinfo::attachedObject() { QQmlComponent component(&engine, testFileUrl("AttachedObject.qml")); - QSignalSpy warningSpy(&engine, SIGNAL(warnings(const QList<QQmlError> &))); + QSignalSpy warningSpy(&engine, SIGNAL(warnings(QList<QQmlError>))); QVERIFY(warningSpy.isValid()); const QString qmlBindingLoopMessage = "QML Rectangle: Binding loop detected for property \"width\""; diff --git a/tests/auto/qml/qqmlinstantiator/CMakeLists.txt b/tests/auto/qml/qqmlinstantiator/CMakeLists.txt index 59280f8c7d..7e453b9671 100644 --- a/tests/auto/qml/qqmlinstantiator/CMakeLists.txt +++ b/tests/auto/qml/qqmlinstantiator/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlinstantiator Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlinstantiator LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml b/tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml new file mode 100644 index 0000000000..82d707af37 --- /dev/null +++ b/tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml @@ -0,0 +1,20 @@ +import QtQml + +QtObject { + id: menu + + Component.onCompleted: dt = new Date(); + property date dt + property Instantiator i: Instantiator { + model: { + var model = []; + var d = menu.dt; + model.push({text: "A"}); + return model; + } + delegate: QtObject { + objectName: modelData.text + Component.onCompleted: menu.objectName = objectName + } + } +} diff --git a/tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml b/tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml new file mode 100644 index 0000000000..079e376549 --- /dev/null +++ b/tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml @@ -0,0 +1,10 @@ +import QtQuick 2.15 +import QtQml.Models 2.15 + +Instantiator { + delegate: QtObject { + function deactivate() { + model.active = false; + } + } +} diff --git a/tests/auto/qml/qqmlinstantiator/stringmodel.h b/tests/auto/qml/qqmlinstantiator/stringmodel.h index d9676b9460..2b0c725e05 100644 --- a/tests/auto/qml/qqmlinstantiator/stringmodel.h +++ b/tests/auto/qml/qqmlinstantiator/stringmodel.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 Dmitrii Kosarev aka Kakadu <kakadu.hafanana@gmail.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef STRINGMODEL_H #define STRINGMODEL_H diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp index 1097c65f02..de7c6645a9 100644 --- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp +++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp @@ -1,7 +1,8 @@ // Copyright (C) 2016 Research In Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QSignalSpy> +#include <QSortFilterProxyModel> #include <QDebug> #include <QtQml/qqmlengine.h> @@ -28,12 +29,15 @@ private slots: void activeModelChangeInteraction(); void intModelChange(); void createAndRemove(); + void removeDuringModelChange(); void asynchronous_data(); void asynchronous(); void handlerWithParent(); void boundDelegateComponent(); + + void listDataDestruction(); }; tst_qqmlinstantiator::tst_qqmlinstantiator() @@ -306,6 +310,103 @@ void tst_qqmlinstantiator::boundDelegateComponent() QCOMPARE(b->objectAt(2)->objectName(), QStringLiteral("root3")); } +void tst_qqmlinstantiator::listDataDestruction() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("listDataDestruction.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), QLatin1String("A")); +} + +class SingleBoolItemModel : public QAbstractListModel +{ + Q_OBJECT + +public: + SingleBoolItemModel(QObject *parent = nullptr) : QAbstractListModel(parent) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) + return 0; + return 1; + } + + QVariant data(const QModelIndex &index, int role) const override + { + if (index.parent().isValid() || index.row() != 0 || index.column() != 0 + || role != Qt::UserRole) + return QVariant(); + + return m_active; + } + + bool setData(const QModelIndex &index, const QVariant &value, + int role) override { + if (index.parent().isValid() || index.row() != 0 || index.column() != 0 + || role != Qt::UserRole || m_active == value.toBool()) + return false; + + m_active = value.toBool(); + Q_EMIT dataChanged(index, index, QList<int>{Qt::UserRole}); + return true; + } + + QHash<int, QByteArray> roleNames() const override + { + return { {Qt::UserRole, "active"} }; + } + +private: + bool m_active = true; +}; + +class FilterBoolRoleProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + FilterBoolRoleProxyModel(QObject *parent = nullptr) + : QSortFilterProxyModel(parent) {} + + bool filterAcceptsRow(int source_row, + const QModelIndex &source_parent) const override + { + return sourceModel()->index(source_row, 0, source_parent).data(Qt::UserRole).toBool(); + } +}; + +void tst_qqmlinstantiator::removeDuringModelChange() +{ + SingleBoolItemModel model; + + FilterBoolRoleProxyModel proxyModel; + proxyModel.setSourceModel(&model); + proxyModel.setFilterRole(Qt::UserRole); + QCOMPARE(proxyModel.rowCount(), 1); + + QQmlEngine engine; + const QUrl url(testFileUrl("removeDuringModelChange.qml")); + QQmlComponent component(&engine, url); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + + QScopedPointer<QObject> o(component.create()); + QVERIFY2(!o.isNull(), qPrintable(component.errorString())); + + QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator *>(o.data()); + + instantiator->setModel(QVariant::fromValue(&proxyModel)); + + QSignalSpy removedSpy(instantiator, &QQmlInstantiator::objectRemoved); + QMetaObject::invokeMethod(instantiator->object(), "deactivate"); + + // We should still be alive at this point. + QCOMPARE(removedSpy.size(), 1); + QCOMPARE(proxyModel.rowCount(), 0); +} + QTEST_MAIN(tst_qqmlinstantiator) #include "tst_qqmlinstantiator.moc" diff --git a/tests/auto/qml/qqmlitemmodels/CMakeLists.txt b/tests/auto/qml/qqmlitemmodels/CMakeLists.txt index 632e98206b..80f210f668 100644 --- a/tests/auto/qml/qqmlitemmodels/CMakeLists.txt +++ b/tests/auto/qml/qqmlitemmodels/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlitemmodels Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlitemmodels LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml index 2756f04120..7bf8bfb5a2 100644 --- a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml +++ b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml @@ -8,6 +8,7 @@ ItemModelsTest { property var parent: modelIndex.parent property var model: modelIndex.model property var internalId: modelIndex.internalId + property var displayData: modelIndex.data(Qt.DisplayRole) onSignalWithModelIndex: { isValid = index.valid @@ -16,5 +17,6 @@ ItemModelsTest { parent = index.parent model = index.model internalId = index.internalId + displayData = index.data(Qt.DisplayRole) } } diff --git a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml index 85987bdcac..aa43252100 100644 --- a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml +++ b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml @@ -8,6 +8,7 @@ ItemModelsTest { property var parent: persistentModelIndex.parent property var model: persistentModelIndex.model property var internalId: persistentModelIndex.internalId + property var displayData: persistentModelIndex.data(Qt.DisplayRole) property var pmi @@ -18,6 +19,7 @@ ItemModelsTest { parent = index.parent model = index.model internalId = index.internalId + displayData = index.data(Qt.DisplayRole) pmi = createPersistentModelIndex(model.index(0, 0)) } diff --git a/tests/auto/qml/qqmlitemmodels/qtestmodel.h b/tests/auto/qml/qqmlitemmodels/qtestmodel.h index 6cbec533b1..6c442b06b7 100644 --- a/tests/auto/qml/qqmlitemmodels/qtestmodel.h +++ b/tests/auto/qml/qqmlitemmodels/qtestmodel.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef Q_TEST_MODEL_H #define Q_TEST_MODEL_H @@ -251,6 +251,8 @@ public: mutable bool wrongIndex; struct Node { + Q_DISABLE_COPY_MOVE(Node) + Node *parent; QVector<Node *> children; @@ -261,8 +263,7 @@ public: ~Node() { - foreach (Node *n, children) - delete n; + qDeleteAll(children); } void addRows(int row, int count) diff --git a/tests/auto/qml/qqmlitemmodels/testtypes.h b/tests/auto/qml/qqmlitemmodels/testtypes.h index 57ac9c12d1..3bbd566cd1 100644 --- a/tests/auto/qml/qqmlitemmodels/testtypes.h +++ b/tests/auto/qml/qqmlitemmodels/testtypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H diff --git a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp index 339a61f996..1182759519 100644 --- a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp +++ b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -55,6 +55,7 @@ void tst_qqmlitemmodels::modelIndex() QCOMPARE(object->property("parent").toModelIndex(), index.parent()); QCOMPARE(object->property("model").value<QAbstractItemModel *>(), index.model()); QCOMPARE(object->property("internalId").toULongLong(), index.internalId()); + QCOMPARE(object->property("displayData"), index.data(Qt::DisplayRole)); if (i < 3) { index = model.index(2 + i, 4 - i, index); @@ -79,6 +80,7 @@ void tst_qqmlitemmodels::persistentModelIndex() QCOMPARE(object->property("parent").toModelIndex(), index.parent()); QCOMPARE(object->property("model").value<QAbstractItemModel *>(), index.model()); QCOMPARE(object->property("internalId").toULongLong(), index.internalId()); + QCOMPARE(object->property("displayData"), index.data(Qt::DisplayRole)); if (i < 2) { index = model.index(2 + i, 4 - i, index); diff --git a/tests/auto/qml/qqmljsscope/CMakeLists.txt b/tests/auto/qml/qqmljsscope/CMakeLists.txt index 542d217d8d..be8e136fc8 100644 --- a/tests/auto/qml/qqmljsscope/CMakeLists.txt +++ b/tests/auto/qml/qqmljsscope/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmljsscope LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt index 3431398f47..5215159373 100644 --- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt +++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt @@ -8,9 +8,10 @@ qt_autogen_tools_initial_setup(qqmljsscope_test_module) target_include_directories(qqmljsscope_test_module PUBLIC cpptypes/) target_link_libraries(qqmljsscope_test_module PUBLIC Qt::Core Qt::Qml Qt::Gui) +qt_policy(SET QTP0001 NEW) + qt6_add_qml_module(qqmljsscope_test_module URI QQmlJSScopeTests - AUTO_RESOURCE_PREFIX SOURCES singleton.h singleton.cpp extensiontypes.h diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h index f5fbde1b27..82fc3b58d2 100644 --- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h +++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef EXTENSIONTYPES_H #define EXTENSIONTYPES_H @@ -18,6 +18,17 @@ public: Extension(QObject *parent = nullptr) : QObject(parent) { } int getCount() const { return 42; } void setCount(int) { } + + enum EnumFromExtension { + ThisIsTheEnumFromExtension, + }; + Q_ENUM(EnumFromExtension) + enum FlagFromExtension { + ThisIsTheFlagFromExtension, + }; + Q_DECLARE_FLAGS(FlagsFromExtension, FlagFromExtension) + Q_FLAG(FlagsFromExtension) + Q_SIGNALS: void countChanged(); }; @@ -41,6 +52,16 @@ public: Extended(QObject *parent = nullptr) : QObject(parent) { } double getCount() const { return 0.0; } void setCount(double) { } + + enum EnumFromExtended { + ThisIsTheEnumFromExtended, + }; + Q_ENUM(EnumFromExtended) + enum FlagFromExtended { + ThisIsTheFlagFromExtended, + }; + Q_DECLARE_FLAGS(FlagsFromExtended, FlagFromExtended) + Q_FLAG(FlagsFromExtended) Q_SIGNALS: void countChanged(); }; diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp index 280ff6163e..56efc324d3 100644 --- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp +++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "singleton.h" diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h index 78aa6466d7..a78b85e1d9 100644 --- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h +++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SINGLETON_H #define SINGLETON_H diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h index 2c812a6d9d..9126cf8f69 100644 --- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h +++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPEWITHPROPERTIES_H #define TYPEWITHPROPERTIES_H diff --git a/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h b/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h index c323645f73..514a98881d 100644 --- a/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h +++ b/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H diff --git a/tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml b/tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml new file mode 100644 index 0000000000..d6062be1f6 --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml @@ -0,0 +1,6 @@ +import QtQml +import QtQuick + +Window { + +} diff --git a/tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml b/tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml new file mode 100644 index 0000000000..52841e323c --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml @@ -0,0 +1,16 @@ +import QtQml + +QtObject { + function f1() { } + function f2(a) { } + function f3(a: int) { } + function f4(a, b) { } + function f5(a, b): void { } + function f6(a, b, c): void { + // Nothing + } + + signal s1() + signal s2(a: int) + signal s3(a: int, b: string) +} diff --git a/tests/auto/qml/qqmljsscope/data/ownModuleName.qml b/tests/auto/qml/qqmljsscope/data/ownModuleName.qml new file mode 100644 index 0000000000..6e43ce6b05 --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/ownModuleName.qml @@ -0,0 +1,10 @@ +import QtQuick + +Item { + Item { id: child } + component IC: Item { + Item { + id: childInIC + } + } +} diff --git a/tests/auto/qml/qqmljsscope/data/requiredAlias.qml b/tests/auto/qml/qqmljsscope/data/requiredAlias.qml new file mode 100644 index 0000000000..77f70dea2b --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/requiredAlias.qml @@ -0,0 +1,20 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Item { + id: self + + property int nonRequired + property alias sameScopeAlias: self.nonRequired + required sameScopeAlias + + property alias innerScopeAlias: inner.nonRequiredInner + required innerScopeAlias + + Item { + id: inner + property int nonRequiredInner + } +} diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp index 09c6601858..05e0b2870a 100644 --- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp +++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -35,9 +35,9 @@ class tst_qqmljsscope : public QQmlDataTest { const QFileInfo fi(url); QFile f(fi.absoluteFilePath()); - f.open(QIODevice::ReadOnly); - QByteArray data(fi.size(), Qt::Uninitialized); - f.read(data.data(), data.size()); + if (!f.open(QIODevice::ReadOnly)) + qFatal("Could not open file %s", qPrintable(url)); + QByteArray data = f.readAll(); return QString::fromUtf8(data); } @@ -71,6 +71,7 @@ class tst_qqmljsscope : public QQmlDataTest logger.setCode(sourceCode); logger.setSilent(expectErrorsOrWarnings); QQmlJSScope::Ptr target = QQmlJSScope::create(); + target->setOwnModuleName(u"HelloModule"_s); QQmlJSImportVisitor visitor(target, &m_importer, &logger, dataDirectory()); QQmlJSTypeResolver typeResolver { &m_importer }; typeResolver.init(&visitor, document->program); @@ -92,6 +93,7 @@ private Q_SLOTS: void signalCreationDifferences(); void allTypesAvailable(); void shadowing(); + void requiredAlias(); #ifdef LABS_QML_MODELS_PRESENT void componentWrappedObjects(); @@ -106,9 +108,15 @@ private Q_SLOTS: void scriptIndices(); void extensions(); void emptyBlockBinding(); - void qualifiedName(); + void hasOwnEnumerationKeys(); + void ownModuleName(); void resolvedNonUniqueScopes(); void compilationUnitsAreCompatible(); + void attachedTypeResolution_data(); + void attachedTypeResolution(); + void builtinTypeResolution_data(); + void builtinTypeResolution(); + void methodAndSignalSourceLocation(); public: tst_qqmljsscope() @@ -160,14 +168,14 @@ void tst_qqmljsscope::orderedBindings() QCOMPARE(std::distance(pBindingsBegin, pBindingsEnd), 2); // check that the bindings are properly ordered - QCOMPARE(pBindingsBegin->bindingType(), QQmlJSMetaPropertyBinding::Object); - QCOMPARE(std::next(pBindingsBegin)->bindingType(), QQmlJSMetaPropertyBinding::Interceptor); + QCOMPARE(pBindingsBegin->bindingType(), QQmlSA::BindingType::Object); + QCOMPARE(std::next(pBindingsBegin)->bindingType(), QQmlSA::BindingType::Interceptor); auto [itemsBindingsBegin, itemsBindingsEnd] = root->ownPropertyBindings(u"items"_s); QCOMPARE(std::distance(itemsBindingsBegin, itemsBindingsEnd), 2); - QCOMPARE(itemsBindingsBegin->bindingType(), QQmlJSMetaPropertyBinding::Object); - QCOMPARE(std::next(itemsBindingsBegin)->bindingType(), QQmlJSMetaPropertyBinding::Object); + QCOMPARE(itemsBindingsBegin->bindingType(), QQmlSA::BindingType::Object); + QCOMPARE(std::next(itemsBindingsBegin)->bindingType(), QQmlSA::BindingType::Object); QCOMPARE(itemsBindingsBegin->objectType()->baseTypeName(), u"Item"_s); QCOMPARE(std::next(itemsBindingsBegin)->objectType()->baseTypeName(), u"Text"_s); @@ -184,8 +192,8 @@ void tst_qqmljsscope::signalCreationDifferences() const auto conflicting = root->ownMethods(u"conflictingPropertyChanged"_s); QCOMPARE(conflicting.size(), 2); - QCOMPARE(conflicting[0].methodType(), QQmlJSMetaMethod::Signal); - QCOMPARE(conflicting[1].methodType(), QQmlJSMetaMethod::Signal); + QCOMPARE(conflicting[0].methodType(), QQmlJSMetaMethodType::Signal); + QCOMPARE(conflicting[1].methodType(), QQmlJSMetaMethodType::Signal); const QQmlJSMetaMethod *explicitMethod = nullptr; if (conflicting[0].isImplicitQmlPropertyChangeSignal()) @@ -204,7 +212,7 @@ void tst_qqmljsscope::allTypesAvailable() QQmlJSImporter importer { importPaths, /* resource file mapper */ nullptr }; const auto imported = importer.importModule(u"QtQml"_s); - QCOMPARE(imported.context(), QQmlJSScope::ContextualTypes::QML); + QCOMPARE(imported.context(), QQmlJS::ContextualTypes::QML); const auto types = imported.types(); QVERIFY(types.contains(u"$internal$.QObject"_s)); QVERIFY(types.contains(u"QtObject"_s)); @@ -235,6 +243,16 @@ void tst_qqmljsscope::shadowing() QCOMPARE(methods[u"method_shadowed"_s].parameterNames().size(), 0); } +void tst_qqmljsscope::requiredAlias() +{ + QQmlJSScope::ConstPtr root = run(u"requiredAlias.qml"_s); + QVERIFY(root); + + // Check whether aliases marked as required are required + QVERIFY(root->isPropertyRequired("sameScopeAlias")); + QVERIFY(root->isPropertyRequired("innerScopeAlias")); +} + #ifdef LABS_QML_MODELS_PRESENT void tst_qqmljsscope::componentWrappedObjects() { @@ -302,7 +320,7 @@ void tst_qqmljsscope::groupedProperties() const auto getBindingsWithinGroup = [&](QMultiHash<QString, QQmlJSMetaPropertyBinding> *bindings, qsizetype index) -> void { const auto &binding = anchorBindings[index]; - QCOMPARE(binding.bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); + QCOMPARE(binding.bindingType(), QQmlSA::BindingType::GroupProperty); auto anchorScope = binding.groupType(); QVERIFY(anchorScope); *bindings = anchorScope->ownPropertyBindings(); @@ -316,14 +334,14 @@ void tst_qqmljsscope::groupedProperties() QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfType; getBindingsWithinGroup(&bindingsOfType, 0); QCOMPARE(bindingsOfType.size(), 2); - QCOMPARE(value(bindingsOfType, u"left"_s).bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfType, u"left"_s).bindingType(), QQmlSA::BindingType::Script); QCOMPARE(value(bindingsOfType, u"leftMargin"_s).bindingType(), - QQmlJSMetaPropertyBinding::NumberLiteral); + QQmlSA::BindingType::NumberLiteral); QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfBaseType; getBindingsWithinGroup(&bindingsOfBaseType, 1); QCOMPARE(bindingsOfBaseType.size(), 1); - QCOMPARE(value(bindingsOfBaseType, u"top"_s).bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfBaseType, u"top"_s).bindingType(), QQmlSA::BindingType::Script); } void tst_qqmljsscope::descriptiveNameOfNull() @@ -337,8 +355,10 @@ void tst_qqmljsscope::descriptiveNameOfNull() property.setPropertyName(u"foo"_s); property.setTypeName(u"baz"_s); QQmlJSRegisterContent unscoped = QQmlJSRegisterContent::create( - stored, property, QQmlJSRegisterContent::ScopeProperty, QQmlJSScope::ConstPtr()); - QCOMPARE(unscoped.descriptiveName(), u"bar of (invalid type)::foo with type baz"_s); + property, QQmlJSRegisterContent::InvalidLookupIndex, + QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ScopeProperty, + QQmlJSScope::ConstPtr()).storedIn(stored); + QCOMPARE(unscoped.descriptiveName(), u"(invalid type)::foo with type baz (stored as bar)"_s); } void tst_qqmljsscope::groupedPropertiesConsistency() @@ -363,8 +383,8 @@ void tst_qqmljsscope::groupedPropertiesConsistency() // The binding order in QQmlJSScope case is "reversed": first come // bindings on the leaf type, followed by the bindings on the base type - QCOMPARE(fontBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); - QCOMPARE(fontBindings[1].bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(fontBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty); + QCOMPARE(fontBindings[1].bindingType(), QQmlSA::BindingType::Script); } } @@ -378,7 +398,7 @@ void tst_qqmljsscope::groupedPropertySyntax() // The binding order in QQmlJSScope case is "reversed": first come // bindings on the leaf type, followed by the bindings on the base type - QCOMPARE(fontBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); + QCOMPARE(fontBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty); auto fontScope = fontBindings[0].groupType(); QVERIFY(fontScope); QCOMPARE(fontScope->accessSemantics(), QQmlJSScope::AccessSemantics::Value); @@ -390,9 +410,8 @@ void tst_qqmljsscope::groupedPropertySyntax() return bindings.value(key, QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {})); }; - QCOMPARE(value(subbindings, u"pixelSize"_s).bindingType(), - QQmlJSMetaPropertyBinding::NumberLiteral); - QCOMPARE(value(subbindings, u"bold"_s).bindingType(), QQmlJSMetaPropertyBinding::BoolLiteral); + QCOMPARE(value(subbindings, u"pixelSize"_s).bindingType(), QQmlSA::BindingType::NumberLiteral); + QCOMPARE(value(subbindings, u"bold"_s).bindingType(), QQmlSA::BindingType::BoolLiteral); } void tst_qqmljsscope::attachedProperties() @@ -407,7 +426,7 @@ void tst_qqmljsscope::attachedProperties() const auto getBindingsWithinAttached = [&](QMultiHash<QString, QQmlJSMetaPropertyBinding> *bindings, qsizetype index) -> void { const auto &binding = keysBindings[index]; - QCOMPARE(binding.bindingType(), QQmlJSMetaPropertyBinding::AttachedProperty); + QCOMPARE(binding.bindingType(), QQmlSA::BindingType::AttachedProperty); auto keysScope = binding.attachingType(); QVERIFY(keysScope); QCOMPARE(keysScope->accessSemantics(), QQmlJSScope::AccessSemantics::Reference); @@ -422,23 +441,21 @@ void tst_qqmljsscope::attachedProperties() QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfType; getBindingsWithinAttached(&bindingsOfType, 0); QCOMPARE(bindingsOfType.size(), 2); - QCOMPARE(value(bindingsOfType, u"enabled"_s).bindingType(), - QQmlJSMetaPropertyBinding::BoolLiteral); - QCOMPARE(value(bindingsOfType, u"forwardTo"_s).bindingType(), - QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfType, u"enabled"_s).bindingType(), QQmlSA::BindingType::BoolLiteral); + QCOMPARE(value(bindingsOfType, u"forwardTo"_s).bindingType(), QQmlSA::BindingType::Script); QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfBaseType; getBindingsWithinAttached(&bindingsOfBaseType, 1); QCOMPARE(bindingsOfBaseType.size(), 1); - QCOMPARE(value(bindingsOfBaseType, u"priority"_s).bindingType(), - QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfBaseType, u"priority"_s).bindingType(), QQmlSA::BindingType::Script); } inline QString getScopeName(const QQmlJSScope::ConstPtr &scope) { Q_ASSERT(scope); QQmlJSScope::ScopeType type = scope->scopeType(); - if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope) + if (type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope) return scope->internalName(); return scope->baseTypeName(); } @@ -507,7 +524,7 @@ void tst_qqmljsscope::scriptIndices() QmlIR::Document document(false); // we need QmlIR information here QQmlJSScope::ConstPtr root = run(u"functionAndBindingIndices.qml"_s, &document); QVERIFY(root); - QVERIFY(document.javaScriptCompilationUnit.unitData()); + QVERIFY(document.javaScriptCompilationUnit->unitData()); // compare QQmlJSScope and QmlIR: @@ -537,8 +554,9 @@ void tst_qqmljsscope::scriptIndices() const auto suitableScope = [](const QQmlJSScope::ConstPtr &scope) { const auto type = scope->scopeType(); - return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope - || type == QQmlJSScope::AttachedPropertyScope; + return type == QQmlSA::ScopeType::QMLScope + || type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope; }; QList<QQmlJSScope::ConstPtr> queue; @@ -550,7 +568,7 @@ void tst_qqmljsscope::scriptIndices() if (suitableScope(current)) { const auto methods = current->ownMethods(); for (const auto &method : methods) { - if (method.methodType() == QQmlJSMetaMethod::Signal) + if (method.methodType() == QQmlJSMetaMethodType::Signal) continue; QString name = method.methodName(); auto relativeIndex = method.jsFunctionIndex(); @@ -563,7 +581,7 @@ void tst_qqmljsscope::scriptIndices() const auto bindings = current->ownPropertyBindings(); for (const auto &binding : bindings) { - if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script) + if (binding.bindingType() != QQmlSA::BindingType::Script) continue; QString name = binding.propertyName(); auto relativeIndex = binding.scriptIndex(); @@ -674,28 +692,50 @@ void tst_qqmljsscope::emptyBlockBinding() QVERIFY(root->hasOwnPropertyBindings(u"y"_s)); } -void tst_qqmljsscope::qualifiedName() +void tst_qqmljsscope::hasOwnEnumerationKeys() { - QQmlJSScope::ConstPtr root = run(u"qualifiedName.qml"_s); + QQmlJSScope::ConstPtr root = run(u"extensions.qml"_s); QVERIFY(root); + QQmlJSScope::ConstPtr extendedDerived = root->childScopes().front(); + QVERIFY(extendedDerived); + // test that enumeration keys from base cannot be found + QVERIFY(!extendedDerived->hasOwnEnumerationKey(u"ThisIsTheEnumFromExtended"_s)); + QVERIFY(!extendedDerived->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtended"_s)); + + QQmlJSScope::ConstPtr extended = extendedDerived->baseType(); + QVERIFY(extended); + + QVERIFY(extended->hasOwnEnumerationKey(u"ThisIsTheEnumFromExtended"_s)); + QVERIFY(extended->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtended"_s)); + QVERIFY(!extended->hasOwnEnumerationKey(u"ThisIsTheEnumFromExtension"_s)); + QVERIFY(!extended->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtension"_s)); +} - auto qualifiedNameOf = [](const QQmlJSScope::ConstPtr &ptr) -> QString { - if (ptr->baseType()) - return ptr->baseType()->qualifiedName(); - else - return u""_s; - }; - - QCOMPARE(root->childScopes().size(), 4); - QQmlJSScope::ConstPtr b = root->childScopes()[0]; - QQmlJSScope::ConstPtr d = root->childScopes()[1]; - QQmlJSScope::ConstPtr qualifiedA = root->childScopes()[2]; - QQmlJSScope::ConstPtr qualifiedB = root->childScopes()[3]; - - QCOMPARE(qualifiedNameOf(b), "QualifiedNamesTests/B 5.0-6.0"); - QCOMPARE(qualifiedNameOf(d), "QualifiedNamesTests/D 6.0"); - QCOMPARE(qualifiedNameOf(qualifiedA), "QualifiedNamesTests/A 5.0"); - QCOMPARE(qualifiedNameOf(qualifiedB), "QualifiedNamesTests/B 5.0-6.0"); +void tst_qqmljsscope::ownModuleName() +{ + const QString moduleName = u"HelloModule"_s; + QQmlJSScope::ConstPtr root = run(u"ownModuleName.qml"_s); + QVERIFY(root); + QCOMPARE(root->moduleName(), moduleName); + QCOMPARE(root->ownModuleName(), moduleName); + + QCOMPARE(root->childScopes().size(), 2); + QQmlJSScope::ConstPtr child = root->childScopes().front(); + QVERIFY(child); + // only root and inline components have own module names, but the child should be able to query + // its component's module Name via moduleName() + QCOMPARE(child->ownModuleName(), QString()); + QCOMPARE(child->moduleName(), moduleName); + + QQmlJSScope::ConstPtr ic = root->childScopes()[1]; + QVERIFY(ic); + QCOMPARE(ic->ownModuleName(), moduleName); + QCOMPARE(ic->moduleName(), moduleName); + + QQmlJSScope::ConstPtr icChild = ic->childScopes().front(); + QVERIFY(icChild); + QCOMPARE(icChild->ownModuleName(), QString()); + QCOMPARE(icChild->moduleName(), moduleName); } void tst_qqmljsscope::resolvedNonUniqueScopes() @@ -711,32 +751,32 @@ void tst_qqmljsscope::resolvedNonUniqueScopes() { auto topLevelBindings = root->propertyBindings(u"Component"_s); QCOMPARE(topLevelBindings.size(), 1); - QCOMPARE(topLevelBindings[0].bindingType(), QQmlJSMetaPropertyBinding::AttachedProperty); + QCOMPARE(topLevelBindings[0].bindingType(), QQmlSA::BindingType::AttachedProperty); auto componentScope = topLevelBindings[0].attachingType(); auto componentBindings = componentScope->ownPropertyBindings(); QCOMPARE(componentBindings.size(), 2); auto onCompletedBinding = value(componentBindings, u"onCompleted"_s); QVERIFY(onCompletedBinding.isValid()); - QCOMPARE(onCompletedBinding.bindingType(), QQmlJSMetaPropertyBinding::Script); - QCOMPARE(onCompletedBinding.scriptKind(), QQmlJSMetaPropertyBinding::Script_SignalHandler); + QCOMPARE(onCompletedBinding.bindingType(), QQmlSA::BindingType::Script); + QCOMPARE(onCompletedBinding.scriptKind(), QQmlSA::ScriptBindingKind::SignalHandler); auto onDestructionBinding = value(componentBindings, u"onDestruction"_s); QVERIFY(onDestructionBinding.isValid()); - QCOMPARE(onDestructionBinding.bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(onDestructionBinding.bindingType(), QQmlSA::BindingType::Script); QCOMPARE(onDestructionBinding.scriptKind(), - QQmlJSMetaPropertyBinding::Script_SignalHandler); + QQmlSA::ScriptBindingKind::SignalHandler); } { auto topLevelBindings = root->propertyBindings(u"p"_s); QCOMPARE(topLevelBindings.size(), 1); - QCOMPARE(topLevelBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); + QCOMPARE(topLevelBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty); auto pScope = topLevelBindings[0].groupType(); auto pBindings = pScope->ownPropertyBindings(); QCOMPARE(pBindings.size(), 1); auto onXChangedBinding = value(pBindings, u"onXChanged"_s); QVERIFY(onXChangedBinding.isValid()); - QCOMPARE(onXChangedBinding.bindingType(), QQmlJSMetaPropertyBinding::Script); - QCOMPARE(onXChangedBinding.scriptKind(), QQmlJSMetaPropertyBinding::Script_SignalHandler); + QCOMPARE(onXChangedBinding.bindingType(), QQmlSA::BindingType::Script); + QCOMPARE(onXChangedBinding.scriptKind(), QQmlSA::ScriptBindingKind::SignalHandler); } } @@ -777,8 +817,8 @@ void tst_qqmljsscope::compilationUnitsAreCompatible() QmlIR::Document document(false); // we need QmlIR information here QVERIFY(run(url, &document)); - QVERIFY(document.javaScriptCompilationUnit.unitData()); - getRuntimeInfoFromCompilationUnit(document.javaScriptCompilationUnit.unitData(), + QVERIFY(document.javaScriptCompilationUnit->unitData()); + getRuntimeInfoFromCompilationUnit(document.javaScriptCompilationUnit->unitData(), cachegenFunctions); if (QTest::currentTestFailed()) return; @@ -791,5 +831,162 @@ void tst_qqmljsscope::compilationUnitsAreCompatible() QCOMPARE(uint(cachegenFunctions[i]->nameIndex), uint(componentFunctions[i]->nameIndex)); } +void tst_qqmljsscope::attachedTypeResolution_data() +{ + QTest::addColumn<bool>("creatable"); + QTest::addColumn<QString>("moduleName"); + QTest::addColumn<QString>("typeName"); + QTest::addColumn<QString>("attachedTypeName"); + QTest::addColumn<QString>("propertyOnSelf"); + QTest::addColumn<QString>("propertyOnAttached"); + + QTest::addRow("ListView") << true + << "QtQuick" + << "ListView" + << "QQuickListViewAttached" + << "orientation" + << ""; + QTest::addRow("Keys") << false + << "QtQuick" + << "Keys" + << "QQuickKeysAttached" + << "priority" + << "priority"; +} + +class TestPass : public QQmlSA::ElementPass +{ +public: + TestPass(QQmlSA::PassManager *manager) : QQmlSA::ElementPass(manager) { } + bool shouldRun(const QQmlSA::Element &) override { return true; } + void run(const QQmlSA::Element &) override { } +}; + +using PassManagerPtr = std::unique_ptr< + QQmlSA::PassManager, decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>; + +void tst_qqmljsscope::attachedTypeResolution() +{ + QFETCH(bool, creatable); + QFETCH(QString, moduleName); + QFETCH(QString, typeName); + QFETCH(QString, attachedTypeName); + QFETCH(QString, propertyOnSelf); + QFETCH(QString, propertyOnAttached); + + std::unique_ptr<QQmlJSLogger> logger = std::make_unique<QQmlJSLogger>(); + QFile qmlFile("data/attachedTypeResolution.qml"); + if (!qmlFile.open(QIODevice::ReadOnly | QIODevice::Text)) + QSKIP("Unable to open qml file"); + + logger->setCode(qmlFile.readAll()); + logger->setFileName(QString(qmlFile.filesystemFileName().string().c_str())); + QQmlJSImporter importer{ { "data" }, nullptr, true }; + QStringList defaultImportPaths = + QStringList{ QLibraryInfo::path(QLibraryInfo::QmlImportsPath) }; + importer.setImportPaths(defaultImportPaths); + QQmlJSTypeResolver resolver(&importer); + const auto &implicitImportDirectory = QQmlJSImportVisitor::implicitImportDirectory( + logger->fileName(), importer.resourceFileMapper()); + QQmlJSImportVisitor v{ + QQmlJSScope::create(), &importer, logger.get(), implicitImportDirectory, {} + }; + + PassManagerPtr manager( + QQmlSA::PassManagerPrivate::createPassManager(&v, &resolver), + &QQmlSA::PassManagerPrivate::deletePassManager); + + TestPass pass{ manager.get() }; + const auto &resolved = pass.resolveType(moduleName, typeName); + + QVERIFY(!resolved.isNull()); + const auto &attachedType = pass.resolveAttached(moduleName, typeName); + QVERIFY(!attachedType.isNull()); + QCOMPARE(attachedType.name(), attachedTypeName); + + if (propertyOnAttached != "") { + QEXPECT_FAIL("Keys", "Keys and QQuickKeysAttached have the same properties", Continue); + QVERIFY(!resolved.hasProperty(propertyOnAttached)); + QVERIFY(attachedType.hasProperty(propertyOnAttached)); + } + if (propertyOnSelf != "") { + QEXPECT_FAIL("Keys", "Keys and QQuickKeysAttached have the same properties", Continue); + QVERIFY(!attachedType.hasProperty(propertyOnSelf)); + } + + if (creatable) + QVERIFY(resolved.hasProperty(propertyOnSelf)); +} + + + +void tst_qqmljsscope::builtinTypeResolution_data() +{ + QTest::addColumn<bool>("valid"); + QTest::addColumn<QString>("typeName"); + + QTest::addRow("global_QtObject") << true << "Qt"; + QTest::addRow("function") << true << "function"; + QTest::addRow("Array") << true << "Array"; + QTest::addRow("invalid") << false << "foobar"; + QTest::addRow("Number") << true << "Number"; + QTest::addRow("bool") << true << "bool"; + QTest::addRow("QString") << true << "QString"; +} + +void tst_qqmljsscope::builtinTypeResolution() +{ + QFETCH(bool, valid); + QFETCH(QString, typeName); + + QQmlJSImporter importer{ { "data" }, nullptr, true }; + QStringList defaultImportPaths = + QStringList{ QLibraryInfo::path(QLibraryInfo::QmlImportsPath) }; + importer.setImportPaths(defaultImportPaths); + QQmlJSTypeResolver resolver(&importer); + const auto &implicitImportDirectory = QQmlJSImportVisitor::implicitImportDirectory({}, nullptr); + QQmlJSLogger logger; + QQmlJSImportVisitor v{ + QQmlJSScope::create(), &importer, &logger, implicitImportDirectory, {} + }; + + PassManagerPtr manager( + QQmlSA::PassManagerPrivate::createPassManager(&v, &resolver), + &QQmlSA::PassManagerPrivate::deletePassManager); + + TestPass pass{ manager.get() }; + auto element = pass.resolveBuiltinType(typeName); + QCOMPARE(element.isNull(), !valid); +} + +void tst_qqmljsscope::methodAndSignalSourceLocation() +{ + QmlIR::Document document(false); + auto jsscope = run(u"methodAndSignalSourceLocation.qml"_s, false); + + std::array<std::array<int, 9>, 2> offsetsByLineEnding = { + std::array{ 29, 51, 74, 102, 128, 160, 219, 235, 257 }, // 1 char line endings + std::array{ 32, 55, 79, 108, 135, 168, 231, 248, 271 } // 2 char line endinds + }; + + // Try to detect the size of line endings as they lead to different source locations + auto offset1 = jsscope->methods("f1")[0].sourceLocation().offset; + QVERIFY(offset1 == 29 || offset1 == 32); + bool oneCharEndings = offset1 == 29; + std::array<int, 9> &offsets = oneCharEndings ? offsetsByLineEnding[0] : offsetsByLineEnding[1]; + + using namespace QQmlJS; + QCOMPARE(jsscope->methods("f1")[0].sourceLocation(), SourceLocation(offsets[0], 17, 4, 5)); + QCOMPARE(jsscope->methods("f2")[0].sourceLocation(), SourceLocation(offsets[1], 18, 5, 5)); + QCOMPARE(jsscope->methods("f3")[0].sourceLocation(), SourceLocation(offsets[2], 23, 6, 5)); + QCOMPARE(jsscope->methods("f4")[0].sourceLocation(), SourceLocation(offsets[3], 21, 7, 5)); + QCOMPARE(jsscope->methods("f5")[0].sourceLocation(), SourceLocation(offsets[4], 27, 8, 5)); + QCOMPARE(jsscope->methods("f6")[0].sourceLocation(), SourceLocation(offsets[5], oneCharEndings ? 53 : 55, 9, 5)); + + QCOMPARE(jsscope->methods("s1")[0].sourceLocation(), SourceLocation(offsets[6], 11, 13, 5)); + QCOMPARE(jsscope->methods("s2")[0].sourceLocation(), SourceLocation(offsets[7], 17, 14, 5)); + QCOMPARE(jsscope->methods("s3")[0].sourceLocation(), SourceLocation(offsets[8], 28, 15, 5)); +} + QTEST_MAIN(tst_qqmljsscope) #include "tst_qqmljsscope.moc" diff --git a/tests/auto/qml/qqmllanguage/CMakeLists.txt b/tests/auto/qml/qqmllanguage/CMakeLists.txt index e07f741bf6..9fff8f2118 100644 --- a/tests/auto/qml/qqmllanguage/CMakeLists.txt +++ b/tests/auto/qml/qqmllanguage/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmllanguage Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllanguage LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -27,6 +33,8 @@ qt_internal_add_test(tst_qqmllanguage TESTDATA ${test_data} ) +add_subdirectory(testhelper) + #### Keys ignored in scope 1:.:.:qqmllanguage.pro:<TRUE>: # OTHER_FILES = "data/readonlyObjectProperty.qml" # QML_IMPORT_NAME = "StaticTest" diff --git a/tests/auto/qml/qqmllanguage/data/AliasHolder.qml b/tests/auto/qml/qqmllanguage/data/AliasHolder.qml new file mode 100644 index 0000000000..42aed6ed26 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/AliasHolder.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + property alias strokeStyle: path.restoreMode + property Binding p: Binding { id: path } +} diff --git a/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml b/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml new file mode 100644 index 0000000000..0ec43bf7aa --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml @@ -0,0 +1,8 @@ +import QtQml +import Test + +QtObject { + enum A { B, C, D } + property int e: CompositeTypeWithEnumSelfReference.C + property int f: CompositeTypeWithEnumSelfReference.A.D +} diff --git a/tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml b/tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml new file mode 100644 index 0000000000..232c755bfb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + function dothing() { console.log("do") } +} diff --git a/tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml b/tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml new file mode 100644 index 0000000000..713c760a04 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property IconPropertiesGroup handleOpenIcon: IconPropertiesGroup {} +} diff --git a/tests/auto/qml/qqmllanguage/data/Comps/qmldir b/tests/auto/qml/qqmllanguage/data/Comps/qmldir new file mode 100644 index 0000000000..0a68a376f6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/Comps/qmldir @@ -0,0 +1,4 @@ +module Comps +OverlayDrawer 254.0 OverlayDrawer.qml +IconPropertiesGroup 254.0 IconPropertiesGroup.qml + diff --git a/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml b/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml new file mode 100644 index 0000000000..134eacf913 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml @@ -0,0 +1,27 @@ +import QtQml + +QtObject { + id: root + objectName: "theRoot" + + component ObjectWithColor: QtObject { + property string color + property var varvar + } + + property ObjectWithColor border: ObjectWithColor { + objectName: root.objectName + color: root.trueBorderColor + varvar: root.trueBorderVarvar + } + + readonly property rect readonlyRect: ({x: 12, y: 13, width: 14, height: 15}) + + property alias borderObjectName: root.border.objectName + property alias borderColor: root.border.color + property alias borderVarvar: root.border.varvar + property alias readonlyRectX: root.readonlyRect.x + + property string trueBorderColor: "green" + property var trueBorderVarvar: 1234 +} diff --git a/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml new file mode 100644 index 0000000000..f549e851a3 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired1.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQml + +QtObject { + required property int i +} diff --git a/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml new file mode 100644 index 0000000000..1f9e7e3a42 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/SingletonWithRequired2.qml @@ -0,0 +1,8 @@ +pragma Singleton +import QtQml + +QtObject { + property QtObject o: QtObject { + required property int i + } +} diff --git a/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir new file mode 100644 index 0000000000..46e397ca76 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SingletonWithRequiredProperties/qmldir @@ -0,0 +1,4 @@ +module SingletonWithRequiredProperties + +singleton SingletonWithRequired1 1.0 SingletonWithRequired1.qml +singleton SingletonWithRequired2 1.0 SingletonWithRequired2.qml diff --git a/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml new file mode 100644 index 0000000000..6186faa00b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml @@ -0,0 +1,15 @@ +import QtQml + +QtObject { + id: self + property QtObject b + property Component c + function a() : TypeAnnotationCycle2 { return c.createObject() as TypeAnnotationCycle2 } + + Component.onCompleted: { + c = Qt.createComponent("TypeAnnotationCycle2.qml"); + let v = a(); + v.addTypeAnnotationCycle1(self as TypeAnnotationCycle1); + b = v.b; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml new file mode 100644 index 0000000000..9e3ffa86d2 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + property QtObject b + function addTypeAnnotationCycle1(c: TypeAnnotationCycle1) { b = c; } +} diff --git a/tests/auto/qml/qqmllanguage/data/UIToolBar.qml b/tests/auto/qml/qqmllanguage/data/UIToolBar.qml new file mode 100644 index 0000000000..08a22d2492 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/UIToolBar.qml @@ -0,0 +1,10 @@ +import QtQml + +QtObject { + id: root + signal doneClicked() + signal foo() + + onObjectNameChanged: foo() + Component.onCompleted: root.foo.connect(root.doneClicked) +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.16.qml b/tests/auto/qml/qqmllanguage/data/alias.16.qml index 4637aec58f..335d240003 100644 --- a/tests/auto/qml/qqmllanguage/data/alias.16.qml +++ b/tests/auto/qml/qqmllanguage/data/alias.16.qml @@ -2,7 +2,7 @@ import QtQuick 2.0 import QtQuick.Window 2.0 Window { - visible: true + visible: false property alias list: repeater.model diff --git a/tests/auto/qml/qqmllanguage/data/aliasWriter.qml b/tests/auto/qml/qqmllanguage/data/aliasWriter.qml new file mode 100644 index 0000000000..4001c2af34 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/aliasWriter.qml @@ -0,0 +1,5 @@ +import QtQml + +AliasHolder { + strokeStyle: 1 +} diff --git a/tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml b/tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml new file mode 100644 index 0000000000..64c31b46d6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml @@ -0,0 +1,16 @@ +import QtQuick +import Comps as Comps + +Comps.OverlayDrawer { + id: self + + property var dothing + + Component.onCompleted: dothing = handleOpenIcon.dothing + + function dodo() { dothing() } + + function testInstanceOf() : bool { + return self instanceof Comps.OverlayDrawer + } +} diff --git a/tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml b/tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml new file mode 100644 index 0000000000..428ccd5eef --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml @@ -0,0 +1,16 @@ +import QtQml + +QtObject { + id: root + + component MyItem: QtObject { + property int value: 10 + onValueChanged: root.objectName = "value: " + value + } + + property Instantiator i: Instantiator { + id: loader + delegate: MyItem {} + onObjectChanged: (loader.object as MyItem).value = 20 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/asValueType.qml b/tests/auto/qml/qqmllanguage/data/asValueType.qml new file mode 100644 index 0000000000..b51dc9c6ec --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/asValueType.qml @@ -0,0 +1,20 @@ +pragma ValueTypeBehavior: Addressable +import QtQml +import StaticTest + +QtObject { + property var a + property rect b: a as rect + property bool c: a instanceof rect + property bool d: ({x: 10, y: 20}) instanceof point + property var e: ({x: 10, y: 20}) as point + property var f: "red" as withString + property var g: "green" as string + property rect bb + property var p: bb as size; + property var q: this as size; + property var r: ({}) as size; + property var s: 11 as size; + property var t: Component as size; + property var u: Qt as size; +} diff --git a/tests/auto/qml/qqmllanguage/data/asValueTypeGood.qml b/tests/auto/qml/qqmllanguage/data/asValueTypeGood.qml new file mode 100644 index 0000000000..777ada3848 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/asValueTypeGood.qml @@ -0,0 +1,35 @@ +pragma ValueTypeBehavior: Assertable +import QtQml as Q +import StaticTest as S + +Q.QtObject { + property var a + property rect b: a as Q.rect + property bool c: a instanceof Q.rect + property bool d: ({x: 10, y: 20}) instanceof Q.point + property var e: ({x: 10, y: 20}) as Q.point + property var f: "red" as S.withString + property var g: "green" as Q.string + + property var h: new S.withString("red") + property var i: { + let p = new Q.point; + p.x = 10 + p.y = 20 + return p + } + + property var j: 4.0 as Q.int + property var k: (4.5 / 1.5) as Q.int + property var l: 5 as Q.double + property var m: "something" as Q.var + property var n: 1 as Q.bool + property var o: Infinity as Q.int + + property var p: b as Q.size; + property var q: this as Q.size; + property var r: ({}) as Q.size; + property var s: 11 as Q.size; + property var t: Q.Component as Q.size; + property var u: Q.Qt as Q.size; +} diff --git a/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml new file mode 100644 index 0000000000..6f0db53f2a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml @@ -0,0 +1,25 @@ +import QtQml + +QtObject { + id: self + + function doStuff(status: Binding.NotAnInlineComponent) : int { + return status + } + + function doStuff2(status: InlineComponentBase.IC) : QtObject { + return status + } + + function doStuff3(status: InlineComponentBase.NotIC) : QtObject { + return status + } + + property InlineComponentBase.IC ic: InlineComponentBase.IC {} + + property int a: doStuff(5) + property QtObject b: doStuff2(ic) + property QtObject c: doStuff3(ic) + property QtObject d: doStuff2(self) +} + diff --git a/tests/auto/qml/qqmllanguage/data/SingletonTest.qml b/tests/auto/qml/qqmllanguage/data/badSingleton/SingletonTest.qml index 70e1671754..70e1671754 100644 --- a/tests/auto/qml/qqmllanguage/data/SingletonTest.qml +++ b/tests/auto/qml/qqmllanguage/data/badSingleton/SingletonTest.qml diff --git a/tests/auto/qml/qqmllanguage/data/qmldir b/tests/auto/qml/qqmllanguage/data/badSingleton/qmldir index c946de657c..c946de657c 100644 --- a/tests/auto/qml/qqmllanguage/data/qmldir +++ b/tests/auto/qml/qqmllanguage/data/badSingleton/qmldir diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_85932.qml b/tests/auto/qml/qqmllanguage/data/badSingleton/qtbug_85932.qml index aa21558220..aa21558220 100644 --- a/tests/auto/qml/qqmllanguage/data/qtbug_85932.qml +++ b/tests/auto/qml/qqmllanguage/data/badSingleton/qtbug_85932.qml diff --git a/tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml b/tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml new file mode 100644 index 0000000000..dc0e145064 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml @@ -0,0 +1,13 @@ +import QtQml + +QtObject { + property var b; + + function returnList(a: QtObject) : list<QtObject> { + return [a] + } + + function setB(a: QtObject) { + b = { b: returnList(a) } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml b/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml new file mode 100644 index 0000000000..50eaa7c3e2 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml @@ -0,0 +1,9 @@ +import QtQml + +DeepAliasOnIC { + borderObjectName: "theLeaf" + borderColor: "black" + borderVarvar: "mauve" +} + + diff --git a/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml b/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml new file mode 100644 index 0000000000..f5ae62406b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml @@ -0,0 +1,5 @@ +import QtQml + +DeepAliasOnIC { + readonlyRectX: 55 +} diff --git a/tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml b/tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml new file mode 100644 index 0000000000..b508474a36 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml @@ -0,0 +1,6 @@ +import Test + +DerivedFromUnexposedBase { + group.value: 42 + groupGadget.value: 42 +} diff --git a/tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml b/tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml new file mode 100644 index 0000000000..2fffea25c6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml @@ -0,0 +1,5 @@ +import Test + +DerivedFromUnexposedBase { + dynamic.value: "This should fail" +} diff --git a/tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml b/tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml new file mode 100644 index 0000000000..b713d2aa24 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml @@ -0,0 +1,10 @@ +import Test + +EnumPropsManyUnderlyingTypes { + si8prop: EnumPropsManyUnderlyingTypes.ResolvedValue + ui8prop: EnumPropsManyUnderlyingTypes.ResolvedValue + si16prop: EnumPropsManyUnderlyingTypes.ResolvedValue + ui16prop: EnumPropsManyUnderlyingTypes.ResolvedValue + si64prop: EnumPropsManyUnderlyingTypes.ResolvedValue + ui64prop: EnumPropsManyUnderlyingTypes.ResolvedValue +} diff --git a/tests/auto/qml/qqmllanguage/data/enumScopes.qml b/tests/auto/qml/qqmllanguage/data/enumScopes.qml new file mode 100644 index 0000000000..c71872387f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/enumScopes.qml @@ -0,0 +1,16 @@ +import QtQml 2.15 +import EnumScopeTest 1.0 + +QtObject { + property NonSingleton n: NonSingleton { + id: nonSingleton + } + + property bool singletonUnscoped: Singleton.enumProperty === Singleton.EnumValue2 + property bool singletonScoped: Singleton.enumProperty === Singleton.EnumType.EnumValue2 + property bool nonSingletonUnscoped: nonSingleton.enumProperty === NonSingleton.EnumValue2 + property bool nonSingletonScoped: nonSingleton.enumProperty === NonSingleton.EnumType.EnumValue2 + + property int singletonScopedValue: EnumProviderSingleton.Expected.Value + property int singletonUnscopedValue: EnumProviderSingleton.Value +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml new file mode 100644 index 0000000000..902dfb501d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml @@ -0,0 +1,27 @@ +import QtQml + +QtObject { + component C1: QtObject { + property Component comp: null + } + + component C2: C1 { + comp: QtObject { + objectName: "green" + } + } + + component C3: C1 { + comp: Component { + QtObject { + objectName: "blue" + } + } + } + + property QtObject c1: C1 {} + property QtObject c2: C2 {} + property QtObject c3: C3 {} + + objectName: c2.comp.createObject().objectName + " " + c3.comp.createObject().objectName +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt index d76f18ba89..5ddb8a2e6d 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt @@ -1 +1 @@ -5:5:Invalid grouped property access: Property "o" with type "QVariant", which is not a value type +5:7:Cannot assign to non-existent property "blah" diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt index 9a0422753f..ced96fba83 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt @@ -1 +1 @@ -4:5:Invalid grouped property access: Property "customType" with type "MyCustomVariantType", which is not a value type +4:5:Invalid grouped property access: Property "customType" with type "MyCustomVariantType", which is neither a value nor an object type diff --git a/tests/auto/qml/qqmllanguage/data/invokableCtors.qml b/tests/auto/qml/qqmllanguage/data/invokableCtors.qml new file mode 100644 index 0000000000..35a8d7bf08 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invokableCtors.qml @@ -0,0 +1,12 @@ +import QtQml as QQ +import Test as VV + +QQ.QtObject { + property QQ.QtObject oo: new QQ.QtObject() + property QQ.QtObject pp: new QQ.QtObject(oo) + property VV.vv v: new VV.vv("green") + + property VV.InvokableSingleton i: new VV.InvokableSingleton(5, oo) + property VV.InvokableExtended k: new VV.InvokableExtended() + property VV.InvokableUncreatable l: new VV.InvokableUncreatable() +} diff --git a/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml new file mode 100644 index 0000000000..460d4667f8 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { + id: self + property int a: 3 + property var result + Component.onCompleted: { + var sum = 0 + let f = function() { + return self.notthere ?? self.a + } + + // Not enough times for the jit to kick in (should run on the interpreter) + for (let i = 0; i < 1; i++) { + sum = sum + f() + } + result = sum + } +} diff --git a/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml new file mode 100644 index 0000000000..43c82f436c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQml + +QtObject { + id: self + property int a: 3 + property int result + Component.onCompleted: { + var sum = 0 + let f = function() { + return self.notthere ?? self.a + } + + // Enough times for the jit to kick in (should run on the jit) + for (let i = 0; i < 50; i++) { + sum = sum + f() + } + result = sum + } +} diff --git a/tests/auto/qml/qqmllanguage/data/jitExceptions.qml b/tests/auto/qml/qqmllanguage/data/jitExceptions.qml new file mode 100644 index 0000000000..c5e11af31d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/jitExceptions.qml @@ -0,0 +1,16 @@ +import QtQml + +QtObject { + function burn() { + return control.font + } + + Component.onCompleted: { + for (var a = 0; a < 10; ++a) { + try { burn() } catch(e) {} + } + + burn(); + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/jsonArrayProperty.qml b/tests/auto/qml/qqmllanguage/data/jsonArrayProperty.qml new file mode 100644 index 0000000000..5bd563a288 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/jsonArrayProperty.qml @@ -0,0 +1,191 @@ +import QtQml +import TypeWithQJsonArrayProperty + +TypeWithQJsonArrayProperty { + function jsArray() { return [1, 2, 3] } + + jsonArray: jsArray() + + property list<int> concatenatedJsonArray: jsonArray.concat([4, 5, 6]) + property list<int> concatenatedJsArray: jsArray().concat([4, 5, 6]) + + property bool entriesMatch: { + var iterator = jsonArray.entries(); + for (var [index, element] of jsArray().entries()) { + var v = iterator.next().value; + if (index !== v[0] || element !== v[1]) { + console.log(index, v[0], element, v[1]); + return false; + } + } + + var iterator = jsArray().entries(); + for (var [index, element] of jsonArray.entries()) { + var v = iterator.next().value; + if (index !== v[0] || element !== v[1]) { + console.log(index, v[0], element, v[1]); + return false; + } + } + + return true; + } + + property bool jsonArrayEvery: jsonArray.every(element => element != 0) + property bool jsArrayEvery: jsArray().every(element => element != 0) + + property list<int> jsonArrayFiltered: jsonArray.filter(element => element > 2) + property list<int> jsArrayFiltered: jsArray().filter(element => element > 2) + + property int jsonArrayFind: jsonArray.find(element => element === 2) + property int jsArrayFind: jsArray().find(element => element === 2) + + property int jsonArrayFindIndex: jsonArray.findIndex(element => element === 1) + property int jsArrayFindIndex: jsArray().findIndex(element => element === 1) + + property string jsonArrayForEach + property string jsArrayForEach + + property bool jsonArrayIncludes: jsonArray.includes(3) + property bool jsArrayIncludes: jsArray().includes(3) + + property int jsonArrayIndexOf: jsonArray.indexOf(2) + property int jsArrayIndexOf: jsArray().indexOf(2) + + property string jsonArrayJoin: jsonArray.join() + property string jsArrayJoin: jsArray().join() + + property bool keysMatch: { + var iterator = jsonArray.keys(); + for (var index of jsArray().keys()) { + var v = iterator.next().value; + if (index !== v) { + console.log(index, v); + return false; + } + } + + var iterator = jsArray().keys(); + for (var index of jsonArray.keys()) { + var v = iterator.next().value; + if (index !== v) { + console.log(index, v); + return false; + } + } + + return true; + } + + property int jsonArrayLastIndexOf: jsonArray.lastIndexOf(1) + property int jsArrayLastIndexOf: jsArray().lastIndexOf(1) + + property list<string> jsonArrayMap: jsonArray.map(element => element.toString()) + property list<string> jsArrayMap: jsArray().map(element => element.toString()) + + property int jsonArrayReduce: jsonArray.reduce((acc, element) => acc - element, 40) + property int jsArrayReduce: jsArray().reduce((acc, element) => acc - element, 40) + + property string jsonArrayReduceRight: jsonArray.reduceRight((acc, element) => acc + element.toString(), "") + property string jsArrayReduceRight: jsArray().reduceRight((acc, element) => acc + element.toString(), "") + + property list<int> jsonArraySlice: jsonArray.slice(0, 1) + property list<int> jsArraySlice: jsArray().slice(0, 1) + + property bool jsonArraySome: jsonArray.some(element => element === 1) + property bool jsArraySome: jsArray().some(element => element === 1) + + property string stringifiedLocaleJsonArray: jsonArray.toLocaleString() + property string stringifiedLocaleJsArray: jsArray().toLocaleString() + + property string stringifiedJsonArray: jsonArray.toString() + property string stringifiedJsArray: jsArray().toString() + + property bool valuesMatch: { + var iterator = jsonArray.values(); + for (var obj of jsArray().values()) { + var v = iterator.next().value; + if (obj !== v) { + console.log(obj, v); + return false; + } + } + + var iterator = jsArray().values(); + for (var obj of jsonArray.values()) { + var v = iterator.next().value; + if (obj !== v) { + console.log(obj, v); + return false; + } + } + + return true; + } + + // In-place mutation methods. + // Set by onCompleted if mutating jsonArray and then accessing it + // respects the mutation and the mutation behaves as for an array. + property bool jsonArrayWasCopiedWithin: false + property bool jsonArrayWasFilled: false + property bool jsonArrayWasPopped: false + property bool jsonArrayWasPushed: false + property bool jsonArrayWasReversed: false + property bool jsonArrayWasShifted: false + property bool jsonArrayWasSpliced: false + property bool jsonArrayWasUnshifted: false + property bool jsonArrayWasSorted: false + + Component.onCompleted: { + function equals(lhs, rhs) { + return lhs.toString() === rhs.toString() + } + + jsonArray.forEach(element => jsonArrayForEach += "-" + element + "-"); + jsArray().forEach(element => jsArrayForEach += "-" + element + "-"); + + var array = jsArray() + + jsonArray.copyWithin(1, 0, 1) + array.copyWithin(1, 0, 1) + jsonArrayWasCopiedWithin = equals(jsonArray, array) + + jsonArray.fill(7, 0, 1) + array.fill(7, 0, 1) + jsonArrayWasFilled = equals(jsonArray, array) + + jsonArray.pop() + array.pop() + jsonArrayWasPopped = equals(jsonArray, array) + + jsonArray.push(23) + jsonArray.push(11) + jsonArray.push(54) + jsonArray.push(42) + array.push(23) + array.push(11) + array.push(54) + array.push(42) + jsonArrayWasPushed = equals(jsonArray, array) + + jsonArray.reverse() + array.reverse() + jsonArrayWasReversed = equals(jsonArray, array) + + jsonArray.shift() + array.shift() + jsonArrayWasShifted = equals(jsonArray, array) + + jsonArray.splice(2, 1, [1, 2], 7, [1, 5]) + array.splice(2, 1, [1, 2], 7, [1, 5]) + jsonArrayWasSpliced = equals(jsonArray, array) + + jsonArray.unshift(4, 71) + array.unshift(4, 71) + jsonArrayWasUnshifted = equals(jsonArray, array) + + jsonArray.sort() + array.sort() + jsonArrayWasSorted = equals(jsonArray, array) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/longConversion.qml b/tests/auto/qml/qqmllanguage/data/longConversion.qml new file mode 100644 index 0000000000..fd9a9518ee --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/longConversion.qml @@ -0,0 +1,28 @@ +import QtQml +import Test + +QtObject { + property bool testProp: GetterObject.getFalse() + property bool testQProp: GetterObject.getQFalse() + + property bool fromLocal + property bool fromQLocal + + property bool fromBoolean + property bool fromQBoolean + + Component.onCompleted: { + let l = GetterObject.getFalse(); + fromLocal = l; + + let b = Boolean(l); + fromBoolean = b; + + let ql = GetterObject.getQFalse(); + fromQLocal = ql; + + + let qb = Boolean(ql); + fromQBoolean = qb; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml b/tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml new file mode 100644 index 0000000000..1ee71e5fd2 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml @@ -0,0 +1,11 @@ +import QtQml + +QtObject { + Component.onDestruction: { + console.log("evil!"); + } + + Component.onCompleted: { + Component.onDestruction() + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nestedVectors.qml b/tests/auto/qml/qqmllanguage/data/nestedVectors.qml new file mode 100644 index 0000000000..0bcea52133 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nestedVectors.qml @@ -0,0 +1,27 @@ +import Test +import QtQml + +NestedVectors { + id: self + + property var list1 + + Component.onCompleted: { + list1 = self.getList() + + let list2 = [] + let data1 = [] + data1.push(2) + data1.push(3) + data1.push(4) + + let data2 = [] + data2.push(5) + data2.push(6) + + list2.push(data1) + list2.push(data2) + + self.setList(list2) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml index acd5463a3c..ac5622f9fb 100644 --- a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml +++ b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml @@ -10,7 +10,7 @@ Item { } } - property bool expectNull: null + property bool expectNull: { return null; } function setExpectNull(b) { success = false; diff --git a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml index ed0e0d10f0..3c18739c32 100644 --- a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml +++ b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml @@ -10,7 +10,7 @@ Item { } } - property bool expectNull: null + property bool expectNull: { return null; } function setExpectNull(b) { success = false; diff --git a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml index f5e94ba715..e2e560199f 100644 --- a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml +++ b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml @@ -10,7 +10,7 @@ Item { } } - property bool expectNull: null + property bool expectNull: { return null; } function setExpectNull(b) { success = false; diff --git a/tests/auto/qml/qqmllanguage/data/objectInList.qml b/tests/auto/qml/qqmllanguage/data/objectInList.qml new file mode 100644 index 0000000000..53c8c3cdd1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/objectInList.qml @@ -0,0 +1,17 @@ +import QtQml + +QtObject { + objectName: "parent" + property list<QtObject> child + property Component c: QtObject { objectName: "child" } + + function doCreate() { + child.push(c.createObject(null)); + } + + Component.onCompleted: { + // Extra function call so that the created object cannot be on the stack + doCreate(); + gc(); + } +} diff --git a/tests/auto/qml/qqmllanguage/data/objectMethodClone.qml b/tests/auto/qml/qqmllanguage/data/objectMethodClone.qml new file mode 100644 index 0000000000..e21179ea14 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/objectMethodClone.qml @@ -0,0 +1,23 @@ +import QtQml + +QtObject { + id: window + + property int doneClicks: 0 + + property UIToolBar t1: UIToolBar { + objectName: window.objectName + onDoneClicked: window.doneClicks++ + } + + property UIToolBar t2: UIToolBar { + objectName: window.objectName + onDoneClicked: window.doneClicks++ + } + + property Timer timer: Timer { + interval: 10 + running: true + onTriggered: window.objectName = "bar" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/optimizedSequenceShift.qml b/tests/auto/qml/qqmllanguage/data/optimizedSequenceShift.qml new file mode 100644 index 0000000000..32765895a0 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/optimizedSequenceShift.qml @@ -0,0 +1,14 @@ +import QtQml + +QtObject { + id: root + + property int changes: 0 + + property list<int> numbers: [1, 2, 3, 4, 5] + onNumbersChanged: ++changes + + property var one: numbers.shift.bind([1,2,3])() + + Component.onCompleted: root.numbers.shift() +} diff --git a/tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml b/tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml new file mode 100644 index 0000000000..00029e3953 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml @@ -0,0 +1,10 @@ +import QtQml + +QtObject { + id: root + property QtObject target: null + + Component.onCompleted: { + target?.destroy( ) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml b/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml new file mode 100644 index 0000000000..69f9316c51 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml @@ -0,0 +1,6 @@ +import QtQuick + +Item { + property list<var> data: [] + Item {} +} diff --git a/tests/auto/qml/qqmllanguage/data/retainThis.qml b/tests/auto/qml/qqmllanguage/data/retainThis.qml new file mode 100644 index 0000000000..7a372ee236 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/retainThis.qml @@ -0,0 +1,49 @@ +import QmlOtherThis +import QtQml + +QtObject { + property var cppMethod: objA.greet + property var cppMethod2: objA.sum + + property Greeter a: Greeter { id: objA; objectName: "objA" } + property Greeter b: Greeter { id: objB; objectName: "objB" } + + function doCall() { + cppMethod.call(objB) + cppMethod2(5, 6) + } + + property var cppMethod3; + function doRetrieve(g) { + cppMethod3 = g.greet; + } + + function doCall2() { + cppMethod3(); + } + + property Greeter c: Greeter { + id: objC + objectName: "objC" + + property var cppMethod: objC.sum + + function doCall() { + cppMethod(7, 7) + } + } + + Component.onCompleted: { + doCall(); + doCall(); + + doRetrieve(objA); + doCall2(); + doRetrieve(objB); + doCall2(); + + objC.doCall(); + objC.cppMethod = objB.sum; + objC.doCall(); + } +} diff --git a/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml b/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml index e2ddd75b55..ec00d5d2b3 100644 --- a/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml +++ b/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml @@ -8,7 +8,7 @@ QtObject { property withLength withLength: 5 function a(r: rect) { - r.x = 77 // does not write back + r.x = 77 // does write back, but this is an evil thing to do. } function b(s: string) : int { @@ -28,8 +28,11 @@ QtObject { property int n: c(99) // creates a withLength property int o: rect.y + function bad(b: int) { return b } + Component.onCompleted: { a(rect) d(rect) + bad(15) } } diff --git a/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml b/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml index 0e9d0f42dc..bdb373040d 100644 --- a/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml +++ b/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml @@ -1,6 +1,6 @@ +pragma FunctionSignatureBehavior: Ignored import StaticTest import QtQml - QtObject { property rect rect: ({ x: 12, y: 13 }) property withLength withLength: 5 diff --git a/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml b/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml index 5359157b31..b86477e40a 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml +++ b/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml @@ -1,5 +1,5 @@ // Copyright (C) 2013 BlackBerry Limited. All rights reserved. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 pragma Singleton diff --git a/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js b/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js index 6e28624bb0..50ed2b0e66 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js +++ b/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js @@ -1,5 +1,5 @@ // Copyright (C) 2013 BlackBerry Limited. All rights reserved. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only .pragma library diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest17.qml b/tests/auto/qml/qqmllanguage/data/singletonTest17.qml index 4a987e31c0..7c7bffa4ac 100644 --- a/tests/auto/qml/qqmllanguage/data/singletonTest17.qml +++ b/tests/auto/qml/qqmllanguage/data/singletonTest17.qml @@ -1,5 +1,5 @@ // Copyright (C) 2013 BlackBerry Limited. All rights reserved. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import org.qtproject.Test 1.0 diff --git a/tests/auto/qml/qqmllanguage/data/typedObjectList.qml b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml new file mode 100644 index 0000000000..7e6f6e8dd9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml @@ -0,0 +1,10 @@ +import QtQml + +QtObject { + property var b; + property Component c: QtObject {} + + function returnList(a: Component) : list<Component> { return [a] } + + Component.onCompleted: b = { b: returnList(c) } +} diff --git a/tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml b/tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml new file mode 100644 index 0000000000..70484d3f0e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml @@ -0,0 +1,10 @@ +import QtQml +import Test + +UnregisteredValueTypeHandler { + Component.onCompleted: { + consume(produce()) + consume(produceDerived()) + consume(produceGadgeted()) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/variantObjectList.qml b/tests/auto/qml/qqmllanguage/data/variantObjectList.qml new file mode 100644 index 0000000000..9ec7d4f90f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/variantObjectList.qml @@ -0,0 +1,17 @@ +import QtQml +import People + +QtObject { + id: root + + property QtObject b: QtObject { id: g1; objectName: "Leo Hodges" } + property QtObject c: QtObject { id: g2; objectName: "Jack Smith" } + property QtObject d: QtObject { id: g3; objectName: "Anne Brown" } + + property Component pc: Component { + id: partyComp + BirthdayParty {} + } + + property BirthdayParty q: partyComp.createObject(root, { guests: [g1, g2, g3] }) +} diff --git a/tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt b/tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt new file mode 100644 index 0000000000..6a58889335 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/testhelper/CMakeLists.txt @@ -0,0 +1,13 @@ +qt_policy(SET QTP0001 NEW) +qt_add_library(tst_qqmllanguage_qmlmodule STATIC) +qt_autogen_tools_initial_setup(tst_qqmllanguage_qmlmodule) +qt_add_qml_module(tst_qqmllanguage_qmlmodule + URI testhelper + VERSION 1.0 + SOURCES + "declarativelyregistered.h" + "declarativelyregistered.cpp" +) + +qt_autogen_tools_initial_setup(tst_qqmllanguage_qmlmoduleplugin) +target_link_libraries(tst_qqmllanguage PRIVATE tst_qqmllanguage_qmlmoduleplugin) diff --git a/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp new file mode 100644 index 0000000000..24fcd83d42 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.cpp @@ -0,0 +1,7 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "declarativelyregistered.h" + +PurelyDeclarativeSingleton::PurelyDeclarativeSingleton() = default; + +#include "moc_declarativelyregistered.cpp" diff --git a/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h new file mode 100644 index 0000000000..4845cc68b9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/testhelper/declarativelyregistered.h @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef DECLARATIVELYREGISTERED_LANGUAGE_H +#define DECLARATIVELYREGISTERED_LANGUAGE_H + +#include <QtCore/qobject.h> +#include <QtQml/qqmlregistration.h> + +class PurelyDeclarativeSingleton : public QObject +{ + Q_OBJECT + QML_SINGLETON + QML_ELEMENT +public: + PurelyDeclarativeSingleton(); +}; + + +#endif diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index f571429b07..526cca4b5b 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include "testtypes.h" #include <private/qv4qmlcontext_p.h> @@ -55,6 +56,8 @@ void registerTypes() qmlRegisterTypeNotAvailable("Test",1,0,"UnavailableType", "UnavailableType is unavailable for testing"); + qmlRegisterTypesAndRevisions<DerivedFromUnexposedBase>("Test", 1); + qmlRegisterType<MyQmlObject>("Test.Version",1,0,"MyQmlObject"); qmlRegisterType<MyTypeObject>("Test.Version",1,0,"MyTypeObject"); qmlRegisterType<MyTypeObject>("Test.Version",2,0,"MyTypeObject"); @@ -92,6 +95,8 @@ void registerTypes() qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass"); + qmlRegisterTypesAndRevisions<EnumPropsManyUnderlyingTypes>("Test", 1); + qmlRegisterType<LazyDeferredSubObject>("Test", 1, 0, "LazyDeferredSubObject"); qmlRegisterType<DeferredProperties>("Test", 1, 0, "DeferredProperties"); qmlRegisterType<ImmediateProperties>("Test", 1, 0, "ImmediateProperties"); @@ -148,6 +153,35 @@ void registerTypes() qmlRegisterTypesAndRevisions<BaseValueType>("ValueTypes", 1); qmlRegisterTypesAndRevisions<DerivedValueType>("ValueTypes", 1); + qmlRegisterTypesAndRevisions<GetterObject>("Test", 1); + + qmlRegisterNamespaceAndRevisions(&TypedEnums::staticMetaObject, "TypedEnums", 1); + qmlRegisterTypesAndRevisions<ObjectWithEnums>("TypedEnums", 1); + qmlRegisterTypesAndRevisions<GadgetWithEnums>("TypedEnums", 1); + + QMetaType::registerConverter<UnregisteredValueDerivedType, UnregisteredValueBaseType>(); + qmlRegisterTypesAndRevisions<UnregisteredValueTypeHandler>("Test", 1); + + qmlRegisterTypesAndRevisions<Greeter>("QmlOtherThis", 1); + qmlRegisterTypesAndRevisions<BirthdayParty>("People", 1); + qmlRegisterTypesAndRevisions<AttachedInCtor>("Test", 1); + + qmlRegisterTypesAndRevisions<ByteArrayReceiver>("Test", 1); + + qmlRegisterTypesAndRevisions<Counter>("Test", 1); + + qmlRegisterTypesAndRevisions<Singleton>("EnumScopeTest", 1); + qmlRegisterTypesAndRevisions<NonSingleton>("EnumScopeTest", 1); + qmlRegisterTypesAndRevisions<EnumProviderSingletonQml>("EnumScopeTest", 1); + + qmlRegisterTypesAndRevisions<TypeWithQJsonArrayProperty>("TypeWithQJsonArrayProperty", 1); + qmlRegisterTypesAndRevisions< + InvokableSingleton, + InvokableExtended, + InvokableUncreatable, + InvokableValueType + >("Test", 1); + qmlRegisterTypesAndRevisions<NestedVectors>("Test", 1); } QVariant myCustomVariantTypeConverter(const QString &data) @@ -170,7 +204,7 @@ void CustomBinding::componentComplete() { Q_ASSERT(m_target); - foreach (const QV4::CompiledData::Binding *binding, bindings) { + for (const QV4::CompiledData::Binding *binding : std::as_const(bindings)) { QString name = compilationUnit->stringAt(binding->propertyNameIndex); int bindingId = binding->value.compiledScriptIndex; @@ -248,3 +282,19 @@ UncreatableSingleton *UncreatableSingleton::instance() static UncreatableSingleton instance; return &instance; } + +QT_BEGIN_NAMESPACE +const QMetaObject *QtPrivate::MetaObjectForType<FakeDynamicObject *, void>::metaObjectFunction(const QMetaTypeInterface *) +{ + static auto ptr = []{ + QMetaObjectBuilder builder(&FakeDynamicObject::staticMetaObject); + builder.setFlags(DynamicMetaObject); + auto mo = builder.toMetaObject(); + QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, [mo]() { + delete mo; + }); + return mo; + }(); + return ptr; +} +QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 0777ed103d..ce6abf3504 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1,11 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H #include <QtCore/qobject.h> #include <QtCore/qrect.h> #include <QtCore/qdatetime.h> +#include <QtCore/qjsonarray.h> #include <QtGui/qtransform.h> #include <QtGui/qcolor.h> #include <QtGui/qvector2d.h> @@ -18,6 +19,8 @@ #include <QtQml/qqmlpropertyvaluesource.h> #include <QtQml/qqmlscriptstring.h> #include <QtQml/qqmlproperty.h> + +#include <private/qqmlcomponentattached_p.h> #include <private/qqmlcustomparser_p.h> QVariant myCustomVariantTypeConverter(const QString &data); @@ -42,6 +45,77 @@ struct MyCustomVariantType }; Q_DECLARE_METATYPE(MyCustomVariantType); + +class Group : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int value MEMBER value) + +public: + Group(QObject *parent = nullptr) : QObject(parent) {} + int value = 0; +}; + + +struct GroupGadget +{ + Q_GADGET + + Q_PROPERTY(int value MEMBER value) + +public: + friend bool operator==(GroupGadget g1, GroupGadget g2) { return g1.value == g2.value; } + friend bool operator!=(GroupGadget g1, GroupGadget g2) { return !(g1 == g2); } + int value = 0; +}; + +struct FakeDynamicObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString value MEMBER value) + +public: + FakeDynamicObject() {} + QString value; +}; + +QT_BEGIN_NAMESPACE +namespace QtPrivate { +// don't do this at home – we override the meta-object which QMetaType collects for +// FakeDynamicObject* properties +template<> +struct MetaObjectForType<FakeDynamicObject *, void> +{ + static const QMetaObject *metaObjectFunction(const QMetaTypeInterface *); +}; +} +QT_END_NAMESPACE + +class UnexposedBase : public QObject +{ + Q_OBJECT + + Q_PROPERTY(Group *group MEMBER group) + Q_PROPERTY(GroupGadget groupGadget MEMBER groupGadget) + Q_PROPERTY(FakeDynamicObject *dynamic MEMBER dynamic) +public: + UnexposedBase(QObject *parent = nullptr) : QObject(parent) + { + group = new Group(this); + } + Group *group; + GroupGadget groupGadget; + FakeDynamicObject *dynamic = nullptr; +}; + +class DerivedFromUnexposedBase : public UnexposedBase +{ + Q_OBJECT + QML_ELEMENT +}; + + class MyAttachedObject : public QObject { Q_OBJECT @@ -1292,6 +1366,40 @@ public: } }; +class EnumPropsManyUnderlyingTypes : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + enum si8 : qint8 { ResolvedValue = 1}; + enum ui8 : quint8 {}; + enum si16 : qint16 {}; + enum ui16 : quint16 {}; + enum ui64 : qint64 {}; + enum si64 : quint64 {}; + Q_ENUM(si8) + Q_ENUM(ui8) + Q_ENUM(si16) + Q_ENUM(ui16) + Q_ENUM(si64) + Q_ENUM(ui64) + + + Q_PROPERTY(si8 si8prop MEMBER si8prop) + Q_PROPERTY(ui8 ui8prop MEMBER ui8prop) + Q_PROPERTY(si16 si16prop MEMBER si16prop) + Q_PROPERTY(ui16 ui16prop MEMBER ui16prop) + Q_PROPERTY(si64 si64prop MEMBER si64prop) + Q_PROPERTY(ui64 ui64prop MEMBER ui64prop) + + si8 si8prop = si8(0); + ui8 ui8prop = ui8(0); + si16 si16prop = si16(0); + ui16 ui16prop = ui16(0); + si64 si64prop = si64(0); + ui64 ui64prop = ui64(0); +}; + Q_DECLARE_METATYPE(MyEnum2Class::EnumB) Q_DECLARE_METATYPE(MyEnum1Class::EnumA) Q_DECLARE_METATYPE(Qt::TextFormat) @@ -1547,6 +1655,7 @@ class BareSingleton : public QObject Q_OBJECT QML_SINGLETON QML_ELEMENT + QML_ADDED_IN_VERSION(1, 0) public: BareSingleton(QObject *parent = nullptr) : QObject(parent) @@ -1560,6 +1669,7 @@ class UncreatableSingleton : public QObject Q_OBJECT QML_SINGLETON QML_ELEMENT + QML_ADDED_IN_VERSION(1, 0) public: static UncreatableSingleton *instance(); @@ -2386,6 +2496,32 @@ public: } }; + +struct ForeignNamespace +{ + Q_GADGET +public: + enum Abc { A, B, C, D }; + Q_ENUM(Abc) +}; + +class ForeignNamespaceForeign +{ + Q_GADGET + QML_ELEMENT + QML_FOREIGN_NAMESPACE(ForeignNamespace) +}; + +class LeakingForeignNamespaceForeign : public QObject, public ForeignNamespaceForeign +{ + Q_OBJECT + QML_ELEMENT + +public: + enum AnotherAbc { D, C, B, A }; + Q_ENUM(AnotherAbc) +}; + struct ValueTypeWithLength { Q_GADGET @@ -2405,4 +2541,506 @@ private: int m_length = 19; }; +struct ValueTypeWithString +{ + Q_GADGET + QML_VALUE_TYPE(withString) + QML_CONSTRUCTIBLE_VALUE + +public: + Q_INVOKABLE ValueTypeWithString(const QString &v = QString()) : m_string(v) {} + QString toString() const { return m_string; } + +private: + QString m_string; +}; + +class GetterObject : public QObject { + Q_OBJECT + QML_ELEMENT + QML_SINGLETON +public: + explicit GetterObject(QObject *parent = nullptr) : QObject{parent} {} + + // always returns a 0 as uint64_t + Q_INVOKABLE uint64_t getFalse() const { return 0; } + Q_INVOKABLE uint64_t getTrue() const { return 1; } + + Q_INVOKABLE quint64 getQFalse() const { return 0; } + Q_INVOKABLE quint64 getQTrue() const { return 1; } +}; + +class EnumProviderSingleton : public QObject { + Q_OBJECT + +public: + enum class Expected { + Value = 42 + }; + Q_ENUM(Expected) + + EnumProviderSingleton(QObject* parent = nullptr) : QObject(parent) {} +}; + +class EnumProviderSingletonQml { + Q_GADGET + QML_FOREIGN(EnumProviderSingleton) + QML_NAMED_ELEMENT(EnumProviderSingleton) + QML_SINGLETON + +public: + static EnumProviderSingleton* create(QQmlEngine*, QJSEngine*) { + return new EnumProviderSingleton(); + } + +private: + EnumProviderSingletonQml() = default; +}; + + + +namespace TypedEnums { +Q_NAMESPACE +QML_ELEMENT + +enum E8S : qint8 { + E8SA = std::numeric_limits<qint8>::min(), + E8SB = -5, + E8SC = -1, + E8SD = 0, + E8SE = 1, + E8SF = 5, + E8SG = std::numeric_limits<qint8>::max(), +}; +Q_ENUM_NS(E8S); + +enum E8U : quint8 { + E8UA = 0, + E8UB = 1, + E8UC = 5, + E8UD = 1 << 7, + E8UE = std::numeric_limits<quint8>::max(), +}; +Q_ENUM_NS(E8U); + +enum E16S : qint16 { + E16SA = std::numeric_limits<qint16>::min(), + E16SB = -5, + E16SC = -1, + E16SD = 0, + E16SE = 1, + E16SF = 5, + E16SG = std::numeric_limits<qint16>::max(), +}; +Q_ENUM_NS(E16S); + +enum E16U : quint16 { + E16UA = 0, + E16UB = 1, + E16UC = 5, + E16UD = 1 << 15, + E16UE = std::numeric_limits<quint16>::max(), +}; +Q_ENUM_NS(E16U); + +enum E32S : qint32 { + E32SA = std::numeric_limits<qint32>::min(), + E32SB = -5, + E32SC = -1, + E32SD = 0, + E32SE = 1, + E32SF = 5, + E32SG = std::numeric_limits<qint32>::max(), +}; +Q_ENUM_NS(E32S); + +enum E32U : quint32 { + E32UA = 0, + E32UB = 1, + E32UC = 5, + E32UD = 1u << 31, + E32UE = std::numeric_limits<quint32>::max(), +}; +Q_ENUM_NS(E32U); + +enum E64S : qint64 { + E64SA = std::numeric_limits<qint64>::min(), + E64SB = -5, + E64SC = -1, + E64SD = 0, + E64SE = 1, + E64SF = 5, + E64SG = std::numeric_limits<qint64>::max(), +}; +Q_ENUM_NS(E64S); + +enum E64U : quint64 { + E64UA = 0, + E64UB = 1, + E64UC = 5, + E64UD = 1ull << 63, + E64UE = std::numeric_limits<quint64>::max(), +}; +Q_ENUM_NS(E64U); +} + +class GadgetWithEnums +{ + Q_GADGET + QML_VALUE_TYPE(gadgetWithEnums) + Q_PROPERTY(TypedEnums::E8S e8s MEMBER m_e8s); + Q_PROPERTY(TypedEnums::E8U e8u MEMBER m_e8u); + Q_PROPERTY(TypedEnums::E16S e16s MEMBER m_e16s); + Q_PROPERTY(TypedEnums::E16U e16u MEMBER m_e16u); + Q_PROPERTY(TypedEnums::E32S e32s MEMBER m_e32s); + Q_PROPERTY(TypedEnums::E32U e32u MEMBER m_e32u); + Q_PROPERTY(TypedEnums::E64S e64s MEMBER m_e64s); + Q_PROPERTY(TypedEnums::E64U e64u MEMBER m_e64u); +public: + TypedEnums::E8S m_e8s = {}; + TypedEnums::E8U m_e8u = {}; + TypedEnums::E16S m_e16s = {}; + TypedEnums::E16U m_e16u = {}; + TypedEnums::E32S m_e32s = {}; + TypedEnums::E32U m_e32u = {}; + TypedEnums::E64S m_e64s = {}; + TypedEnums::E64U m_e64u = {}; +private: + friend bool operator==(const GadgetWithEnums &a, const GadgetWithEnums &b) + { + return a.m_e8s == b.m_e8s && a.m_e8u == b.m_e8u && a.m_e16s == b.m_e16s + && a.m_e16u == b.m_e16u && a.m_e32s == b.m_e32s && a.m_e32u == b.m_e32u + && a.m_e64s == b.m_e64s && a.m_e64u == b.m_e64u; + } + friend bool operator!=(const GadgetWithEnums &a, const GadgetWithEnums &b) + { + return !(a == b); + } +}; + +class ObjectWithEnums : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(TypedEnums::E8S e8s MEMBER m_e8s NOTIFY changed); + Q_PROPERTY(TypedEnums::E8U e8u MEMBER m_e8u NOTIFY changed); + Q_PROPERTY(TypedEnums::E16S e16s MEMBER m_e16s NOTIFY changed); + Q_PROPERTY(TypedEnums::E16U e16u MEMBER m_e16u NOTIFY changed); + Q_PROPERTY(TypedEnums::E32S e32s MEMBER m_e32s NOTIFY changed); + Q_PROPERTY(TypedEnums::E32U e32u MEMBER m_e32u NOTIFY changed); + Q_PROPERTY(TypedEnums::E64S e64s MEMBER m_e64s NOTIFY changed); + Q_PROPERTY(TypedEnums::E64U e64u MEMBER m_e64u NOTIFY changed); + Q_PROPERTY(GadgetWithEnums g MEMBER m_g NOTIFY changed); +public: + ObjectWithEnums(QObject *parent = nullptr) : QObject(parent) {} + TypedEnums::E8S m_e8s = {}; + TypedEnums::E8U m_e8u = {}; + TypedEnums::E16S m_e16s = {}; + TypedEnums::E16U m_e16u = {}; + TypedEnums::E32S m_e32s = {}; + TypedEnums::E32U m_e32u = {}; + TypedEnums::E64S m_e64s = {}; + TypedEnums::E64U m_e64u = {}; + GadgetWithEnums m_g; +Q_SIGNALS: + void changed(); +}; + +struct UnregisteredValueBaseType +{ + int foo = 12; +}; + +struct UnregisteredValueDerivedType: public UnregisteredValueBaseType +{ + int bar = 13; +}; + +struct GadgetedValueBaseType +{ + Q_GADGET + int foo = 12; +}; + +struct GadgetedValueDerivedType: public GadgetedValueBaseType +{ + Q_GADGET + int bar = 13; +}; + +class UnregisteredValueTypeHandler: public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + int consumed = 0; + int gadgeted = 0; + +public slots: + UnregisteredValueBaseType produce() { return UnregisteredValueBaseType(); } + UnregisteredValueDerivedType produceDerived() { return UnregisteredValueDerivedType(); } + void consume(UnregisteredValueBaseType) { ++consumed; } + + GadgetedValueDerivedType produceGadgeted() { return GadgetedValueDerivedType(); } + void consume(GadgetedValueBaseType) { ++gadgeted; } +}; + +class Greeter : public QObject +{ + Q_OBJECT + QML_ELEMENT + +public: + Greeter(QObject *parent = nullptr) : QObject(parent) {} + + Q_INVOKABLE void greet() + { + qDebug().noquote() << objectName() << "says hello"; + } + + Q_INVOKABLE void sum(int a, int b) + { + qDebug().noquote() << objectName() << QString("says %1 + %2 = %3").arg(a).arg(b).arg(a + b); + } +}; + +class Attachment : public QObject { + Q_OBJECT +public: + Attachment(QObject *parent = nullptr) : QObject(parent) {} +}; + +class AttachedInCtor : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_ATTACHED(Attachment) + +public: + AttachedInCtor(QObject *parent = nullptr) + : QObject(parent) + { + attached = qmlAttachedPropertiesObject<AttachedInCtor>(this, true); + } + + static Attachment *qmlAttachedProperties(QObject *object) { + return new Attachment(object); + } + + QObject *attached = nullptr; +}; + +class BirthdayParty : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<QObject> guests READ guests) + Q_CLASSINFO("DefaultProperty", "guests") + QML_ELEMENT + +public: + using QObject::QObject; + QQmlListProperty<QObject> guests() { return {this, &m_guests}; } + qsizetype guestCount() const { return m_guests.count(); } + QObject *guest(qsizetype i) const { return m_guests.at(i); } + +private: + QList<QObject *> m_guests; +}; + +class ByteArrayReceiver : public QObject +{ + Q_OBJECT + QML_ELEMENT + +public: + QList<QByteArray> byteArrays; + + Q_INVOKABLE void byteArrayTest(const QByteArray &ba) + { + byteArrays.push_back(ba); + } +}; + +class CounterAttachedBaseType: public QObject +{ + Q_OBJECT + QML_ANONYMOUS + Q_PROPERTY (int value READ value NOTIFY valueChanged) + +public: + CounterAttachedBaseType(QObject *parent = nullptr) : QObject(parent) {} + + int value() { return m_value; } + Q_SIGNAL void valueChanged(); + +protected: + int m_value = 98; +}; + + +class CounterAttachedType: public CounterAttachedBaseType +{ + Q_OBJECT + QML_ANONYMOUS + +public: + CounterAttachedType(QObject *parent = nullptr) : CounterAttachedBaseType(parent) {} + + Q_INVOKABLE void increase() { + ++m_value; + Q_EMIT valueChanged(); + } +}; + +class Counter : public QObject +{ + Q_OBJECT + QML_ATTACHED(CounterAttachedBaseType) + QML_ELEMENT + +public: + static CounterAttachedBaseType *qmlAttachedProperties(QObject *o) + { + return new CounterAttachedType(o); + } +}; + + +class Singleton: public QObject +{ + Q_OBJECT + Q_PROPERTY(EnumType enumProperty READ enumProperty CONSTANT) + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + QML_ELEMENT + QML_SINGLETON +public: + explicit Singleton(QObject* parent = nullptr) : QObject(parent) {} + enum class EnumType { + EnumValue1, + EnumValue2 + }; + Q_ENUM(EnumType); + EnumType enumProperty() const { + return EnumType::EnumValue2; + } +}; + +class NonSingleton: public QObject +{ + Q_OBJECT + Q_PROPERTY(EnumType enumProperty READ enumProperty CONSTANT) + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + QML_ELEMENT +public: + explicit NonSingleton(QObject* parent = nullptr) : QObject(parent) {} + enum class EnumType { + EnumValue1, + EnumValue2 + }; + Q_ENUM(EnumType); + EnumType enumProperty() const { + return EnumType::EnumValue2; + } +}; + +class TypeWithQJsonArrayProperty : public QObject { + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QJsonArray jsonArray READ jsonArray WRITE setJsonArray NOTIFY jsonArrayChanged) + +public: + TypeWithQJsonArrayProperty(QObject *parent = nullptr) : QObject(parent) {} + + const QJsonArray& jsonArray() { return m_jsonArray; } + void setJsonArray(const QJsonArray& a) { m_jsonArray = a; } + +signals: + void jsonArrayChanged(); + +private: + QJsonArray m_jsonArray; +}; + +class InvokableSingleton : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON +public: + InvokableSingleton() = default; + Q_INVOKABLE InvokableSingleton(int a, QObject *parent) : QObject(parent), m_a(a) {} + + int m_a = 0; +}; + +class InvokableExtension : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE InvokableExtension(QObject *parent = nullptr) : QObject(parent) {} +}; + +class InvokableExtended : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_EXTENDED(InvokableExtension) + +public: + Q_INVOKABLE InvokableExtended() = default; +}; + +class InvokableUncreatable : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("no") + +public: + Q_INVOKABLE InvokableUncreatable() = default; +}; + +class InvokableValueType +{ + Q_GADGET + QML_VALUE_TYPE(vv) +public: + Q_INVOKABLE InvokableValueType() = default; + Q_INVOKABLE InvokableValueType(const QString &s) : m_s(s) {} + QString m_s; +}; + +class NestedVectors : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + NestedVectors(QObject *parent = nullptr) : QObject(parent) + { + std::vector<int> data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + m_list.push_back(data); + data.clear(); + data.push_back(4); + data.push_back(5); + m_list.push_back(data); + } + + Q_INVOKABLE std::vector<std::vector<int>> getList() + { + return m_list; + } + + Q_INVOKABLE void setList(std::vector<std::vector<int>> list) + { + m_list = list; + } + +private: + std::vector<std::vector<int>> m_list; +}; + #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index fee65fcb17..2212a40eee 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -37,7 +38,7 @@ #include <deque> -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) #include <unistd.h> #endif @@ -47,7 +48,7 @@ DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) static inline bool isCaseSensitiveFileSystem(const QString &path) { Q_UNUSED(path); -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) return pathconf(path.toLatin1().constData(), _PC_CASE_SENSITIVE); #elif defined(Q_OS_WIN) return false; @@ -248,6 +249,8 @@ private slots: void compositeSingletonSelectors(); void compositeSingletonRegistered(); void compositeSingletonCircular(); + void compositeSingletonRequiredProperties(); + void compositeSingletonRequiredProperties_data(); void singletonsHaveContextAndEngine(); @@ -316,6 +319,7 @@ private slots: void inlineComponentFoundBeforeOtherImports(); void inlineComponentDuplicateNameError(); void inlineComponentWithAliasInstantiatedWithNewProperties(); + void inlineComponentWithImplicitComponent(); void selfReference(); void selfReferencingSingleton(); @@ -358,6 +362,8 @@ private slots: void hangOnWarning(); + void groupPropertyFromNonExposedBaseClass(); + void listEnumConversion(); void deepInlineComponentScriptBinding(); @@ -402,10 +408,62 @@ private slots: void importPrecedence(); void nullIsNull(); void multiRequired(); + void isNullOrUndefined(); void objectAndGadgetMethodCallsRejectThisObject(); void objectAndGadgetMethodCallsAcceptThisObject(); + void asValueType(); + void asValueTypeGood(); + + void longConversion(); + + void enumPropsManyUnderylingTypes(); + + void typedEnums_data(); + void typedEnums(); + + void objectMethodClone(); + void unregisteredValueTypeConversion(); + void retainThis(); + + void variantObjectList(); + void jitExceptions(); + + void attachedInCtor(); + void byteArrayConversion(); + void propertySignalNames_data(); + void propertySignalNames(); + void signalNames_data(); + void signalNames(); + + void callMethodOfAttachedDerived(); + + void multiVersionSingletons(); + void typeAnnotationCycle(); + void corpseInQmlList(); + void objectInQmlListAndGc(); + void asCastToInlineComponent(); + void deepAliasOnICOrReadonly(); + + void optionalChainCallOnNullProperty(); + + void ambiguousComponents(); + + void writeNumberToEnumAlias(); + void badInlineComponentAnnotation(); + void manuallyCallSignalHandler(); + void overrideDefaultProperty(); + void enumScopes(); + + void typedObjectList(); + void invokableCtors(); + + void jsonArrayPropertyBehavesLikeAnArray(); + + void nestedVectors(); + void optimizedSequenceShift(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -492,11 +550,11 @@ void tst_qqmllanguage::insertedSemicolon() QQmlComponent component(&engine, testFileUrl(file)); - QScopedPointer<QObject> object; + std::unique_ptr<QObject> object; if(create) { object.reset(component.create()); - QVERIFY(object.isNull()); + QVERIFY(object.get()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); @@ -1381,13 +1439,13 @@ void tst_qqmllanguage::rootItemIsComponent() QtWarningMsg, QRegularExpression( ".*/rootItemIsComponent\\.qml:3:1: Using a Component as the root of " - "a qmldocument is deprecated: types defined in qml documents are automatically " - "wrapped into Components when needed\\.")); + "a QML document is deprecated: types defined in qml documents are " + "automatically wrapped into Components when needed\\.")); QTest::ignoreMessage( QtWarningMsg, QRegularExpression( ".*/EvilComponentType\\.qml:3:1: Using a Component as the root of a " - "qmldocument is deprecated: types defined in qml documents are automatically " + "QML document is deprecated: types defined in qml documents are automatically " "wrapped into Components when needed\\.")); QTest::ignoreMessage( QtWarningMsg, @@ -1707,8 +1765,8 @@ void tst_qqmllanguage::propertyValueSource() QVERIFY(object != nullptr); QList<QObject *> valueSources; - QObjectList allChildren = object->findChildren<QObject*>(); - foreach (QObject *child, allChildren) { + const QObjectList allChildren = object->findChildren<QObject*>(); + for (QObject *child : allChildren) { if (qobject_cast<QQmlPropertyValueSource *>(child)) valueSources.append(child); } @@ -1728,8 +1786,8 @@ void tst_qqmllanguage::propertyValueSource() QVERIFY(object != nullptr); QList<QObject *> valueSources; - QObjectList allChildren = object->findChildren<QObject*>(); - foreach (QObject *child, allChildren) { + const QObjectList allChildren = object->findChildren<QObject*>(); + for (QObject *child : allChildren) { if (qobject_cast<QQmlPropertyValueSource *>(child)) valueSources.append(child); } @@ -2039,7 +2097,7 @@ void tst_qqmllanguage::aliasProperties() MyQmlObject *o = qvariant_cast<MyQmlObject*>(v); QCOMPARE(o->value(), 10); - delete o; + delete o; //intentional delete v = object->property("otherAlias"); QCOMPARE(v.typeId(), qMetaTypeId<MyQmlObject *>()); @@ -2074,7 +2132,7 @@ void tst_qqmllanguage::aliasProperties() QObject *alias = qvariant_cast<QObject *>(object->property("aliasedObject")); QCOMPARE(alias, object2); - delete object1; + delete object1; //intentional delete QObject *alias2 = object.data(); // "Random" start value int status = -1; @@ -2583,7 +2641,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() Q_ASSERT(td); QVERIFY(!td->backupSourceCode().isValid()); - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = td->compilationUnit(); readOnlyQmlUnit.reset(compilationUnit->unitData()); Q_ASSERT(readOnlyQmlUnit); QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); @@ -2863,7 +2921,8 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q if (type.isEmpty()) { QVERIFY(component.isError()); QString actualerror; - foreach (const QQmlError e, component.errors()) { + const auto errors = component.errors(); + for (const QQmlError &e : errors) { if (!actualerror.isEmpty()) actualerror.append("; "); actualerror.append(e.description()); @@ -3861,6 +3920,7 @@ void tst_qqmllanguage::initTestCase() qmlRegisterType(testFileUrl("invalidRoot.1.qml"), "Test", 1, 0, "RegisteredCompositeType3"); qmlRegisterType(testFileUrl("CompositeTypeWithEnum.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithEnum"); qmlRegisterType(testFileUrl("CompositeTypeWithAttachedProperty.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithAttachedProperty"); + qmlRegisterType(testFileUrl("CompositeTypeWithEnumSelfReference.qml"), "Test", 1, 0, "CompositeTypeWithEnumSelfReference"); // Registering the TestType class in other modules should have no adverse effects qmlRegisterType<TestType>("org.qtproject.TestPre", 1, 0, "Test"); @@ -3889,6 +3949,7 @@ void tst_qqmllanguage::initTestCase() // Register a Composite Singleton. qmlRegisterSingletonType(testFileUrl("singleton/RegisteredCompositeSingletonType.qml"), "org.qtproject.Test", 1, 0, "RegisteredSingleton"); + qmlRegisterType(testFileUrl("Comps/OverlayDrawer.qml"), "Comps", 2, 0, "OverlayDrawer"); } void tst_qqmllanguage::aliasPropertyChangeSignals() @@ -4036,6 +4097,17 @@ void tst_qqmllanguage::registeredCompositeTypeWithEnum() QCOMPARE(o->property("enumValue0").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue0)); QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42)); QCOMPARE(o->property("enumValue15").toInt(), static_cast<int>(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15)); + + { + QQmlComponent component(&engine); + component.setData("import Test\nCompositeTypeWithEnumSelfReference {}", QUrl()); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); + + QCOMPARE(o->property("e").toInt(), 1); + QCOMPARE(o->property("f").toInt(), 2); + } } // QTBUG-43581 @@ -4212,7 +4284,8 @@ void tst_qqmllanguage::lowercaseEnumRuntime() QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); - delete component.create(); + std::unique_ptr<QObject> root { component.create() }; + QVERIFY(root); } void tst_qqmllanguage::lowercaseEnumCompileTime_data() @@ -4229,7 +4302,8 @@ void tst_qqmllanguage::lowercaseEnumCompileTime() QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); - delete component.create(); + std::unique_ptr<QObject> root { component.create() }; + QVERIFY(root); } void tst_qqmllanguage::scopedEnum() @@ -4473,7 +4547,6 @@ void tst_qqmllanguage::groupAssignmentFailure() { auto ep = std::make_unique<QQmlEngine>(); QTest::failOnWarning("QQmlComponent: Component destroyed while completion pending"); - QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(".*Invalid property assignment: url expected - Assigning null to incompatible properties in QML is deprecated. This will become a compile error in future versions of Qt..*")); QQmlComponent component(ep.get(), testFileUrl("groupFailure.qml")); QScopedPointer<QObject> o(component.create()); QVERIFY(!o); @@ -4844,6 +4917,36 @@ void tst_qqmllanguage::compositeSingletonCircular() QCOMPARE(o->property("value").toInt(), 2); } +void tst_qqmllanguage::compositeSingletonRequiredProperties() +{ + QFETCH(QString, warning); + QFETCH(QString, singletonName); + QQmlEngine engine; + engine.addImportPath(dataDirectory()); + { + QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning)); + std::unique_ptr<QObject> singleton {engine.singletonInstance<QObject *>( + "SingletonWithRequiredProperties", + singletonName + )}; + QVERIFY(!singleton); + } +} + +void tst_qqmllanguage::compositeSingletonRequiredProperties_data() +{ + QTest::addColumn<QString>("warning"); + QTest::addColumn<QString>("singletonName"); + + QString warning1 = testFileUrl("SingletonWithRequiredProperties/SingletonWithRequired1.qml").toString() + + ":5:5: Required property i was not initialized"; + QString warning2 = testFileUrl("SingletonWithRequiredProperties/SingletonWithRequired2.qml").toString() + + ":6:9: Required property i was not initialized"; + + QTest::addRow("toplevelRequired") << warning1 << "SingletonWithRequired1"; + QTest::addRow("subObjectRequired") << warning2 << "SingletonWithRequired2"; +} + void tst_qqmllanguage::singletonsHaveContextAndEngine() { QObject *qmlSingleton = nullptr; @@ -5348,24 +5451,30 @@ void tst_qqmllanguage::namespacedPropertyTypes() void tst_qqmllanguage::qmlTypeCanBeResolvedByName_data() { QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QString>("name"); // Built-in C++ types - QTest::newRow("C++ - Anonymous") << testFileUrl("quickTypeByName_anon.qml"); - QTest::newRow("C++ - Named") << testFileUrl("quickTypeByName_named.qml"); + QTest::newRow("C++ - Anonymous") << testFileUrl("quickTypeByName_anon.qml") + << QStringLiteral("QtQuick/Item"); + QTest::newRow("C++ - Named") << testFileUrl("quickTypeByName_named.qml") + << QStringLiteral("QtQuick/Item"); // Composite types with a qmldir - QTest::newRow("QML - Anonymous - qmldir") << testFileUrl("compositeTypeByName_anon_qmldir.qml"); - QTest::newRow("QML - Named - qmldir") << testFileUrl("compositeTypeByName_named_qmldir.qml"); + QTest::newRow("QML - Anonymous - qmldir") << testFileUrl("compositeTypeByName_anon_qmldir.qml") + << QStringLiteral("SimpleType"); + QTest::newRow("QML - Named - qmldir") << testFileUrl("compositeTypeByName_named_qmldir.qml") + << QStringLiteral("SimpleType"); } void tst_qqmllanguage::qmlTypeCanBeResolvedByName() { QFETCH(QUrl, componentUrl); + QFETCH(QString, name); QQmlEngine engine; QQmlComponent component(&engine, componentUrl); VERIFY_ERRORS(0); - QTest::ignoreMessage(QtMsgType::QtWarningMsg, "[object Object]"); // a bit crude, but it will do + QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(name)); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -5654,6 +5763,9 @@ void tst_qqmllanguage::retrieveQmlTypeId() QVERIFY(qmlTypeId("Test", 1, 0, "MyExtendedUncreateableBaseClass") >= 0); QVERIFY(qmlTypeId("Test", 1, 0, "MyUncreateableBaseClass") >= 0); QVERIFY(qmlTypeId("Test", 1, 0, "MyTypeObjectSingleton") >= 0); + + // Must also work for declaratively registered types whose module wasn't imported so far + QVERIFY(qmlTypeId("testhelper", 1, 0, "PurelyDeclarativeSingleton") >= 0); } void tst_qqmllanguage::polymorphicFunctionLookup() @@ -5749,7 +5861,7 @@ void tst_qqmllanguage::selfReference() const QMetaObject *metaObject = o->metaObject(); QMetaProperty selfProperty = metaObject->property(metaObject->indexOfProperty("self")); - QCOMPARE(selfProperty.metaType().id(), compilationUnit->typeIds.id.id()); + QCOMPARE(selfProperty.metaType().id(), compilationUnit->metaType().id()); QByteArray typeName = selfProperty.typeName(); QVERIFY(typeName.endsWith('*')); @@ -5758,7 +5870,7 @@ void tst_qqmllanguage::selfReference() QMetaMethod selfFunction = metaObject->method(metaObject->indexOfMethod("returnSelf()")); QVERIFY(selfFunction.isValid()); - QCOMPARE(selfFunction.returnType(), compilationUnit->typeIds.id.id()); + QCOMPARE(selfFunction.returnType(), compilationUnit->metaType().id()); QMetaMethod selfSignal; @@ -5772,7 +5884,7 @@ void tst_qqmllanguage::selfReference() QVERIFY(selfSignal.isValid()); QCOMPARE(selfSignal.parameterCount(), 1); - QCOMPARE(selfSignal.parameterType(0), compilationUnit->typeIds.id.id()); + QCOMPARE(selfSignal.parameterType(0), compilationUnit->metaType().id()); } void tst_qqmllanguage::selfReferencingSingleton() @@ -5809,10 +5921,10 @@ void tst_qqmllanguage::listContainingDeletedObject() QVERIFY(root); auto cmp = root->property("a").value<QQmlComponent*>(); - auto o = cmp->create(); + std::unique_ptr<QObject> o { cmp->create() }; - QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o))); - delete o; + QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o.get()))); + o.reset(); QMetaObject::invokeMethod(root.get(), "use"); } @@ -6096,6 +6208,17 @@ void tst_qqmllanguage::inlineComponentWithAliasInstantiatedWithNewProperties() QCOMPARE(root->property("result").toString(), "Bar"); } +void tst_qqmllanguage::inlineComponentWithImplicitComponent() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("inlineComponentWithImplicitComponent.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + + QCOMPARE(root->objectName(), "green blue"_L1); +} + struct QJSValueConvertible { Q_GADGET @@ -6392,23 +6515,15 @@ void tst_qqmllanguage::extendedSingleton() void tst_qqmllanguage::qtbug_85932() { - QString warning1 = QLatin1String("%1:10:9: id is not unique").arg(testFileUrl("SingletonTest.qml").toString()); - QString warning2 = QLatin1String("%1:4: Error: Due to the preceding error(s), Singleton \"SingletonTest\" could not be loaded.").arg(testFileUrl("qtbug_85932.qml").toString()); - - QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning1)); - QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning2)); - QQmlEngine engine; - QList<QQmlError> allWarnings; - QObject::connect(&engine, &QQmlEngine::warnings, [&allWarnings](const QList<QQmlError> &warnings) { - allWarnings.append(warnings); - }); - QQmlComponent c(&engine, testFileUrl("qtbug_85932.qml")); - QScopedPointer<QObject> obj(c.create()); - QTRY_COMPARE(allWarnings.size(), 2); - QCOMPARE(allWarnings.at(0).toString(), warning1); - QCOMPARE(allWarnings.at(1).toString(), warning2); + QQmlComponent c(&engine, testFileUrl("badSingleton/qtbug_85932.qml")); + QVERIFY(c.isError()); + + const QString error = c.errorString(); + QVERIFY(error.contains(QLatin1String("Type SingletonTest unavailable"))); + QVERIFY(error.contains(QLatin1String("%1:10 id is not unique") + .arg(testFileUrl("badSingleton/SingletonTest.qml").toString()))); } void tst_qqmllanguage::multiExtension() @@ -6758,15 +6873,22 @@ void tst_qqmllanguage::bareInlineComponent() if (type.elementName() == QStringLiteral("Tab1")) { QVERIFY(type.module().isEmpty()); tab1Found = true; - const auto ics = type.priv()->objectIdToICType; - QVERIFY(ics.size() > 0); - for (const QQmlType &ic : ics) - QVERIFY(ic.containingType() == type); + + const QQmlType leftTab = QQmlMetaType::inlineComponentType(type, "LeftTab"); + QUrl leftUrl = leftTab.sourceUrl(); + leftUrl.setFragment(QString()); + QCOMPARE(leftUrl, type.sourceUrl()); + + const QQmlType rightTab = QQmlMetaType::inlineComponentType(type, "RightTab"); + QUrl rightUrl = rightTab.sourceUrl(); + rightUrl.setFragment(QString()); + QCOMPARE(rightUrl, type.sourceUrl()); } } QVERIFY(tab1Found); } +#if QT_CONFIG(qml_debug) struct DummyDebugger : public QV4::Debugging::Debugger { bool pauseAtNextOpportunity() const final { return false; } @@ -6775,6 +6897,9 @@ struct DummyDebugger : public QV4::Debugging::Debugger void leavingFunction(const QV4::ReturnedValue &) final { } void aboutToThrow() final { } }; +#else +using DummyDebugger = QV4::Debugging::Debugger; // it's already dummy +#endif void tst_qqmllanguage::hangOnWarning() { @@ -6792,6 +6917,25 @@ void tst_qqmllanguage::hangOnWarning() QVERIFY(object != nullptr); } +void tst_qqmllanguage::groupPropertyFromNonExposedBaseClass() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("derivedFromUnexposedBase.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + auto root = qobject_cast<DerivedFromUnexposedBase *>(o.get()); + QVERIFY(root); + QVERIFY(root->group); + QCOMPARE(root->group->value, 42); + QCOMPARE(root->groupGadget.value, 42); + + c.loadUrl(testFileUrl("dynamicGroupPropertyRejected.qml")); + QVERIFY(c.isError()); + QVERIFY2(c.errorString().contains("Unsupported grouped property access"), qPrintable(c.errorString())); +} + void tst_qqmllanguage::listEnumConversion() { QQmlEngine e; @@ -7424,6 +7568,33 @@ LeakingForeignerForeign { QVERIFY(o->property("anotherAbc").isValid()); QVERIFY(!o->property("abc").isValid()); } + + { + QQmlComponent c(&engine); + c.setData(R"( +import StaticTest +import QtQml +QtObject { + objectName: 'b' + ForeignNamespaceForeign.B +})", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->objectName(), "b1"); + } + { + QQmlComponent c(&engine); + c.setData(R"( +import StaticTest +import QtQml +QtObject { + objectName: 'b' + LeakingForeignNamespaceForeign.B +})", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->objectName(), "b2"); + } } void tst_qqmllanguage::attachedOwnProperties() @@ -7711,12 +7882,20 @@ void tst_qqmllanguage::functionSignatureEnforcement() QCOMPARE(ignored->property("m").toInt(), 77); QCOMPARE(ignored->property("n").toInt(), 67); - QQmlComponent c2(&engine, testFileUrl("signatureEnforced.qml")); + const QUrl url2 = testFileUrl("signatureEnforced.qml"); + QQmlComponent c2(&engine, url2); QVERIFY2(c2.isReady(), qPrintable(c2.errorString())); + QTest::ignoreMessage( + QtCriticalMsg, + qPrintable(url2.toString() + u":36: 15 should be coerced to void because the function " + "called is insufficiently annotated. The original value " + "is retained. " + "This will change in a future version of Qt."_s)); + QScopedPointer<QObject> enforced(c2.create()); QCOMPARE(enforced->property("l").toInt(), 2); // strlen("no") - QCOMPARE(enforced->property("m").toInt(), 12); + QCOMPARE(enforced->property("m").toInt(), 77); QCOMPARE(enforced->property("n").toInt(), 99); QCOMPARE(enforced->property("o").toInt(), 77); } @@ -7766,6 +7945,30 @@ void tst_qqmllanguage::multiRequired() qPrintable(url.toString() + ":5 Required property description was not initialized\n")); } +// QTBUG-111088 +void tst_qqmllanguage::isNullOrUndefined() +{ + { + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("isNullOrUndefined_interpreter.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVariant result = o.data()->property("result"); + QVERIFY(result.isValid()); + QCOMPARE(result.toInt(), 3); + } + + { + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("isNullOrUndefined_jit.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVariant result = o.data()->property("result"); + QVERIFY(result.isValid()); + QCOMPARE(result.toInt(), 150); + } +} + void tst_qqmllanguage::objectAndGadgetMethodCallsRejectThisObject() { QQmlEngine engine; @@ -7810,6 +8013,15 @@ void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject() QQmlComponent c(&engine, testFileUrl("objectAndGadgetMethodCallsAcceptThisObject.qml")); QVERIFY2(c.isReady(), qPrintable(c.errorString())); + // Explicitly retrieve the metaobject for the Qt singleton so that the proxy data is created. + // This way the inheritance analysis we do when figuring out what toString() means is somewhat + // more interesting. Also, we get a deterministic result for Qt.toString(). + const QQmlType qtType = QQmlMetaType::qmlType(QStringLiteral("Qt"), QString(), QTypeRevision()); + QVERIFY(qtType.isValid()); + const QMetaObject *qtMeta = qtType.metaObject(); + QVERIFY(qtMeta); + QCOMPARE(QString::fromUtf8(qtMeta->className()), QLatin1String("Qt")); + QTest::ignoreMessage( QtWarningMsg, QRegularExpression( "objectAndGadgetMethodCallsAcceptThisObject.qml:16: Error: " @@ -7840,7 +8052,7 @@ void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject() QCOMPARE(o->property("goodString2"), QStringLiteral("27")); QCOMPARE(o->property("goodString3"), QStringLiteral("28")); - QVERIFY(o->property("goodString4").value<QString>().startsWith("QtObject"_L1)); + QVERIFY(o->property("goodString4").value<QString>().startsWith("Qt("_L1)); QCOMPARE(o->property("badString2"), QString()); QCOMPARE(o->property("badInt"), 0); @@ -7849,6 +8061,906 @@ void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject() QCOMPARE(o->property("goodInt3"), 5); } +void tst_qqmllanguage::longConversion() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("longConversion.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + for (const char *prop : { + "testProp", + "testQProp", + "fromLocal", + "fromQLocal", + "fromBoolean", + "fromQBoolean"}) { + const QVariant val = o->property(prop); + QVERIFY(val.isValid()); + QCOMPARE(val.metaType(), QMetaType::fromType<bool>()); + QVERIFY(!val.toBool()); + } +} + +void tst_qqmllanguage::enumPropsManyUnderylingTypes() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("enumPropsManyUnderlyingTypes.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + auto *enumObject = qobject_cast<EnumPropsManyUnderlyingTypes *>(o.get()); + QCOMPARE(enumObject->si8prop, EnumPropsManyUnderlyingTypes::ResolvedValue); + QCOMPARE(enumObject->ui8prop, EnumPropsManyUnderlyingTypes::ResolvedValue); + QCOMPARE(enumObject->si16prop, EnumPropsManyUnderlyingTypes::ResolvedValue); + QCOMPARE(enumObject->ui16prop, EnumPropsManyUnderlyingTypes::ResolvedValue); + QCOMPARE(enumObject->si64prop, EnumPropsManyUnderlyingTypes::ResolvedValue); + QCOMPARE(enumObject->ui64prop, EnumPropsManyUnderlyingTypes::ResolvedValue); +} + +void tst_qqmllanguage::asValueType() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("asValueType.qml"); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + "Could not find any constructor for value type QQmlRectFValueType " + "to call with value undefined"); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":7:5: Unable to assign [undefined] to QRectF"_L1)); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":10: Coercing a value to QML/point using a type " + "assertion. This behavior is deprecated. Add 'pragma " + "ValueTypeBehavior: Assertable' to prevent it."_L1)); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":14: Coercing between incompatible value types mistakenly " + "yields null rather than undefined. Add 'pragma " + "ValueTypeBehavior: Assertable' to prevent this."_L1)); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":15: Coercing from instances of object types to value " + "types mistakenly yields null rather than undefined. Add " + "'pragma ValueTypeBehavior: Assertable' to prevent " + "this."_L1)); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":16: Coercing a value to QML/size using a type " + "assertion. This behavior is deprecated. Add 'pragma " + "ValueTypeBehavior: Assertable' to prevent it."_L1)); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":11: Coercing a value to StaticTest/withString using a " + "type assertion. This behavior is deprecated. Add 'pragma " + "ValueTypeBehavior: Assertable' to prevent it."_L1)); + QTest::ignoreMessage( + QtWarningMsg, + "Could not find any constructor for value type QQmlSizeFValueType to call " + "with value 11"); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":18: Coercing a value to QML/size using a type " + "assertion. This behavior is deprecated. Add 'pragma " + "ValueTypeBehavior: Assertable' to prevent it."_L1)); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":19: Coercing a value to QML/size using a type " + "assertion. This behavior is deprecated. Add 'pragma " + "ValueTypeBehavior: Assertable' to prevent it."_L1)); + + QScopedPointer<QObject> o(c.create()); + + QCOMPARE(o->property("a"), QVariant()); + QCOMPARE(o->property("b").value<QRectF>(), QRectF()); + QVERIFY(!o->property("c").toBool()); + + const QRectF rect(1, 2, 3, 4); + o->setProperty("a", QVariant(rect)); + QCOMPARE(o->property("b").value<QRectF>(), rect); + QVERIFY(o->property("c").toBool()); + + QVERIFY(!o->property("d").toBool()); + const QPointF point = o->property("e").value<QPointF>(); + QCOMPARE(point.x(), 10.0); + QCOMPARE(point.y(), 20.0); + + const ValueTypeWithString withString = o->property("f").value<ValueTypeWithString>(); + QCOMPARE(withString.toString(), u"red"); + + const QVariant string = o->property("g"); + QCOMPARE(string.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(string.toString(), u"green"); + + const QVariant p = o->property("p"); + QCOMPARE(p.metaType(), QMetaType::fromType<std::nullptr_t>()); + + const QVariant q = o->property("q"); + QCOMPARE(q.metaType(), QMetaType::fromType<std::nullptr_t>()); + + const QVariant r = o->property("r"); + QCOMPARE(r.metaType(), QMetaType::fromType<QSizeF>()); + QCOMPARE(r.value<QSizeF>(), QSizeF()); + + const QVariant s = o->property("s"); + QCOMPARE(s.metaType(), QMetaType()); + + const QVariant t = o->property("t"); + QCOMPARE(t.metaType(), QMetaType::fromType<QSizeF>()); + QCOMPARE(t.value<QSizeF>(), QSizeF()); + + const QVariant u = o->property("u"); + QCOMPARE(u.metaType(), QMetaType::fromType<QSizeF>()); + QCOMPARE(u.value<QSizeF>(), QSizeF()); +} + +void tst_qqmllanguage::asValueTypeGood() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("asValueTypeGood.qml"); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + ":7:5: Unable to assign [undefined] to QRectF"_L1)); + QScopedPointer<QObject> o(c.create()); + + QCOMPARE(o->property("a"), QVariant()); + QCOMPARE(o->property("b").value<QRectF>(), QRectF()); + QVERIFY(!o->property("c").toBool()); + + const QRectF rect(1, 2, 3, 4); + o->setProperty("a", QVariant(rect)); + QCOMPARE(o->property("b").value<QRectF>(), rect); + QVERIFY(o->property("c").toBool()); + + QVERIFY(!o->property("d").toBool()); + QVERIFY(!o->property("e").isValid()); + QVERIFY(!o->property("f").isValid()); + + const QVariant string = o->property("g"); + QCOMPARE(string.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(string.toString(), u"green"); + + const ValueTypeWithString withString = o->property("h").value<ValueTypeWithString>(); + QCOMPARE(withString.toString(), u"red"); + + const QPointF point = o->property("i").value<QPointF>(); + QCOMPARE(point.x(), 10.0); + QCOMPARE(point.y(), 20.0); + + const QVariant j = o->property("j"); + QCOMPARE(j.metaType(), QMetaType::fromType<int>()); + QCOMPARE(j.toInt(), 4); + + QVERIFY(!o->property("k").isValid()); + QVERIFY(!o->property("l").isValid()); + + const QVariant m = o->property("m"); + QCOMPARE(m.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(m.toString(), u"something"); + + QVERIFY(!o->property("n").isValid()); + QVERIFY(!o->property("o").isValid()); + QVERIFY(!o->property("p").isValid()); + QVERIFY(!o->property("q").isValid()); + QVERIFY(!o->property("r").isValid()); + QVERIFY(!o->property("s").isValid()); + QVERIFY(!o->property("t").isValid()); + QVERIFY(!o->property("u").isValid()); +} + +void tst_qqmllanguage::typedEnums_data() +{ + QTest::addColumn<QString>("property"); + QTest::addColumn<double>("value"); + const QMetaObject *mo = &TypedEnums::staticMetaObject; + for (int i = 0, end = mo->enumeratorCount(); i != end; ++i) { + const QMetaEnum e = mo->enumerator(i); + for (int k = 0, end = e.keyCount(); k != end; ++k) { + QTest::addRow("%s::%s", e.name(), e.key(k)) + << QString::fromLatin1(e.name()).toLower() + << double(e.value(k)); + } + } +} +void tst_qqmllanguage::typedEnums() +{ + QFETCH(QString, property); + QFETCH(double, value); + QQmlEngine e; + const QString qml = QLatin1String(R"( + import QtQml + import TypedEnums + ObjectWithEnums { + property real input: %2 + %1: input + g.%1: input + property real output1: %1 + property real output2: g.%1 + } + )").arg(property).arg(value, 0, 'f'); + QQmlComponent c(&engine); + c.setData(qml.toUtf8(), QUrl("enums.qml"_L1)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + // TODO: This silently fails for quint32, qint64 and quint64 because QMetaEnum cannot encode + // such values either. For the 64bit values we'll also need a better type than double + // inside QML. + QEXPECT_FAIL("E32U::E32UD", "Not supported", Abort); + QEXPECT_FAIL("E32U::E32UE", "Not supported", Abort); + QEXPECT_FAIL("E64U::E64UE", "Not supported", Abort); + + QCOMPARE(o->property("output1").toDouble(), value); + QCOMPARE(o->property("output2").toDouble(), value); +} + +void tst_qqmllanguage::objectMethodClone() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("objectMethodClone.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QTRY_COMPARE(o->property("doneClicks").toInt(), 2); +} + +void tst_qqmllanguage::unregisteredValueTypeConversion() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("unregisteredValueTypeConversion.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + UnregisteredValueTypeHandler *handler = qobject_cast<UnregisteredValueTypeHandler *>(o.data()); + Q_ASSERT(handler); + QCOMPARE(handler->consumed, 2); + QCOMPARE(handler->gadgeted, 1); +} + +void tst_qqmllanguage::retainThis() +{ + QQmlEngine e; + const QUrl url = testFileUrl("retainThis.qml"); + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + const QString warning = u"Calling C++ methods with 'this' objects different " + "from the one they were retrieved from is broken, due to " + "historical reasons. The original object is used as 'this' " + "object. You can allow the given 'this' object to be used " + "by setting 'pragma NativeMethodBehavior: AcceptThisObject'"_s; + + // Both cases objA because we retain the thisObject. + for (int i = 0; i < 2; ++i) { + QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":12: "_s + warning)); + QTest::ignoreMessage(QtDebugMsg, "objA says hello"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":13: "_s + warning)); + QTest::ignoreMessage(QtDebugMsg, "objA says 5 + 6 = 11"); + } + + QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":22: "_s + warning)); + QTest::ignoreMessage(QtDebugMsg, "objA says hello"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":22: "_s + warning)); + QTest::ignoreMessage(QtDebugMsg, "objB says hello"); + + QTest::ignoreMessage(QtDebugMsg, "objC says 7 + 7 = 14"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":32: "_s + warning)); + QTest::ignoreMessage(QtDebugMsg, "objB says 7 + 7 = 14"); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::variantObjectList() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("variantObjectList.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + BirthdayParty *party = o->property("q").value<BirthdayParty *>(); + QCOMPARE(party->guestCount(), 3); + QCOMPARE(party->guest(0)->objectName(), "Leo Hodges"); + QCOMPARE(party->guest(1)->objectName(), "Jack Smith"); + QCOMPARE(party->guest(2)->objectName(), "Anne Brown"); +} + +void tst_qqmllanguage::jitExceptions() +{ + QQmlEngine e; + const QUrl url = testFileUrl("jitExceptions.qml"); + QQmlComponent c(&e, testFileUrl("jitExceptions.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + u":5: ReferenceError: control is not defined"_s)); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::attachedInCtor() +{ + QQmlEngine e; + QQmlComponent c(&e); + c.setData(R"( + import Test + AttachedInCtor {} + )", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + AttachedInCtor *a = qobject_cast<AttachedInCtor *>(o.data()); + QVERIFY(a->attached); + QCOMPARE(a->attached, qmlAttachedPropertiesObject<AttachedInCtor>(a, false)); +} + +void tst_qqmllanguage::byteArrayConversion() +{ + QQmlEngine e; + QQmlComponent c(&e); + c.setData(R"( + import Test + import QtQml + ByteArrayReceiver { + Component.onCompleted: { + byteArrayTest([1, 2, 3]); + byteArrayTest(Array.from('456')); + } + } + )", QUrl()); + + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + ByteArrayReceiver *receiver = qobject_cast<ByteArrayReceiver *>(o.data()); + QVERIFY(receiver); + QCOMPARE(receiver->byteArrays.length(), 2); + QCOMPARE(receiver->byteArrays[0], QByteArray("\1\2\3")); + QCOMPARE(receiver->byteArrays[1], QByteArray("\4\5\6")); +} +void tst_qqmllanguage::propertySignalNames_data() +{ + QTest::addColumn<QString>("propertyName"); + QTest::addColumn<QString>("propertyChangedSignal"); + QTest::addColumn<QString>("propertyChangedHandler"); + QTest::addRow("helloWorld") << u"helloWorld"_s << u"helloWorldChanged"_s + << u"onHelloWorldChanged"_s; + QTest::addRow("$helloWorld") << u"$helloWorld"_s << u"$helloWorldChanged"_s + << u"on$HelloWorldChanged"_s; + QTest::addRow("_helloWorld") << u"_helloWorld"_s << u"_helloWorldChanged"_s + << u"on_HelloWorldChanged"_s; + QTest::addRow("_") << u"_"_s << u"_Changed"_s << u"on_Changed"_s; + QTest::addRow("$") << u"$"_s << u"$Changed"_s << u"on$Changed"_s; + QTest::addRow("ä") << u"ä"_s << u"äChanged"_s << u"onÄChanged"_s; + QTest::addRow("___123a") << u"___123a"_s << u"___123aChanged"_s << u"on___123AChanged"_s; +} +void tst_qqmllanguage::propertySignalNames() +{ + QFETCH(QString, propertyName); + QFETCH(QString, propertyChangedSignal); + QFETCH(QString, propertyChangedHandler); + QQmlEngine e; + QQmlComponent c(&e); + c.setData(uR"( +import QtQuick +Item { + property int %1: 456 + property bool success: false + function f() { %1 = 123; } + function g() { %2(); } + %3: success = true +})"_s.arg(propertyName, propertyChangedSignal, propertyChangedHandler) + .toUtf8(), + QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o != nullptr); + const QMetaObject *metaObject = o->metaObject(); + auto signalIndex = + metaObject->indexOfSignal(propertyChangedSignal.append("()").toStdString().c_str()); + QVERIFY(signalIndex > -1); + auto signal = metaObject->method(signalIndex); + QVERIFY(signal.isValid()); + QSignalSpy changeSignal(o.data(), signal); + QMetaObject::invokeMethod(o.data(), "f"); + QCOMPARE(o->property(propertyName.toStdString().c_str()), 123); + QVERIFY(changeSignal.size() == 1); + QCOMPARE(o->property("success"), true); + QMetaObject::invokeMethod(o.data(), "g"); + QVERIFY(changeSignal.size() == 2); +} +void tst_qqmllanguage::signalNames_data() +{ + QTest::addColumn<QString>("signalName"); + QTest::addColumn<QString>("handlerName"); + QTest::addRow("helloWorld") << u"helloWorld"_s << u"onHelloWorld"_s; + QTest::addRow("$helloWorld") << u"$helloWorld"_s << u"on$HelloWorld"_s; + QTest::addRow("_helloWorld") << u"_helloWorld"_s << u"on_HelloWorld"_s; + QTest::addRow("_") << u"_"_s << u"on_"_s; + QTest::addRow("aUmlaut") << u"ä"_s << u"onÄ"_s; + QTest::addRow("___123a") << u"___123a"_s << u"on___123A"_s; +} +void tst_qqmllanguage::signalNames() +{ + QFETCH(QString, signalName); + QFETCH(QString, handlerName); + QQmlEngine e; + QQmlComponent c(&e); + c.setData(uR"( +import QtQuick +Item { + signal %1() + property bool success: false + function f() { %1(); } + %2: success = true +})"_s.arg(signalName, handlerName) + .toUtf8(), + QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o != nullptr); + const QMetaObject *metaObject = o->metaObject(); + auto signalIndex = metaObject->indexOfSignal(signalName.append("()").toStdString().c_str()); + QVERIFY(signalIndex > -1); + auto signal = metaObject->method(signalIndex); + QVERIFY(signal.isValid()); + QSignalSpy changeSignal(o.data(), signal); + signal.invoke(o.data()); + QVERIFY(changeSignal.size() == 1); + QCOMPARE(o->property("success"), true); + QMetaObject::invokeMethod(o.data(), "f"); + QVERIFY(changeSignal.size() == 2); +} + +void tst_qqmllanguage::callMethodOfAttachedDerived() +{ + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(R"( + import QtQml + import Test + + QtObject { + Component.onCompleted: Counter.increase() + property int v: Counter.value + } + )", QUrl()); + + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("v").toInt(), 99); +} + +void tst_qqmllanguage::multiVersionSingletons() +{ + qmlRegisterTypesAndRevisions<BareSingleton>("MultiVersionSingletons", 11); + qmlRegisterTypesAndRevisions<UncreatableSingleton>("MultiVersionSingletons", 11); + QQmlEngine engine; + + for (const char *name : { "BareSingleton", "UncreatableSingleton"}) { + const int id1 = qmlTypeId("MultiVersionSingletons", 1, 0, name); + const int id2 = qmlTypeId("MultiVersionSingletons", 11, 0, name); + QVERIFY(id1 != id2); + const QJSValue value1 = engine.singletonInstance<QJSValue>(id1); + const QJSValue value2 = engine.singletonInstance<QJSValue>(id2); + QVERIFY(value1.strictlyEquals(value2)); + } +} + +void tst_qqmllanguage::typeAnnotationCycle() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("TypeAnnotationCycle1.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("b").value<QObject*>(), o.data()); +} + +void tst_qqmllanguage::corpseInQmlList() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("corpseInQmlList.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QScopedPointer<QObject> a(new QObject); + QMetaObject::invokeMethod(o.data(), "setB", Q_ARG(QObject *, a.data())); + + QJSValue b = o->property("b").value<QJSValue>(); + QQmlListProperty<QObject> list + = qjsvalue_cast<QQmlListProperty<QObject>>(b.property(QStringLiteral("b"))); + + QCOMPARE(list.count(&list), 1); + QCOMPARE(list.at(&list, 0), a.data()); + + a.reset(); + + b = o->property("b").value<QJSValue>(); + list = qjsvalue_cast<QQmlListProperty<QObject>>(b.property(QStringLiteral("b"))); + + QCOMPARE(list.count(&list), 1); + QCOMPARE(list.at(&list, 0), nullptr); + + // The list itself is still alive: + + list.append(&list, o.data()); + QCOMPARE(list.count(&list), 2); + QCOMPARE(list.at(&list, 0), nullptr); + QCOMPARE(list.at(&list, 1), o.data()); + + list.replace(&list, 0, o.data()); + QCOMPARE(list.count(&list), 2); + QCOMPARE(list.at(&list, 0), o.data()); + QCOMPARE(list.at(&list, 1), o.data()); + + list.removeLast(&list); + QCOMPARE(list.count(&list), 1); + QCOMPARE(list.at(&list, 0), o.data()); + + list.clear(&list); + QCOMPARE(list.count(&list), 0); +} + +void tst_qqmllanguage::objectInQmlListAndGc() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("objectInList.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + // Process the deletion event + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QQmlListProperty<QObject> children = o->property("child").value<QQmlListProperty<QObject>>(); + QCOMPARE(children.count(&children), 1); + QObject *child = children.at(&children, 0); + QVERIFY(child); + QCOMPARE(child->objectName(), QLatin1String("child")); +} + +void tst_qqmllanguage::asCastToInlineComponent() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("asCastToInlineComponent.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), QLatin1String("value: 20")); +} + +void tst_qqmllanguage::deepAliasOnICOrReadonly() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("deepAliasOnICUser.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("borderColor").toString(), QLatin1String("black")); + QCOMPARE(o->property("borderObjectName").toString(), QLatin1String("theLeaf")); + + const QVariant var = o->property("borderVarvar"); + QCOMPARE(var.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(var.toString(), QLatin1String("mauve")); + + QQmlComponent c2(&engine, testFileUrl("deepAliasOnReadonly.qml")); + QVERIFY(c2.isError()); + QVERIFY(c2.errorString().contains( + QLatin1String( + "Invalid property assignment: \"readonlyRectX\" is a read-only property"))); +} + +void tst_qqmllanguage::optionalChainCallOnNullProperty() +{ + QTest::failOnWarning(QRegularExpression(".*Cannot call method 'destroy' of null.*")); + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("optionalChainCallOnNullProperty.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::ambiguousComponents() +{ + auto e1 = std::make_unique<QQmlEngine>(); + e1->addImportPath(dataDirectory()); + bool isInstanceOf = false; + + { + QQmlComponent c(e1.get()); + c.loadUrl(testFileUrl("ambiguousComponents.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QTest::ignoreMessage(QtDebugMsg, "do"); + QMetaObject::invokeMethod(o.data(), "dodo"); + + QMetaObject::invokeMethod(o.data(), "testInstanceOf", Q_RETURN_ARG(bool, isInstanceOf)); + QVERIFY(isInstanceOf); + } + + QQmlEngine e2; + e2.addImportPath(dataDirectory()); + QQmlComponent c2(&e2); + c2.loadUrl(testFileUrl("ambiguousComponents.qml")); + QVERIFY2(c2.isReady(), qPrintable(c2.errorString())); + + QScopedPointer<QObject> o2(c2.create()); + QTest::ignoreMessage(QtDebugMsg, "do"); + QMetaObject::invokeMethod(o2.data(), "dodo"); + + isInstanceOf = false; + QMetaObject::invokeMethod(o2.data(), "testInstanceOf", Q_RETURN_ARG(bool, isInstanceOf)); + QVERIFY(isInstanceOf); + + e1.reset(); + + // We can still invoke the function. This means its CU belongs to e2. + QTest::ignoreMessage(QtDebugMsg, "do"); + QMetaObject::invokeMethod(o2.data(), "dodo"); + + isInstanceOf = false; + QMetaObject::invokeMethod(o2.data(), "testInstanceOf", Q_RETURN_ARG(bool, isInstanceOf)); + QVERIFY(isInstanceOf); +} + +void tst_qqmllanguage::writeNumberToEnumAlias() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("aliasWriter.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("strokeStyle").toInt(), 1); +} + +void tst_qqmllanguage::badInlineComponentAnnotation() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("badICAnnotation.qml"); + QQmlComponent c(&engine, testFileUrl("badICAnnotation.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtCriticalMsg, + qPrintable(url.toString() + ":20: 5 should be coerced to void because the function " + "called is insufficiently annotated. The original " + "value is retained. This will change in a future " + "version of Qt.")); + QTest::ignoreMessage( + QtCriticalMsg, + QRegularExpression(":22: IC\\([^\\)]+\\) should be coerced to void because the " + "function called is insufficiently annotated. The original " + "value is retained. This will change in a future version of " + "Qt\\.")); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("a").toInt(), 5); + + QObject *ic = o->property("ic").value<QObject *>(); + QVERIFY(ic); + + QCOMPARE(o->property("b").value<QObject *>(), ic); + QCOMPARE(o->property("c").value<QObject *>(), ic); + QCOMPARE(o->property("d").value<QObject *>(), nullptr); +} + +void tst_qqmllanguage::manuallyCallSignalHandler() +{ + // TODO: This test verifies the absence of regression legacy behavior. See QTBUG-120573 + // Once we can get rid of the legacy behavior, delete this test! + + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("manuallyCallSignalHandler.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + for (int i = 0; i < 10; ++i) { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + "Property 'onDestruction' of object QQmlComponentAttached\\(0x[0-9a-f]+\\) is a signal " + "handler\\. You should not call it directly\\. Make it a proper function and call that " + "or emit the signal\\.")); + QTest::ignoreMessage(QtDebugMsg, "evil!"); + QScopedPointer<QObject> o(c.create()); + QTest::ignoreMessage(QtDebugMsg, "evil!"); + } +} + +void tst_qqmllanguage::overrideDefaultProperty() +{ + QQmlEngine e; + const QUrl url = testFileUrl("overrideDefaultProperty.qml"); + + // Should not crash here! + + QQmlComponent c(&e, url); + QVERIFY(c.isError()); + QCOMPARE(c.errorString(), + url.toString() + QLatin1String(":5 Cannot assign object to list property \"data\"\n")); +} + +void tst_qqmllanguage::enumScopes() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("enumScopes.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("singletonUnscoped"), false); + QCOMPARE(o->property("singletonScoped"), true); + QCOMPARE(o->property("nonSingletonUnscoped"), false); + QCOMPARE(o->property("nonSingletonScoped"), true); + + QCOMPARE(o->property("singletonScopedValue").toInt(), int(EnumProviderSingleton::Expected::Value)); + QCOMPARE(o->property("singletonUnscopedValue").toInt(), int(EnumProviderSingleton::Expected::Value)); +} + +void tst_qqmllanguage::typedObjectList() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("typedObjectList.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QJSValue b = o->property("b").value<QJSValue>(); + auto list = qjsvalue_cast<QQmlListProperty<QQmlComponent>>(b.property(QStringLiteral("b"))); + + QCOMPARE(list.count(&list), 1); + QVERIFY(list.at(&list, 0) != nullptr); +} + +void tst_qqmllanguage::jsonArrayPropertyBehavesLikeAnArray() { + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("jsonArrayProperty.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("concatenatedJsonArray"), o->property("concatenatedJsArray")); + QVERIFY(o->property("entriesMatch").toBool()); + QCOMPARE(o->property("jsonArrayEvery"), o->property("jsArrayEvery")); + QCOMPARE(o->property("jsonArrayFiltered"), o->property("jsArrayFiltered")); + QCOMPARE(o->property("jsonArrayFind"), o->property("jsArrayFind")); + QCOMPARE(o->property("jsonArrayFindIndex"), o->property("jsArrayFindIndex")); + QCOMPARE(o->property("jsonArrayForEach"), o->property("jsArrayForEach")); + QCOMPARE(o->property("jsonArrayIncludes"), o->property("jsArrayIncludes")); + QCOMPARE(o->property("jsonArrayIndexOf"), o->property("jsArrayIndexOf")); + QCOMPARE(o->property("jsonArrayJoin"), o->property("jsArrayJoin")); + QVERIFY(o->property("keysMatch").toBool()); + QCOMPARE(o->property("jsonArrayLastIndexOf"), o->property("jsArrayLastIndexOf")); + QCOMPARE(o->property("jsonArrayMap"), o->property("jsArrayMap")); + QCOMPARE(o->property("jsonArrayReduce"), o->property("jsArrayReduce")); + QCOMPARE(o->property("jsonArrayReduceRight"), o->property("jsArrayReduceRight")); + QCOMPARE(o->property("jsonArraySlice"), o->property("jsArraySlice")); + QCOMPARE(o->property("jsonArraySome"), o->property("jsArraySome")); + QCOMPARE(o->property("stringifiedLocaleJsonArray"), o->property("stringifiedLocaleJsArray")); + QCOMPARE(o->property("stringifiedJsonArray"), o->property("stringifiedJsArray")); + QVERIFY(o->property("valuesMatch").toBool()); + + QVERIFY(o->property("jsonArrayWasCopiedWithin").toBool()); + QVERIFY(o->property("jsonArrayWasFilled").toBool()); + QVERIFY(o->property("jsonArrayWasPopped").toBool()); + QVERIFY(o->property("jsonArrayWasPushed").toBool()); + QVERIFY(o->property("jsonArrayWasReversed").toBool()); + QVERIFY(o->property("jsonArrayWasShifted").toBool()); + QVERIFY(o->property("jsonArrayWasSpliced").toBool()); + QVERIFY(o->property("jsonArrayWasUnshifted").toBool()); + QVERIFY(o->property("jsonArrayWasSorted").toBool()); +} + +void tst_qqmllanguage::invokableCtors() +{ + QQmlEngine e; + + const QUrl url = testFileUrl("invokableCtors.qml"); + + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + const QString urlString = url.toString(); + QTest::ignoreMessage(QtWarningMsg, qPrintable( + urlString + ":9: You are calling a Q_INVOKABLE constructor of " + "InvokableSingleton which is a singleton in QML.")); + + // Extended types look like types without any constructors. + // Therefore they aren't even FunctionObjects. + QTest::ignoreMessage(QtWarningMsg, qPrintable( + urlString + ":10: TypeError: Type error")); + + QTest::ignoreMessage(QtWarningMsg, qPrintable( + urlString + ":11: You are calling a Q_INVOKABLE constructor of " + "InvokableUncreatable which is uncreatable in QML.")); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QObject *oo = qvariant_cast<QObject *>(o->property("oo")); + QVERIFY(oo); + QObject *pp = qvariant_cast<QObject *>(o->property("pp")); + QVERIFY(pp); + QCOMPARE(pp->parent(), oo); + + InvokableValueType vv = qvariant_cast<InvokableValueType>(o->property("v")); + QCOMPARE(vv.m_s, "green"); + + InvokableSingleton *i = qvariant_cast<InvokableSingleton *>(o->property("i")); + QVERIFY(i); + QCOMPARE(i->m_a, 5); + QCOMPARE(i->parent(), oo); + + QVariant k = o->property("k"); + QCOMPARE(k.metaType(), QMetaType::fromType<InvokableExtended *>()); + QCOMPARE(k.value<InvokableExtended *>(), nullptr); + + InvokableUncreatable *l = qvariant_cast<InvokableUncreatable *>(o->property("l")); + QVERIFY(l); +} + +void tst_qqmllanguage::nestedVectors() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("nestedVectors.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + NestedVectors *n = qobject_cast<NestedVectors *>(o.data()); + QVERIFY(n); + + const std::vector<std::vector<int>> expected1 { { 1, 2, 3 }, { 4, 5 } }; + const QVariant list1 = n->property("list1"); + QCOMPARE(list1.metaType(), QMetaType::fromType<std::vector<std::vector<int>>>()); + QCOMPARE(list1.value<std::vector<std::vector<int>>>(), expected1); + + const std::vector<std::vector<int>> expected2 { { 2, 3, 4 }, { 5, 6 } }; + QCOMPARE(n->getList(), expected2); +} + +void tst_qqmllanguage::optimizedSequenceShift() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("optimizedSequenceShift.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("changes").toInt(), 2); + + const QVariant one = o->property("one"); + QCOMPARE(one.metaType(), QMetaType::fromType<int>()); + QCOMPARE(one.toInt(), 1); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmllistcompositor/CMakeLists.txt b/tests/auto/qml/qqmllistcompositor/CMakeLists.txt index c05b97f6d2..b6999900ba 100644 --- a/tests/auto/qml/qqmllistcompositor/CMakeLists.txt +++ b/tests/auto/qml/qqmllistcompositor/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmllistcompositor Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllistcompositor LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmllistcompositor SOURCES tst_qqmllistcompositor.cpp diff --git a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp index 2d02cac9f8..c10ceafeae 100644 --- a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp +++ b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtest.h> #include <private/qqmllistcompositor_p.h> @@ -179,7 +180,7 @@ void tst_qqmllistcompositor::find() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); compositor.find(startGroup, startIndex); @@ -241,7 +242,7 @@ void tst_qqmllistcompositor::findInsertPosition() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QQmlListCompositor::insert_iterator it = compositor.findInsertPosition(group, index); @@ -499,7 +500,7 @@ void tst_qqmllistcompositor::clearFlags() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Remove> removes; @@ -681,7 +682,7 @@ void tst_qqmllistcompositor::setFlags() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Insert> inserts; @@ -968,7 +969,7 @@ void tst_qqmllistcompositor::move() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Remove> removes; @@ -1202,7 +1203,7 @@ void tst_qqmllistcompositor::listItemsInserted() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Insert> inserts; @@ -1338,7 +1339,7 @@ void tst_qqmllistcompositor::listItemsRemoved() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Remove> removes; @@ -1532,7 +1533,7 @@ void tst_qqmllistcompositor::listItemsMoved() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Remove> removes; @@ -1612,7 +1613,7 @@ void tst_qqmllistcompositor::listItemsChanged() compositor.setGroupCount(4); compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - foreach (const Range &range, ranges) + for (const Range &range : std::as_const(ranges)) compositor.append(range.list, range.index, range.count, range.flags); QVector<C::Change> changes; diff --git a/tests/auto/qml/qqmllistmodel/CMakeLists.txt b/tests/auto/qml/qqmllistmodel/CMakeLists.txt index 7c689e643c..133a2dce5d 100644 --- a/tests/auto/qml/qqmllistmodel/CMakeLists.txt +++ b/tests/auto/qml/qqmllistmodel/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmllistmodel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllistmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmllistmodel/data/nestedLists.qml b/tests/auto/qml/qqmllistmodel/data/nestedLists.qml new file mode 100644 index 0000000000..4aea6f1ae1 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/nestedLists.qml @@ -0,0 +1,37 @@ +import QtQuick + +Item { + id: mainWindow + + function load(data) { + model.clear() + for (var i = 0; i < data.length; i++) + model.append(data[i]) + } + + ListModel { + id: model + } + + Repeater { + objectName: "topLevel" + model: model + Item { + objectName: _headline + Repeater { + objectName: "month" + model: _weeks + Item { + objectName: index + Repeater { + objectName: "week" + model: _week + Item { + objectName: _day + } + } + } + } + } + } +} diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index e522f280a3..d738a7f68d 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -1,13 +1,15 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickanimation_p.h> #include <QtQuick/private/qquicklistview_p.h> +#include <QtQuick/private/qquickrepeater_p.h> #include <QtQml/private/qqmlengine_p.h> #include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qqmlexpression_p.h> +#include <QtQml/private/qqmlsignalnames_p.h> #include <QQmlComponent> #include <QtCore/qtimer.h> @@ -119,6 +121,7 @@ private slots: void objectOwnershipFlip(); void enumsInListElement(); void protectQObjectFromGC(); + void nestedLists(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -248,8 +251,8 @@ void tst_qqmllistmodel::static_types() QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); if (error.isEmpty()) { QVariant actual = obj->property("test"); @@ -257,8 +260,6 @@ void tst_qqmllistmodel::static_types() QCOMPARE(actual, value); QCOMPARE(actual.toString(), value.toString()); } - - delete obj; } void tst_qqmllistmodel::static_i18n_data() @@ -319,15 +320,13 @@ void tst_qqmllistmodel::static_i18n() QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); QVariant actual = obj->property("test"); QCOMPARE(actual, value); QCOMPARE(actual.toString(), value.toString()); - - delete obj; } void tst_qqmllistmodel::dynamic_i18n_data() @@ -368,16 +367,15 @@ void tst_qqmllistmodel::dynamic_i18n() QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); QVariant actual = obj->property("test"); QCOMPARE(actual, value); QCOMPARE(actual.toString(), value.toString()); - - delete obj; } + void tst_qqmllistmodel::static_nestedElements() { QFETCH(int, elementCount); @@ -406,14 +404,12 @@ void tst_qqmllistmodel::static_nestedElements() QQmlComponent component(&engine); component.setData(componentStr.toUtf8(), QUrl::fromLocalFile("")); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); QVariant count = obj->property("count"); QCOMPARE(count.typeId(), QMetaType::Int); QCOMPARE(count.toInt(), elementCount); - - delete obj; } void tst_qqmllistmodel::static_nestedElements_data() @@ -606,8 +602,8 @@ void tst_qqmllistmodel::enumerate() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("enumerate.qml")); QVERIFY(!component.isError()); - QQuickItem *item = qobject_cast<QQuickItem*>(component.create()); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component.create()) }; + QVERIFY(item); QLatin1String expectedStrings[] = { QLatin1String("val1=1Y"), @@ -638,8 +634,6 @@ void tst_qqmllistmodel::enumerate() } QCOMPARE(matchCount, expectedStringCount); - - delete item; } void tst_qqmllistmodel::error_data() @@ -779,9 +773,9 @@ void tst_qqmllistmodel::get() component.setData( "import QtQuick 2.0\n" "ListModel {}\n", QUrl()); - QQmlListModel *model = qobject_cast<QQmlListModel*>(component.create()); + std::unique_ptr<QQmlListModel> model { qobject_cast<QQmlListModel*>(component.create()) }; model->setDynamicRoles(dynamicRoles); - engine.rootContext()->setContextProperty("model", model); + engine.rootContext()->setContextProperty("model", model.get()); RUNEXPR("model.append({roleA: 100})"); RUNEXPR("model.append({roleA: 200, roleB: 400})"); @@ -789,12 +783,12 @@ void tst_qqmllistmodel::get() RUNEXPR("model.append({roleC: {} })"); RUNEXPR("model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); - QSignalSpy spy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QQmlExpression expr(engine.rootContext(), model, expression); + QSignalSpy spy(model.get(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); + QQmlExpression expr(engine.rootContext(), model.get(), expression); expr.evaluate(); QVERIFY(!expr.hasError()); - int role = roleFromName(model, roleName); + int role = roleFromName(model.get(), roleName); QVERIFY(role >= 0); if (roleValue.typeId() == QMetaType::QVariantList) { @@ -810,8 +804,6 @@ void tst_qqmllistmodel::get() QCOMPARE(spyResult.at(0).value<QModelIndex>(), model->index(index, 0, QModelIndex())); QCOMPARE(spyResult.at(1).value<QModelIndex>(), model->index(index, 0, QModelIndex())); // only 1 item is modified at a time QCOMPARE(spyResult.at(2).value<QVector<int> >(), (QVector<int>() << role)); - - delete model; } void tst_qqmllistmodel::get_data() @@ -857,11 +849,11 @@ void tst_qqmllistmodel::get_nested() component.setData( "import QtQuick 2.0\n" "ListModel {}", QUrl()); - QQmlListModel *model = qobject_cast<QQmlListModel*>(component.create()); + std::unique_ptr<QQmlListModel> model { qobject_cast<QQmlListModel*>(component.create()) }; model->setDynamicRoles(dynamicRoles); QVERIFY(component.errorString().isEmpty()); QQmlListModel *childModel; - engine.rootContext()->setContextProperty("model", model); + engine.rootContext()->setContextProperty("model", model.get()); RUNEXPR("model.append({ listRoleA: [\n" "{ roleA: 100 },\n" @@ -908,16 +900,16 @@ void tst_qqmllistmodel::get_nested() for (int i=0; i<testData.size(); i++) { int outerListIndex = testData[i].first; QString outerListRoleName = testData[i].second; - int outerListRole = roleFromName(model, outerListRoleName); + int outerListRole = roleFromName(model.get(), outerListRoleName); QVERIFY(outerListRole >= 0); childModel = qobject_cast<QQmlListModel*>(model->data(outerListIndex, outerListRole).value<QObject*>()); QVERIFY(childModel); QString extendedExpression = QString("get(%1).%2.%3").arg(outerListIndex).arg(outerListRoleName).arg(expression); - QQmlExpression expr(engine.rootContext(), model, extendedExpression); + QQmlExpression expr(engine.rootContext(), model.get(), extendedExpression); - QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); expr.evaluate(); QVERIFY(!expr.hasError()); @@ -935,8 +927,6 @@ void tst_qqmllistmodel::get_nested() QCOMPARE(spyResult.at(1).value<QModelIndex>(), childModel->index(index, 0, QModelIndex())); // only 1 item is modified at a time QCOMPARE(spyResult.at(2).value<QVector<int> >(), (QVector<int>() << role)); } - - delete model; } void tst_qqmllistmodel::get_nested_data() @@ -949,16 +939,14 @@ void tst_qqmllistmodel::crash_model_with_multiple_roles() { QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); - QObject *rootItem = component.create(); + std::unique_ptr<QObject> rootItem { component.create() }; QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != nullptr); + QVERIFY(rootItem); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); QVERIFY(model != nullptr); // used to cause a crash model->setProperty(0, "black", true); - - delete rootItem; } void tst_qqmllistmodel::crash_model_with_unknown_roles() @@ -983,22 +971,19 @@ void tst_qqmllistmodel::crash_model_with_dynamic_roles() // setting a dynamic role to a QObject value, then triggering dtor QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); - QObject *rootItem = component.create(); + std::unique_ptr<QObject> rootItem { component.create() }; qWarning() << component.errorString(); QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); + QVERIFY(rootItem.get() != 0); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); QVERIFY(model != 0); QMetaObject::invokeMethod(model, "appendNewElement"); - QObject *testObj = new QObject; - model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); - delete testObj; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(std::make_unique<QObject>().get())); - // delete the root item, which will cause the model dtor to run - // previously, this would crash as it attempted to delete testObj. - delete rootItem; + // Let root item go out of scope to let the model dtor run. + // Previously, this would crash as it attempted to delete the already-deleted temporary QObject. } { @@ -1006,22 +991,18 @@ void tst_qqmllistmodel::crash_model_with_dynamic_roles() // DynamicRoleModelNode::updateValues() to trigger unsafe qobject_cast QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); - QObject *rootItem = component.create(); + std::unique_ptr<QObject> rootItem { component.create() }; qWarning() << component.errorString(); QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); + QVERIFY(rootItem.get() != 0); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); QVERIFY(model != 0); QMetaObject::invokeMethod(model, "appendNewElement"); - QObject *testObj = new QObject; - model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); - delete testObj; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(std::make_unique<QObject>().get())); QMetaObject::invokeMethod(model, "setElementAgain"); - - delete rootItem; } { @@ -1098,21 +1079,21 @@ void tst_qqmllistmodel::property_changes() expr.evaluate(); QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString())); - QString signalHandler = "on" + QString(roleName[0].toUpper()) + roleName.mid(1, roleName.size()) + "Changed:"; + QString signalHandler = QQmlSignalNames::propertyNameToChangedHandlerName(roleName); QString qml = "import QtQuick 2.0\n" "Connections {\n" "property bool gotSignal: false\n" "target: model.get(" + QString::number(listIndex) + ")\n" - + signalHandler + " gotSignal = true\n" + + signalHandler + ": gotSignal = true\n" "}\n"; QQmlComponent component(&engine); component.setData(qml.toUtf8(), QUrl::fromLocalFile("")); engine.rootContext()->setContextProperty("model", &model); - QObject *connectionsObject = component.create(); + std::unique_ptr<QObject> connectionsObject { component.create() }; QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); - QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); expr.setExpression(script_change); expr.evaluate(); @@ -1132,8 +1113,6 @@ void tst_qqmllistmodel::property_changes() expr.setExpression(testExpression); QCOMPARE(expr.evaluate().toBool(), true); - - delete connectionsObject; } void tst_qqmllistmodel::property_changes_data() @@ -1354,10 +1333,8 @@ void tst_qqmllistmodel::empty_element_warning() component.setData(qml.toUtf8(), QUrl::fromLocalFile(QString("dummy.qml"))); QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); - - delete obj; + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); } void tst_qqmllistmodel::datetime_data() @@ -1549,6 +1526,8 @@ void tst_qqmllistmodel::modify_through_delegate() " ListElement { name: \"Doe\"; age: 33 }\n" " }\n" " ListView {\n" + " height: 100\n" \ + " width: 100\n" \ " model: testModel\n" " delegate: Item {\n" " Component.onCompleted: model.age = 18;\n" @@ -1929,6 +1908,154 @@ void tst_qqmllistmodel::protectQObjectFromGC() } } +static QVariantList createLast7Days() +{ + QVariantList last7DaysList; + for (int i = 0; i < 7; i++) { + QVariantMap map; + map.insert("_day", i); + last7DaysList.append(map); + } + return last7DaysList; +} + +static QVariantList createWeekChartModels() +{ + QVariantList list; + for (int i = 0; i < 4; i++) { + QVariantMap map; + map.insert("_week", createLast7Days()); + list.append(map); + } + return list; +} + +static QVariantList createVariantModel() +{ + QVariantMap element1; + element1.insert("_headline", "Element 1"); + element1.insert("_weeks", createWeekChartModels()); + + QVariantMap element2; + element2.insert("_headline", "Element 2"); + element2.insert("_weeks", createWeekChartModels()); + + QVariantMap element3; + element3.insert("_headline", "Element 3"); + element3.insert("_weeks", createWeekChartModels()); + + QVariantList list; + list.append(element1); + list.append(element2); + list.append(element3); + + return list; +} + +class Day : public QObject +{ + Q_OBJECT + Q_PROPERTY(int _day READ _day CONSTANT) +public: + Day(int day, QObject *parent = nullptr) : QObject(parent), day(day) {} + int _day() const { return day; } +private: + int day = 0; +}; + +class Week : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<Day> _week READ _week) +public: + Week(QObject *parent = nullptr) : QObject(parent) + { + for (int i = 0; i < 7; ++i) + week.append(new Day(i, this)); + } + + QQmlListProperty<Day> _week() { return QQmlListProperty<Day>(this, &week); } + +private: + QList<Day *> week; +}; + +class Month : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<Week> _weeks READ _weeks) + Q_PROPERTY(QString _headline READ _headline CONSTANT) +public: + + Month(int i, QObject *parent = nullptr) + : QObject(parent) + , headline(QLatin1String("Element ") + QString::number(i)) + { + for (int i = 0; i < 4; ++i) + weeks.append(new Week(this)); + } + + QQmlListProperty<Week> _weeks() { return QQmlListProperty<Week>(this, &weeks); } + QString _headline() const { return headline; } + +private: + QList<Week *> weeks; + QString headline; +}; + +static void verifyLists(const QVariantList &list, QQuickRepeater *topLevel) +{ + QVERIFY(topLevel); + QCOMPARE(topLevel->count(), 3); + + for (int month = 0; month < 3; ++month) { + const QVariantMap monthData = list[month].toMap(); + const QQuickItem *monthItem = topLevel->itemAt(month); + QCOMPARE(monthItem->objectName(), monthData["_headline"].toString()); + const QQuickRepeater *monthRepeater = monthItem->findChild<QQuickRepeater *>("month"); + QVERIFY(monthRepeater); + QCOMPARE(monthRepeater->count(), 4); + const QVariantList weekList = monthData["_weeks"].toList(); + for (int week = 0; week < 4; ++week) { + const QVariantList weekData = weekList[week].toMap()["_week"].toList(); + const QQuickItem *weekItem = monthRepeater->itemAt(week); + QCOMPARE(weekItem->objectName(), QString::number(week)); + const QQuickRepeater *weekRepeater = weekItem->findChild<QQuickRepeater *>("week"); + QVERIFY(weekRepeater); + QCOMPARE(weekRepeater->count(), 7); + for (int day = 0; day < 7; ++day) { + const QVariantMap dayData = weekData[day].toMap(); + const QQuickItem *dayItem = weekRepeater->itemAt(day); + QCOMPARE(dayItem->objectName(), dayData["_day"]); + } + } + } +} + +void tst_qqmllistmodel::nestedLists() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("nestedLists.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QQuickRepeater *topLevel = o->findChild<QQuickRepeater *>("topLevel"); + + const QVariantList list = createVariantModel(); + QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(list))); + verifyLists(list, topLevel); + + const QObjectList objects { + new Month(1, o.data()), + new Month(2, o.data()), + new Month(3, o.data()) + }; + + QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(objects))); + verifyLists(list, topLevel); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" diff --git a/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt b/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt index ac77f65b68..93d597e687 100644 --- a/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt +++ b/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmllistmodelworkerscript Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllistmodelworkerscript LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp index 4875602314..93f18ea8d5 100644 --- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp +++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtest.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquicktext_p.h> @@ -55,7 +56,7 @@ public: private: int roleFromName(const QQmlListModel *model, const QString &roleName); - QQuickItem *createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model); + std::unique_ptr<QQuickItem> createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model); void waitForWorker(QQuickItem *item); static bool compareVariantList(const QVariantList &testList, QVariant object); @@ -134,9 +135,9 @@ int tst_qqmllistmodelworkerscript::roleFromName(const QQmlListModel *model, cons return model->roleNames().key(roleName.toUtf8(), -1); } -QQuickItem *tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model) +std::unique_ptr<QQuickItem> tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model) { - QQuickItem *item = qobject_cast<QQuickItem*>(component->create()); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component->create()) }; QQmlEngine::setContextForObject(model, eng->rootContext()); if (item) item->setProperty("model", QVariant::fromValue(model)); @@ -326,15 +327,16 @@ void tst_qqmllistmodelworkerscript::dynamic_worker() model.setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model); + QVERIFY(item); QSignalSpy spyCount(&model, SIGNAL(countChanged())); if (script[0] == QLatin1Char('{') && script[script.size()-1] == QLatin1Char('}')) script = script.mid(1, script.size() - 2); QVariantList operations; - foreach (const QString &s, script.split(';')) { + const QStringList statements = script.split(';'); + for (const QString &s : statements) { if (!s.isEmpty()) operations << s; } @@ -342,15 +344,15 @@ void tst_qqmllistmodelworkerscript::dynamic_worker() if (isValidErrorMessage(warning, dynamicRoles)) QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker", Q_ARG(QVariant, operations))); - waitForWorker(item); - QCOMPARE(QQmlProperty(item, "result").read().toInt(), result); + waitForWorker(item.get()); + QCOMPARE(QQmlProperty(item.get(), "result").read().toInt(), result); if (model.count() > 0) QVERIFY(spyCount.size() > 0); - delete item; + item.reset(); qApp->processEvents(); } @@ -377,13 +379,14 @@ void tst_qqmllistmodelworkerscript::dynamic_worker_sync() model.setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model); + QVERIFY(item); if (script[0] == QLatin1Char('{') && script[script.size()-1] == QLatin1Char('}')) script = script.mid(1, script.size() - 2); QVariantList operations; - foreach (const QString &s, script.split(';')) { + const QStringList statements = script.split(';'); + for (const QString &s : statements) { if (!s.isEmpty()) operations << s; } @@ -393,14 +396,14 @@ void tst_qqmllistmodelworkerscript::dynamic_worker_sync() // execute a set of commands on the worker list model, then check the // changes are reflected in the list model in the main thread - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker", Q_ARG(QVariant, operations.mid(0, operations.size()-1)))); - waitForWorker(item); + waitForWorker(item.get()); QQmlExpression e(eng.rootContext(), &model, operations.last().toString()); QCOMPARE(e.evaluate().toInt(), result); - delete item; + item.reset(); qApp->processEvents(); } @@ -440,25 +443,25 @@ void tst_qqmllistmodelworkerscript::get_worker() model.setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model); + QVERIFY(item); // Add some values like get() test - RUNEVAL(item, "model.append({roleA: 100})"); - RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); - RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); - RUNEVAL(item, "model.append({roleC: {} })"); - RUNEVAL(item, "model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); + RUNEVAL(item.get(), "model.append({roleA: 100})"); + RUNEVAL(item.get(), "model.append({roleA: 200, roleB: 400})"); + RUNEVAL(item.get(), "model.append({roleA: 200, roleB: 400})"); + RUNEVAL(item.get(), "model.append({roleC: {} })"); + RUNEVAL(item.get(), "model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); int role = roleFromName(&model, roleName); QVERIFY(role >= 0); - QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); // in the worker thread, change the model data and call sync() - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker", Q_ARG(QVariant, QStringList(expression)))); - waitForWorker(item); + waitForWorker(item.get()); // see if we receive the model changes in the main thread's model if (roleValue.typeId() == QMetaType::QVariantList) { @@ -474,8 +477,6 @@ void tst_qqmllistmodelworkerscript::get_worker() QCOMPARE(spyResult.at(0).value<QModelIndex>(), model.index(index, 0, QModelIndex())); QCOMPARE(spyResult.at(1).value<QModelIndex>(), model.index(index, 0, QModelIndex())); // only 1 item is modified at a time QVERIFY(spyResult.at(2).value<QVector<int> >().contains(role)); - - delete item; } void tst_qqmllistmodelworkerscript::get_worker_data() @@ -570,18 +571,18 @@ void tst_qqmllistmodelworkerscript::property_changes_worker() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("model.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); - QQuickItem *item = createWorkerTest(&engine, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&engine, &component, &model); + QVERIFY(item); QQmlExpression expr(engine.rootContext(), &model, script_setup); expr.evaluate(); - QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString())); - QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker", Q_ARG(QVariant, QStringList(script_change)))); - waitForWorker(item); + waitForWorker(item.get()); // test itemsChanged() is emitted correctly if (itemsChanged) { @@ -592,7 +593,7 @@ void tst_qqmllistmodelworkerscript::property_changes_worker() QCOMPARE(spyItemsChanged.size(), 0); } - delete item; + item.reset(); qApp->processEvents(); } @@ -617,12 +618,12 @@ void tst_qqmllistmodelworkerscript::worker_sync() model.setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workersync.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model); + QVERIFY(item); QCOMPARE(model.count(), 0); - QVERIFY(QMetaObject::invokeMethod(item, "addItem0")); + QVERIFY(QMetaObject::invokeMethod(item.get(), "addItem0")); QCOMPARE(model.count(), 2); QVariant childData = model.data(0, 0); @@ -633,39 +634,39 @@ void tst_qqmllistmodelworkerscript::worker_sync() QSignalSpy spyModelInserted(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); QSignalSpy spyChildInserted(childModel, SIGNAL(rowsInserted(QModelIndex,int,int))); - QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "addItemViaWorker")); + waitForWorker(item.get()); QCOMPARE(model.count(), 2); QCOMPARE(childModel->count(), 1); QCOMPARE(spyModelInserted.size(), 0); QCOMPARE(spyChildInserted.size(), 0); - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync")); + waitForWorker(item.get()); QCOMPARE(model.count(), 2); QCOMPARE(childModel->count(), 2); QCOMPARE(spyModelInserted.size(), 0); QCOMPARE(spyChildInserted.size(), 1); - QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "addItemViaWorker")); + waitForWorker(item.get()); QCOMPARE(model.count(), 2); QCOMPARE(childModel->count(), 2); QCOMPARE(spyModelInserted.size(), 0); QCOMPARE(spyChildInserted.size(), 1); - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync")); + waitForWorker(item.get()); QCOMPARE(model.count(), 2); QCOMPARE(childModel->count(), 3); QCOMPARE(spyModelInserted.size(), 0); QCOMPARE(spyChildInserted.size(), 2); - delete item; + item.reset(); qApp->processEvents(); } @@ -682,53 +683,51 @@ void tst_qqmllistmodelworkerscript::worker_remove_element() model.setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model); + QVERIFY(item); QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); QCOMPARE(model.count(), 0); QCOMPARE(spyModelRemoved.size(), 0); - QVERIFY(QMetaObject::invokeMethod(item, "addItem")); + QVERIFY(QMetaObject::invokeMethod(item.get(), "addItem")); QCOMPARE(model.count(), 1); - QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "removeItemViaWorker")); + waitForWorker(item.get()); QCOMPARE(model.count(), 1); QCOMPARE(spyModelRemoved.size(), 0); - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync")); + waitForWorker(item.get()); QCOMPARE(model.count(), 0); QCOMPARE(spyModelRemoved.size(), 1); - delete item; + item.reset(); qApp->processEvents(); { //don't crash if model was deleted earlier - QQmlListModel* model = new QQmlListModel; + std::unique_ptr<QQmlListModel> model = std::make_unique<QQmlListModel>(); model->setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, model.get()); + QVERIFY(item); - QVERIFY(QMetaObject::invokeMethod(item, "addItem")); + QVERIFY(QMetaObject::invokeMethod(item.get(), "addItem")); QCOMPARE(model->count(), 1); - QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker")); - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - delete model; + QVERIFY(QMetaObject::invokeMethod(item.get(), "removeItemViaWorker")); + QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync")); + model.reset(); qApp->processEvents(); //must not crash here - waitForWorker(item); - - delete item; + waitForWorker(item.get()); } } @@ -745,31 +744,31 @@ void tst_qqmllistmodelworkerscript::worker_remove_list() model.setDynamicRoles(dynamicRoles); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workerremovelist.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model); + QVERIFY(item); QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); QCOMPARE(model.count(), 0); QCOMPARE(spyModelRemoved.size(), 0); - QVERIFY(QMetaObject::invokeMethod(item, "addList")); + QVERIFY(QMetaObject::invokeMethod(item.get(), "addList")); QCOMPARE(model.count(), 1); - QVERIFY(QMetaObject::invokeMethod(item, "removeListViaWorker")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "removeListViaWorker")); + waitForWorker(item.get()); QCOMPARE(model.count(), 1); QCOMPARE(spyModelRemoved.size(), 0); - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); + QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync")); + waitForWorker(item.get()); QCOMPARE(model.count(), 0); QCOMPARE(spyModelRemoved.size(), 1); - delete item; + item.reset(); qApp->processEvents(); } @@ -792,8 +791,8 @@ void tst_qqmllistmodelworkerscript::dynamic_role() model.setDynamicRoles(true); QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&engine, &component, &model); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item = createWorkerTest(&engine, &component, &model); + QVERIFY(item); QQmlExpression preExp(engine.rootContext(), &model, preamble); QCOMPARE(preExp.evaluate().toInt(), 0); @@ -801,21 +800,22 @@ void tst_qqmllistmodelworkerscript::dynamic_role() if (script[0] == QLatin1Char('{') && script[script.size()-1] == QLatin1Char('}')) script = script.mid(1, script.size() - 2); QVariantList operations; - foreach (const QString &s, script.split(';')) { + const QStringList statements = script.split(';'); + for (const QString &s : statements) { if (!s.isEmpty()) operations << s; } // execute a set of commands on the worker list model, then check the // changes are reflected in the list model in the main thread - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker", Q_ARG(QVariant, operations.mid(0, operations.size()-1)))); - waitForWorker(item); + waitForWorker(item.get()); QQmlExpression e(engine.rootContext(), &model, operations.last().toString()); QCOMPARE(e.evaluate().toInt(), result); - delete item; + item.reset(); qApp->processEvents(); } diff --git a/tests/auto/qml/qqmllistreference/CMakeLists.txt b/tests/auto/qml/qqmllistreference/CMakeLists.txt index 3286531a45..0fa70259b2 100644 --- a/tests/auto/qml/qqmllistreference/CMakeLists.txt +++ b/tests/auto/qml/qqmllistreference/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmllistreference Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllistreference LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml b/tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml new file mode 100644 index 0000000000..9d2fd3e0b2 --- /dev/null +++ b/tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + id: self + + function createList() : list<QtObject> { return [self] } + Component.onCompleted: console.log(createList()) +} diff --git a/tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml b/tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml new file mode 100644 index 0000000000..35c8d66dad --- /dev/null +++ b/tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml @@ -0,0 +1,21 @@ +import QtQuick 2.7 +import Test + +Item { + id: root + + TestItem { + id: testItem + } + + Component.onCompleted : { + testItem.data.push(null); + testItem.data.length = 5; + testItem.data.unshift(null); + + var state = Qt.createQmlObject( "import QtQuick 2.7; State{ name: 'MyState' }", root, "dynamicState" ); + root.states.length = 5; + root.states.push(null); + root.states.unshift(state); + } +} diff --git a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp index 049ffb2d72..76711a00e6 100644 --- a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp +++ b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QUrl> @@ -58,6 +58,9 @@ private slots: void jsArrayMethods(); void jsArrayMethodsWithParams_data(); void jsArrayMethodsWithParams(); + void listIgnoresNull_data() { modeData(); } + void listIgnoresNull(); + void consoleLogSyntheticList(); }; class TestType : public QObject @@ -73,12 +76,18 @@ public: SyntheticClearAndReplace, SyntheticRemoveLast, SyntheticRemoveLastAndReplace, - AutomaticPointer + AutomaticPointer, + IgnoreNullValues, }; static void append(QQmlListProperty<TestType> *p, TestType *v) { reinterpret_cast<QList<TestType *> *>(p->data)->append(v); } + static void appendNoNullValues(QQmlListProperty<TestType> *p, TestType *v) { + if (!v) + return; + reinterpret_cast<QList<TestType *> *>(p->data)->append(v); + } static qsizetype count(QQmlListProperty<TestType> *p) { return reinterpret_cast<QList<TestType *> *>(p->data)->size(); } @@ -121,6 +130,10 @@ public: case AutomaticPointer: property = QQmlListProperty<TestType>(this, &data); break; + case IgnoreNullValues: + property = QQmlListProperty<TestType>(this, &data, appendNoNullValues, count, at, clear, + replace, removeLast); + break; } } @@ -142,6 +155,7 @@ void tst_qqmllistreference::modeData() QTest::addRow("SyntheticClearAndReplace") << TestType::SyntheticClearAndReplace; QTest::addRow("SyntheticRemoveLast") << TestType::SyntheticRemoveLast; QTest::addRow("SyntheticRemoveLastAndReplace") << TestType::SyntheticRemoveLastAndReplace; + QTest::addRow("IgnoreNullValues") << TestType::IgnoreNullValues; } void tst_qqmllistreference::initTestCase() @@ -1036,6 +1050,50 @@ void tst_qqmllistreference::jsArrayMethodsWithParams() QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf")); } +/*! + Some of our list implementations ignore attempts to append a null object. + This should result in warnings or type errors, and not crash our wrapper + code. +*/ +void tst_qqmllistreference::listIgnoresNull() +{ + QFETCH(const TestType::Mode, mode); + static TestType::Mode globalMode; + globalMode = mode; + struct TestItem : public TestType + { + TestItem() : TestType(globalMode) {} + }; + + const auto id = qmlRegisterType<TestItem>("Test", 1, 0, "TestItem"); + const auto unregister = qScopeGuard([id]{ + QQmlPrivate::qmlunregister(QQmlPrivate::TypeRegistration, id); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("listIgnoresNull.qml")); + + // For lists that don't append null values, creating the component shouldn't crash + // in the onCompleted handler, but generate type errors and warnings. + if (mode == TestType::IgnoreNullValues) { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* QML TestItem: List didn't append all objects$")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* TypeError: List doesn't append null objects$")); + } + QScopedPointer<QObject> object( component.create() ); + QVERIFY(object != nullptr); +} + +void tst_qqmllistreference::consoleLogSyntheticList() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("consoleLogSyntheticList.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QTest::ignoreMessage( + QtDebugMsg, QRegularExpression("\\[QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)\\]")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); +} + QTEST_MAIN(tst_qqmllistreference) #include "tst_qqmllistreference.moc" diff --git a/tests/auto/qml/qqmllocale/CMakeLists.txt b/tests/auto/qml/qqmllocale/CMakeLists.txt index 8603fe454d..beca01f955 100644 --- a/tests/auto/qml/qqmllocale/CMakeLists.txt +++ b/tests/auto/qml/qqmllocale/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmllocale Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllocale LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index 3349f5f9b6..a7ddf79ad5 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -1,9 +1,11 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> +#include <private/qqmllocale_p.h> + #include <QtQml/qqmlengine.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlcomponent.h> @@ -457,9 +459,10 @@ void tst_qqmllocale::toString_data() const QDateTime midnight2000(QDate(2000, 1, 1), QTime(0, 0)); // 12 AM might not exist in this timezone (some timezones have transitions at midnight). if (midnight2000.isValid()) { - functionCallScript = "locale.toString(new Date(2000, 1, 1))"; - QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript - << QLatin1String("Tuesday, 1 February 2000 12:00:00 AM ") + midnight2000.timeZoneAbbreviation() << QString(); + functionCallScript = "locale.toString(new Date(2000, 0, 1))"; + const QLocale locale("en_AU"); + QTest::newRow(qPrintable(functionCallScript)) << locale.name() << functionCallScript + << locale.toString(midnight2000, QLocale::LongFormat) << QString(); } functionCallScript = "locale.toString(new Date(2022, 7, 16), [])"; @@ -475,10 +478,12 @@ void tst_qqmllocale::toString_data() QTest::newRow(qPrintable(functionCallScript)) << "ar" << functionCallScript << "١٦" << QString(); functionCallScript = "locale.toString(new Date(2022, 7, 16), Locale.ShortFormat)"; - QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript << "16/8/22 12:00 AM" << QString(); + QTest::newRow(qPrintable(functionCallScript)) + << "en_AU" << functionCallScript << "16/8/22 12:00 am" << QString(); functionCallScript = "locale.toString(new Date(2022, 7, 16, 1, 23, 4), Locale.ShortFormat)"; - QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript << "16/8/22 1:23 AM" << QString(); + QTest::newRow(qPrintable(functionCallScript)) + << "en_AU" << functionCallScript << "16/8/22 1:23 am" << QString(); } void tst_qqmllocale::toString() @@ -544,7 +549,7 @@ void tst_qqmllocale::weekDays() Q_ARG(QVariant, QVariant(locale))); QVariant val = obj->property("weekDays"); - QCOMPARE(val.userType(), qMetaTypeId<QJSValue>()); + QCOMPARE(val.metaType(), QMetaType::fromType<QList<QQmlLocale::DayOfWeek>>()); QList<QVariant> qmlDays = val.toList(); QList<Qt::DayOfWeek> days = QLocale(locale).weekdays(); @@ -584,7 +589,7 @@ void tst_qqmllocale::uiLanguages() Q_ARG(QVariant, QVariant(locale))); QVariant val = obj->property("uiLanguages"); - QCOMPARE(val.userType(), qMetaTypeId<QJSValue>()); + QCOMPARE(val.metaType(), QMetaType::fromType<QStringList>()); QList<QVariant> qmlLangs = val.toList(); QStringList langs = QLocale(locale).uiLanguages(); @@ -763,7 +768,15 @@ void tst_qqmllocale::testFunctionCall() STOP_ON_FAILURE QVERIFY(evaluationResult.canConvert<QString>()); STOP_ON_FAILURE - QCOMPARE(evaluationResult.toString(), expectedResult); + + // We're not interested in whether the spaces in the date/time format + // are breaking or non-breaking. + const QString resultWithBreakingSpaces + = evaluationResult.toString().replace(u'\u202f', u' '); + const QString expectedWithBreakingSpaces + = expectedResult.replace(u'\u202f', u' '); + + QCOMPARE(resultWithBreakingSpaces, expectedWithBreakingSpaces); STOP_ON_FAILURE } else { QVERIFY(qmlExpression.hasError()); diff --git a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt index 9895bb04fb..6f77e030e6 100644 --- a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt +++ b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlmetaobject Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlmetaobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp index d945f460eb..0516140667 100644 --- a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp +++ b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtCore/QScopedPointer> diff --git a/tests/auto/qml/qqmlmetatype/CMakeLists.txt b/tests/auto/qml/qqmlmetatype/CMakeLists.txt index 7a5b47b571..2ab974ca91 100644 --- a/tests/auto/qml/qqmlmetatype/CMakeLists.txt +++ b/tests/auto/qml/qqmlmetatype/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlmetatype Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlmetatype LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlmetatype/data/Components/App.qml b/tests/auto/qml/qqmlmetatype/data/Components/App.qml index 57b2b4520a..693cffea40 100644 --- a/tests/auto/qml/qqmlmetatype/data/Components/App.qml +++ b/tests/auto/qml/qqmlmetatype/data/Components/App.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.0 diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index c1bac33d87..04c2a5bfdb 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qstandardpaths.h> #include <qtest.h> @@ -51,6 +51,9 @@ private slots: void enumsInRecursiveImport(); void revertValueTypeAnimation(); + + void clearPropertyCaches(); + void builtins(); }; class TestType : public QObject @@ -724,6 +727,115 @@ void tst_qqmlmetatype::revertValueTypeAnimation() QCOMPARE(o->property("pointSize").toDouble(), 12.0); } +void tst_qqmlmetatype::clearPropertyCaches() +{ + qmlClearTypeRegistrations(); + qmlRegisterType<TestType>("ClearPropertyCaches", 1, 0, "A"); + QQmlPropertyCache::ConstPtr oldCache = QQmlMetaType::propertyCache(&TestType::staticMetaObject); + QVERIFY(oldCache); + qmlClearTypeRegistrations(); + qmlRegisterType<TestType>("ClearPropertyCaches", 1, 0, "B"); + QQmlPropertyCache::ConstPtr newCache = QQmlMetaType::propertyCache(&TestType::staticMetaObject); + QVERIFY(oldCache.data() != newCache.data()); +} + +template<typename T> +void checkBuiltinBaseType() +{ + const QQmlType type = QQmlMetaType::qmlType(QMetaType::fromType<T>()); + QVERIFY(type.isValid()); + QCOMPARE(type.typeId(), QMetaType::fromType<T>()); + QCOMPARE(type.qListTypeId(), QMetaType::fromType<QList<T>>()); +} + +template<typename T> +void checkBuiltinListType() +{ + const QQmlType listType = QQmlMetaType::qmlListType(QMetaType::fromType<QList<T>>()); + QVERIFY(listType.isValid()); + QVERIFY(listType.isSequentialContainer()); + QCOMPARE(listType.typeId(), QMetaType::fromType<T>()); + QCOMPARE(listType.qListTypeId(), QMetaType::fromType<QList<T>>()); + QCOMPARE(listType.listMetaSequence().valueMetaType(), QMetaType::fromType<T>()); +} + +template<typename... T> +void checkBuiltinTypes() +{ + (checkBuiltinBaseType<T>(), ...); + (checkBuiltinListType<T>(), ...); +} + +template<typename T> +void checkNamedBuiltin(const QString &name) +{ + const QQmlType expected = QQmlMetaType::qmlType(QMetaType::fromType<T>()); + const QQmlType actual = QQmlMetaType::qmlType("QML/" + name, QTypeRevision::fromVersion(1, 0)); + if (actual != expected) { + qWarning() << Q_FUNC_INFO << "looking for" << name; + qWarning() << "found" << actual.module() << actual.elementName() << actual.version() + << actual.typeId(); + qWarning() << "expected" << expected.module() << expected.elementName() + << expected.version() << expected.typeId(); + QFAIL("mismatch"); + } +} + +template<typename T> +void checkObjectBuiltin(const QString &name) +{ + const QQmlType objectType = QQmlMetaType::qmlType(QMetaType::fromType<T *>()); + QVERIFY(objectType.isValid()); + QCOMPARE(objectType.typeId(), QMetaType::fromType<T *>()); + QCOMPARE(objectType.qListTypeId(), QMetaType::fromType<QQmlListProperty<T>>()); + + const QQmlType listType = QQmlMetaType::qmlListType(QMetaType::fromType<QQmlListProperty<T>>()); + QVERIFY(listType.isValid()); + QCOMPARE(listType.typeId(), QMetaType::fromType<T *>()); + QCOMPARE(listType.qListTypeId(), QMetaType::fromType<QQmlListProperty<T>>()); + + checkNamedBuiltin<T *>(name); +} + +void tst_qqmlmetatype::builtins() +{ + qmlClearTypeRegistrations(); + QQmlEngine engine; // registers the builtins + + checkBuiltinTypes< + QVariant, QJSValue, qint8, quint8, short, ushort, int, uint, qlonglong, qulonglong, float, + double, QChar, QString, bool, QDateTime, QDate, QTime, QUrl, QByteArray>(); + + checkNamedBuiltin<QVariant>("var"); + checkNamedBuiltin<QVariant>("variant"); + checkNamedBuiltin<int>("int"); + checkNamedBuiltin<double>("double"); + checkNamedBuiltin<double>("real"); + checkNamedBuiltin<QString>("string"); + checkNamedBuiltin<bool>("bool"); + checkNamedBuiltin<QDateTime>("date"); + checkNamedBuiltin<QUrl>("url"); + +#if QT_CONFIG(regularexpression) + checkBuiltinBaseType<QRegularExpression>(); + checkBuiltinListType<QRegularExpression>(); + checkNamedBuiltin<QRegularExpression>("regexp"); +#endif + + // Can't retrieve this one by metatype + const QQmlType voidType = QQmlMetaType::qmlType("QML/void", QTypeRevision::fromVersion(1, 0)); + QVERIFY(voidType.isValid()); + QCOMPARE(voidType.typeId(), QMetaType()); + QCOMPARE(voidType.qListTypeId(), QMetaType()); + + // No separate list types + checkBuiltinBaseType<std::nullptr_t>(); + checkBuiltinBaseType<QVariantMap>(); + + checkObjectBuiltin<QObject>("QtObject"); + checkObjectBuiltin<QQmlComponent>("Component"); +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt b/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt index 1d536afd24..4508ae3986 100644 --- a/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt +++ b/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlmoduleplugin Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlmoduleplugin LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt index ee68ebdfe0..56373ba3f4 100644 --- a/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt +++ b/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt @@ -1,2 +1,2 @@ 1:-1:a component declaration requires two or three arguments, but 4 were provided -2:-1:internal types require 2 arguments, but 3 were provided +2:-1:invalid version bar, expected <major>.<minor> diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp index b97fdf7cff..c60161f424 100644 --- a/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp index cecc074946..000a75375f 100644 --- a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp index 5922ca34f5..5ffd28dcbb 100644 --- a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp index 4400c11996..858b2b4704 100644 --- a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp index 8f20900838..00be65b599 100644 --- a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp index 9d8c110db6..519b567c1d 100644 --- a/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp index 9b54f3946a..7061c5a8c1 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp index eaa76e08d4..0ca3603267 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp index d5906611b7..c97824112e 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp index db158e6b6d..1ac6e8902f 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp index f1043b4b72..4dbc3c15bb 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp index 410d2c483a..3e5ae31d62 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp index 2142ac604d..dcf9ed676a 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp index c9cd28fc83..84b795d165 100644 --- a/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp index 698e5f2692..26d9977f7d 100644 --- a/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp index e49d1c1a28..c8620cbe5e 100644 --- a/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp index 96d01191fd..3e4987f9b1 100644 --- a/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp index 4383325d93..40eebfe3bb 100644 --- a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp index 3e851c1df3..644719cebf 100644 --- a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp index d8de79dcfd..590f10dced 100644 --- a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> #include <QDebug> diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp index ef55650a31..160f8f5ac6 100644 --- a/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp index d50de5242c..39f7f86acf 100644 --- a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QStringList> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index a4901aebad..67e1591b8b 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtest.h> #include <qdir.h> #include <QtQml/qqmlengine.h> @@ -16,7 +17,7 @@ #include <QtQuickShapes/private/qquickshapesglobal_p.h> -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) // For _PC_CASE_SENSITIVE #include <unistd.h> #endif @@ -130,7 +131,9 @@ void registerStaticPlugin(const char *uri) PluginType::metaData.append(char(QT_VERSION_MAJOR)); PluginType::metaData.append(char(QT_VERSION_MINOR)); PluginType::metaData.append(char(qPluginArchRequirements())); +#if QT_CONFIG(cborstreamwriter) PluginType::metaData.append(QCborValue(QCborMap::fromJsonObject(md)).toCbor()); +#endif auto rawMetaDataFunctor = []() -> QPluginMetaData { return {reinterpret_cast<const uchar *>(PluginType::metaData.constData()), size_t(PluginType::metaData.size())}; @@ -207,13 +210,13 @@ void tst_qqmlmoduleplugin::importsPlugin() QTest::ignoreMessage(QtWarningMsg, qPrintable(QString("import%1 worked").arg(suffix))); QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType' does not contain a module identifier directive - it cannot be protected from external registrations."); QQmlComponent component(&engine, testFileUrl(qmlFile)); - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(object->property("value").toInt(),123); - delete object; } void tst_qqmlmoduleplugin::importsPlugin_data() @@ -239,9 +242,9 @@ void tst_qqmlmoduleplugin::incorrectPluginCase() QString expectedError = QLatin1String("module \"org.qtproject.WrongCase\" plugin \"PluGin\" not found"); -#if defined(Q_OS_MAC) || defined(Q_OS_WIN32) +#if defined(Q_OS_DARWIN) || defined(Q_OS_WIN32) bool caseSensitive = true; -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) int res = pathconf(QDir::currentPath().toLatin1().constData(), _PC_CASE_SENSITIVE); if (res == -1) QSKIP("Could not establish case sensitivity of file system"); @@ -282,12 +285,12 @@ void tst_qqmlmoduleplugin::importPluginWithQmlFile() QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestPluginWithQmlFile' does not contain a module identifier directive - it cannot be protected from external registrations."); QQmlComponent component(&engine, testFileUrl(QStringLiteral("pluginWithQmlFile.qml"))); - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != nullptr); - delete object; + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); } void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl() @@ -300,12 +303,12 @@ void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl() component.setData(qml.toUtf8(), QUrl()); QTRY_COMPARE(component.status(), QQmlComponent::Ready); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QCOMPARE(object->property("width").toInt(), 300); - QVERIFY(object != nullptr); - delete object; + QVERIFY(object.get() != nullptr); - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); } @@ -323,12 +326,12 @@ void tst_qqmlmoduleplugin::remoteImportWithUnquotedUri() QTRY_COMPARE(component.status(), QQmlComponent::Ready); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(object->property("width").toInt(), 300); - delete object; - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); } @@ -368,20 +371,18 @@ void tst_qqmlmoduleplugin::importsMixedQmlCppPlugin() { QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.qml"))); - QObject *o = component.create(); - QVERIFY2(o != nullptr, msgComponentError(component, &engine)); + std::unique_ptr<QObject> o { component.create() }; + QVERIFY2(o.get() != nullptr, msgComponentError(component, &engine)); QCOMPARE(o->property("test").toBool(), true); - delete o; } { QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.2.qml"))); - QObject *o = component.create(); - QVERIFY2(o != nullptr, msgComponentError(component, &engine)); + std::unique_ptr<QObject> o { component.create() }; + QVERIFY2(o.get() != nullptr, msgComponentError(component, &engine)); QCOMPARE(o->property("test").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); - delete o; } @@ -444,9 +445,8 @@ void tst_qqmlmoduleplugin::implicitQmldir() QList<QQmlError> errors = component.errors(); VERIFY_ERRORS(errorFileName.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - QObject *obj = component.create(); - QVERIFY(!obj); - delete obj; + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(!obj.get()); } void tst_qqmlmoduleplugin::importsNested_data() @@ -481,17 +481,17 @@ void tst_qqmlmoduleplugin::importsNested() QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlNestedPluginType' does not contain a module identifier directive - it cannot be protected from external registrations."); QQmlComponent component(&engine, testFile(file)); - QObject *obj = component.create(); + std::unique_ptr<QObject> obj { component.create() }; if (errorFile.isEmpty()) { if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) qWarning() << "Unexpected Errors:" << component.errors(); - QVERIFY(obj); - delete obj; + QVERIFY(obj.get()); + obj.reset(); } else { QList<QQmlError> errors = component.errors(); VERIFY_ERRORS(errorFile.toLatin1().constData()); - QVERIFY(!obj); + QVERIFY(!obj.get()); } } @@ -684,13 +684,13 @@ void tst_qqmlmoduleplugin::importsChildPlugin() QTest::ignoreMessage(QtWarningMsg, "child import worked"); QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType.ChildPlugin' does not contain a module identifier directive - it cannot be protected from external registrations."); QQmlComponent component(&engine, testFileUrl(QStringLiteral("child.qml"))); - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(object->property("value").toInt(),123); - delete object; } void tst_qqmlmoduleplugin::importsChildPlugin2() @@ -701,13 +701,13 @@ void tst_qqmlmoduleplugin::importsChildPlugin2() QTest::ignoreMessage(QtWarningMsg, "child import2 worked"); QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType.ChildPlugin' does not contain a module identifier directive - it cannot be protected from external registrations."); QQmlComponent component(&engine, testFileUrl(QStringLiteral("child2.qml"))); - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(object->property("value").toInt(),123); - delete object; } void tst_qqmlmoduleplugin::importsChildPlugin21() @@ -718,13 +718,13 @@ void tst_qqmlmoduleplugin::importsChildPlugin21() QTest::ignoreMessage(QtWarningMsg, "child import2.1 worked"); QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType.ChildPlugin' does not contain a module identifier directive - it cannot be protected from external registrations."); QQmlComponent component(&engine, testFileUrl(QStringLiteral("child21.qml"))); - foreach (QQmlError err, component.errors()) + const auto errors = component.errors(); + for (const QQmlError &err : errors) qWarning() << err; VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(object->property("value").toInt(),123); - delete object; } void tst_qqmlmoduleplugin::parallelPluginImport() @@ -780,10 +780,9 @@ void tst_qqmlmoduleplugin::multiSingleton() engine.addImportPath(m_importsDirectory); QQmlComponent component(&engine, testFileUrl("multiSingleton.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object.get() != nullptr); QCOMPARE(obj.objectName(), QLatin1String("first")); - delete object; } void tst_qqmlmoduleplugin::optionalPlugin() diff --git a/tests/auto/qml/qqmlnotifier/CMakeLists.txt b/tests/auto/qml/qqmlnotifier/CMakeLists.txt index 44bd62ed9c..f91c1411e3 100644 --- a/tests/auto/qml/qqmlnotifier/CMakeLists.txt +++ b/tests/auto/qml/qqmlnotifier/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlnotifier Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlnotifier LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index e3171496d8..501aa472bc 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research In Motion -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> #include <QQmlEngine> diff --git a/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt b/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt index c54e195b26..aea2cae6dd 100644 --- a/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt +++ b/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlobjectmodel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlobjectmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlobjectmodel SOURCES tst_qqmlobjectmodel.cpp diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp index d007f4d024..95a05ebf44 100644 --- a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp +++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQmlModels/private/qqmlobjectmodel_p.h> #include <QtQmlModels/private/qqmlchangeset_p.h> #include <QtTest/qsignalspy.h> diff --git a/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt b/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt index f01d281ed3..b6ecc6bc0f 100644 --- a/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt +++ b/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlopenmetaobject Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlopenmetaobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmlopenmetaobject SOURCES tst_qqmlopenmetaobject.cpp diff --git a/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp b/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp index 2432282f8c..a3ccda0a1a 100644 --- a/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp +++ b/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <private/qqmlopenmetaobject_p.h> diff --git a/tests/auto/qml/qqmlparser/CMakeLists.txt b/tests/auto/qml/qqmlparser/CMakeLists.txt index 48e1a97f44..faa241bb5e 100644 --- a/tests/auto/qml/qqmlparser/CMakeLists.txt +++ b/tests/auto/qml/qqmlparser/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlparser Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlparser LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index adad96b864..62f67e548f 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <private/qqmljsengine_p.h> #include <private/qqmljsparser_p.h> @@ -34,6 +34,10 @@ private slots: void codeLocationsWithContinuationStringLiteral_data(); void noSubstitutionTemplateLiteral(); void templateLiteral(); + void numericSeparator_data(); + void numericSeparator(); + void invalidNumericSeparator_data(); + void invalidNumericSeparator(); void leadingSemicolonInClass(); void templatedReadonlyProperty(); void qmlImportInJS(); @@ -243,15 +247,13 @@ QStringList tst_qqmlparser::findFiles(const QDir &d) QStringList rv; - QStringList files = d.entryList(QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"), - QDir::Files); - foreach (const QString &file, files) { + const QStringList files = d.entryList( + QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"), QDir::Files); + for (const QString &file : files) rv << d.absoluteFilePath(file); - } - QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | - QDir::NoSymLinks); - foreach (const QString &dir, dirs) { + const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); + for (const QString &dir : dirs) { QDir sub = d; sub.cd(dir); rv << findFiles(sub); @@ -277,7 +279,7 @@ void tst_qqmlparser::qmlParser_data() files << findFiles(QDir(examples)); files << findFiles(QDir(tests)); - foreach (const QString &file, files) + for (const QString &file : std::as_const(files)) QTest::newRow(qPrintable(file)) << file; } #endif @@ -498,6 +500,74 @@ void tst_qqmlparser::templateLiteral() QVERIFY(e); } +void tst_qqmlparser::numericSeparator_data() { + QTest::addColumn<QString>("code"); + QTest::addColumn<double>("expected_value"); + + QTest::newRow("Separator in decimal literal") << "1_000_000_000" << 1000000000.0; + QTest::newRow("Separator in fractional part") << "1000.22_33" << 1000.2233; + QTest::newRow("Separator in exponent part") << "1e1_0_0" << std::pow(10, 100); + QTest::newRow("Separator in positive exponent part") << "1e+1_0_0" << 1e100; + QTest::newRow("Separator in negative exponent part") << "1e-1_0_0" << 1e-100; + QTest::newRow("Separator in binary literal with b prefix") << "0b1010_0001_1000_0101" << static_cast<double>(0b1010000110000101); + QTest::newRow("Separator in binary literal with B prefix") << "0B01_10_01_10" << static_cast<double>(0b01100110); + QTest::newRow("Separator in octal literal with o prefix") << "0o1234_5670" << static_cast<double>(012345670); + QTest::newRow("Separator in octal literal with O prefix") << "0O7777_0000" << static_cast<double>(077770000); + QTest::newRow("Separator in hex literal with x prefix") << "0xA0_B0_C0" << static_cast<double>(0xA0B0C0); + QTest::newRow("Separator in hex literal with X prefix") << "0X1000_AAAA" << static_cast<double>(0x1000AAAA); +} + +void tst_qqmlparser::numericSeparator() { + using namespace QQmlJS; + + QFETCH(QString, code); + QFETCH(double, expected_value); + + QQmlJS::Engine engine; + + QQmlJS::Lexer lexer(&engine); + lexer.setCode(code, 1); + + QQmlJS::Parser parser(&engine); + QVERIFY(parser.parseExpression()); + + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + + auto *literal = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expression); + QVERIFY(literal); + + QCOMPARE(literal->value, expected_value); + QCOMPARE(literal->firstSourceLocation().begin(), 0u); + QCOMPARE(literal->lastSourceLocation().end(), quint32(code.size())); +} + +void tst_qqmlparser::invalidNumericSeparator_data() { + QTest::addColumn<QString>("code"); + QTest::addColumn<QString>("error"); + + QTest::newRow("Trailing numeric separator") << "1_" << "A trailing numeric separator is not allowed in numeric literals"; + QTest::newRow("Multiple numeric separators") << "1__2" << "There can be at most one numeric separator between digits"; +} + +void tst_qqmlparser::invalidNumericSeparator() { + using namespace QQmlJS; + + QFETCH(QString, code); + QFETCH(QString, error); + + QQmlJS::Engine engine; + + QQmlJS::Lexer lexer(&engine); + lexer.setCode(code, 1); + + QQmlJS::Parser parser(&engine); + QVERIFY(!parser.parseExpression()); + + QVERIFY(lexer.errorCode() != Lexer::NoError); + QCOMPARE(lexer.errorMessage(), error); +} + void tst_qqmlparser::leadingSemicolonInClass() { QQmlJS::Engine engine; diff --git a/tests/auto/qml/qqmlpromise/CMakeLists.txt b/tests/auto/qml/qqmlpromise/CMakeLists.txt index b12da35bab..749963fc12 100644 --- a/tests/auto/qml/qqmlpromise/CMakeLists.txt +++ b/tests/auto/qml/qqmlpromise/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlpromise Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlpromise LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml b/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml index 18e141f787..9846777d97 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml b/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml index be6651a3c3..103263aca6 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml b/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml index 1345fbc6d7..8f0ce78006 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml index 380026c485..eef8065713 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml index e4bee826fa..b3be71243e 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml b/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml index 7088aa935c..d6c37bee1d 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml index c924b476d9..9a5f2d0331 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml index c7d9536bdb..7767aa49eb 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml index e494983ae6..f8e5a3c32c 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml index 541b884303..321506c483 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml index baafee07a1..800db1144c 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml index 806d276f51..22f066b2bd 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-get-length.qml b/tests/auto/qml/qqmlpromise/data/promise-get-length.qml index 96d48b42fb..cf0648025b 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-get-length.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-get-length.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml b/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml index 454930f06b..c6e9dce72b 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml index 84f85f19dc..76d344eb3b 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml index b537af12a3..847911cc9f 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml index 498c3fa778..d7d5fbd06e 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.4 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml b/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml index 5dc5abcb27..003c2ef102 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml index 6d09642842..30a08d5004 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml index 46700fe77e..88005b62e6 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml index 383c827741..1327d91e5f 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml index 7d31972633..f71000d0dd 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml index aefe25bfc1..1571b3a837 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml index 7ba5352431..62b70828fd 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml index 52842dde76..08cb4d62df 100644 --- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml b/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml index 07d607ca99..eaa5c89717 100644 --- a/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml +++ b/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml b/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml index 876a953b7e..be31f1a4f2 100644 --- a/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml +++ b/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml b/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml index 37c046d823..908149b1e6 100644 --- a/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml +++ b/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml b/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml index bb525fab7b..780e75660f 100644 --- a/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml +++ b/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml b/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml index 7075fc8a41..778bd085d8 100644 --- a/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml +++ b/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 QtObject { diff --git a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp index 2e00729dbc..c6c7767d44 100644 --- a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp +++ b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QString> #include <QtTest> #include <QQmlEngine> diff --git a/tests/auto/qml/qqmlproperty/CMakeLists.txt b/tests/auto/qml/qqmlproperty/CMakeLists.txt index 0d41159fb3..7148289318 100644 --- a/tests/auto/qml/qqmlproperty/CMakeLists.txt +++ b/tests/auto/qml/qqmlproperty/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlproperty Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlproperty LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml new file mode 100644 index 0000000000..bfa832c1c8 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml @@ -0,0 +1,50 @@ +import QtQml + +QtObject { + id: root + objectName: column.text + + property Component c: Component { + id: comp + QtObject { } + } + + property QtObject rectItem: null + + property bool running: false + + property Timer t: Timer { + id: column + interval: 200 + running: root.running + repeat: true + + property string text: { + let item = root.rectItem + let result = rectItem ? rectItem.objectName : "Create Object" + return result + } + + onTriggered: { + let rectItem = root.rectItem + + // If rectItem exists destory it. + if (rectItem) { + rectItem.destroy() + return + } + + // Otherwise create a new object + let newRectItem = comp.createObject(column, {}) + + + // Setting the objectName before setting root.rectItem seems to work. + // newRectItem.width = 1200 + root.rectItem = newRectItem + + // But setting the objectName after setting root.rectItem seems to + // cause the issue. + newRectItem.objectName = "1300" + } + } +} diff --git a/tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml b/tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml new file mode 100644 index 0000000000..8a2c68ab5d --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Item { + property int signalCounter: 0 + property list<QtObject> sourceList: [ QtObject{}, QtObject{}, QtObject{} ] + property list<QtObject> targetList1: sourceList + + onTargetList1Changed: signalCounter++ + + function assignList() { + targetList1 = sourceList + } +} diff --git a/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml b/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml new file mode 100644 index 0000000000..0ced54a9ca --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml @@ -0,0 +1,9 @@ +import QtQml + +QtObject { + id: root + property int onlineStatus + property Binding b: Binding { + root.onlineStatus: 12 + } +} diff --git a/tests/auto/qml/qqmlproperty/interfaces.h b/tests/auto/qml/qqmlproperty/interfaces.h index 7ca15214d4..e8f08d06fc 100644 --- a/tests/auto/qml/qqmlproperty/interfaces.h +++ b/tests/auto/qml/qqmlproperty/interfaces.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef INTERFACES_H #define INTERFACES_H diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 88c34f0e22..2635af705b 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "interfaces.h" #include <qtest.h> @@ -30,8 +30,15 @@ class MyQmlObject : public QObject { Q_OBJECT Q_PROPERTY(QPoint pointProperty MEMBER m_point) + Q_PROPERTY(QString a READ objectName NOTIFY somethingHappened) + Q_PROPERTY(QString b READ objectName NOTIFY somethingHappened) public: - MyQmlObject(QObject *parent = nullptr) : QObject(parent) {} + MyQmlObject(QObject *parent = nullptr) : QObject(parent) { + connect(this, &QObject::objectNameChanged, this, &MyQmlObject::somethingHappened); + } + +signals: + void somethingHappened(); private: QPoint m_point; @@ -217,6 +224,13 @@ private slots: void bindToNonQObjectTarget(); void assignVariantList(); + + void listAssignmentSignals(); + + void invalidateQPropertyChangeTriggers(); + + void propertyStartsWithOn(); + private: QQmlEngine engine; }; @@ -355,6 +369,7 @@ void tst_qqmlproperty::registeredCompositeTypeProperty() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); QScopedPointer<QObject> obj(component.create()); QVERIFY(obj); @@ -2416,9 +2431,13 @@ void tst_qqmlproperty::initFlags_data() const QString names[] = { QStringLiteral("foo"), + QStringLiteral("aChanged"), QStringLiteral("self.foo"), + QStringLiteral("self.aChanged"), QStringLiteral("onFoo"), + QStringLiteral("onAChanged"), QStringLiteral("self.onFoo"), + QStringLiteral("self.onAChanged"), QStringLiteral("bar"), QStringLiteral("self.bar"), QStringLiteral("abar"), @@ -2453,15 +2472,15 @@ void tst_qqmlproperty::initFlags() QQmlEngine engine; QQmlComponent c(&engine); c.setData(R"( - import QtQml - QtObject { + import Test + MyQmlObject { id: self signal foo() property int bar: 12 property alias abar: self.bar } )", QUrl()); - QVERIFY(c.isReady()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer<QObject> o(c.create()); QVERIFY(!o.isNull()); @@ -2471,7 +2490,9 @@ void tst_qqmlproperty::initFlags() passObject ? o.data() : nullptr, name, context, flags); const bool usesId = name.startsWith(QStringLiteral("self.")); - const bool hasSignal = name.endsWith(QStringLiteral("foo")); + const bool hasSignal = name.endsWith(QStringLiteral("foo")) + || name.endsWith(QStringLiteral("aChanged")); + if (!passObject && !usesId) { QVERIFY(!property.isValid()); } else if (passObject && usesId) { @@ -2486,10 +2507,14 @@ void tst_qqmlproperty::initFlags() QVERIFY(property.isProperty()); QCOMPARE(property.name(), usesId ? name.mid(strlen("self.")) : name); QCOMPARE(property.propertyMetaType(), QMetaType::fromType<int>()); - } else { + } else if (name.endsWith(QStringLiteral("oo"))) { // 'onFoo' or 'foo' QVERIFY(property.isSignalProperty()); QCOMPARE(property.name(), QStringLiteral("onFoo")); QVERIFY(!property.propertyMetaType().isValid()); + } else { + QVERIFY(property.isSignalProperty()); + QCOMPARE(property.name(), QStringLiteral("onSomethingHappened")); + QVERIFY(!property.propertyMetaType().isValid()); } } @@ -2544,6 +2569,58 @@ void tst_qqmlproperty::assignVariantList() QCOMPARE(holder->doubleList(), doubleList); } +void tst_qqmlproperty::listAssignmentSignals() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("listAssignmentSignals.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + + QCOMPARE(root->property("signalCounter").toInt(), 1); + QMetaObject::invokeMethod(root.get(), "assignList"); + QCOMPARE(root->property("signalCounter").toInt(), 2); +} + +void tst_qqmlproperty::invalidateQPropertyChangeTriggers() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("invalidateQPropertyChangeTriggers.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + + QStringList names; + QObject::connect(root.data(), &QObject::objectNameChanged, [&](const QString &name) { + if (names.length() == 10) + root->setProperty("running", false); + else + names.append(name); + }); + + root->setProperty("running", true); + QTRY_VERIFY(!root->property("running").toBool()); + + QCOMPARE(names, (QStringList { + u""_s, u"1300"_s, u"Create Object"_s, + u""_s, u"1300"_s, u"Create Object"_s, + u""_s, u"1300"_s, u"Create Object"_s, + u""_s + })); +} + +void tst_qqmlproperty::propertyStartsWithOn() +{ + QTest::failOnWarning("\"onlineStatus\" is not a properly capitalized signal handler name. " + "\"onLineStatus\" would be correct."); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("propertyStartsWithOn.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + QCOMPARE(root->property("onlineStatus").toInt(), 12); +} + QTEST_MAIN(tst_qqmlproperty) #include "tst_qqmlproperty.moc" diff --git a/tests/auto/qml/qqmlpropertycache/CMakeLists.txt b/tests/auto/qml/qqmlpropertycache/CMakeLists.txt index 8a7ffd2d9d..6dcb042dab 100644 --- a/tests/auto/qml/qqmlpropertycache/CMakeLists.txt +++ b/tests/auto/qml/qqmlpropertycache/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlpropertycache Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlpropertycache LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml b/tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml new file mode 100644 index 0000000000..2dd2cd8e21 --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml @@ -0,0 +1,64 @@ +import QtQuick 2.15 + +Item { + component First : Item { + Item { + id: a + } + + states: [ + State { + name: "test1" + + PropertyChanges { + a.enabled: false + } + } + ] + } + + component Second : Item { + QtObject { + id: a + property bool enabled: true + } + + states: [ + State { + name: "test2" + + PropertyChanges { + a.enabled: false + } + } + ] + + property Component cc: Item { + Item { id: a } + + states: [ + State { + name: "test3" + + PropertyChanges { + a.enabled: false + } + } + ] + } + } + + First { id: first } + Second { id: second } + property Item third: second.cc.createObject(); + + Component.onCompleted: { + console.log(1, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled); + first.state = "test1"; + console.log(2, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled); + second.state = "test2"; + console.log(3, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled); + third.state = "test3"; + console.log(4, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled); + } +} diff --git a/tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml b/tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml new file mode 100644 index 0000000000..c948d47a1b --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml @@ -0,0 +1,36 @@ +import QtQml +import Test.PropertyCache + +QtObject { + id: root + + property BaseObject obj: null + property Connections connection: Connections { + target: obj + function onPropertyAChanged() { ++root.a } + } + onObjChanged: { + connection.target = obj // Make sure this takes effect before sending the signal + obj.propertyAChanged() + } + + property BaseObject obj2: null + property Connections connection2: Connections { + target: obj2 + function onSignalA() { ++root.b } + } + onObj2Changed: { + connection2.target = obj2 // Make sure this takes effect before sending the signal + obj2.signalA(); + } + + property BaseObject theObj: BaseObject {} + Component.onCompleted: { + // Make sure the change signals are triggered also initially + obj = theObj; + obj2 = theObj; + } + + property int a: 0 + property int b: 0 +} diff --git a/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml new file mode 100644 index 0000000000..b8fee08978 --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml @@ -0,0 +1,8 @@ +import QtQml +import Test.PropertyCache + +BaseObject { + property int callCount: 0 + function signalA() { ++callCount } + Component.onCompleted: signalA() +} diff --git a/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml new file mode 100644 index 0000000000..cbf1cfe037 --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml @@ -0,0 +1,6 @@ +import QtQml +import Test.PropertyCache + +BaseObject { + signal propertyAChanged +} diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index cfea7c0da1..6af2a3e371 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <private/qqmlpropertycache_p.h> @@ -8,6 +8,7 @@ #include <QtQml/qqmlcomponent.h> #include <private/qmetaobjectbuilder_p.h> #include <private/qqmlcontextdata_p.h> +#include <private/qqmlpropertycachecreator_p.h> #include <QCryptographicHash> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -34,6 +35,8 @@ private slots: void derivedGadgetMethod(); void restrictRegistrationVersion(); void rejectOverriddenFinal(); + void overriddenSignals(); + void duplicateIdsAndGeneralizedGroupProperties(); private: QQmlEngine engine; @@ -164,6 +167,19 @@ Q_SIGNALS: void signalB(); }; +class OverriddenSignal : public BaseObject +{ + Q_OBJECT +public: + OverriddenSignal(QObject *parent = nullptr) : BaseObject(parent) {} + + Q_INVOKABLE void propertyAChanged() { ++propertyAChangedCalled; } + int propertyAChangedCalled = 0; + +Q_SIGNALS: + void signalA(); +}; + const QQmlPropertyData *cacheProperty(const QQmlPropertyCache::ConstPtr &cache, const char *name) { return cache->property(QLatin1String(name), nullptr, nullptr); @@ -707,4 +723,70 @@ void tst_qqmlpropertycache::rejectOverriddenFinal() QCOMPARE(o->property("c").toInt(), 0); } +void tst_qqmlpropertycache::overriddenSignals() +{ + qmlRegisterTypesAndRevisions<BaseObject>("Test.PropertyCache", 3); + QQmlEngine engine; + + QQmlComponent c1(&engine, testFileUrl("overriddenSignal.qml")); + QVERIFY2(!c1.isError(), qPrintable(c1.errorString())); + + QScopedPointer<QObject> o(c1.create()); + + // the propertyAChanged _signal_ is sent once (initially). + QCOMPARE(o->property("a").toInt(), 1); + + // signalA() is invoked once as signal, and the other time as method since both are C++. + QCOMPARE(o->property("b").toInt(), 1); + + OverriddenSignal *derived = new OverriddenSignal(o.data()); + + // Does call our overridden method, since that is defined in C++ + QCOMPARE(derived->propertyAChangedCalled, 0); + o->setProperty("obj", QVariant::fromValue(derived)); + QCOMPARE(derived->propertyAChangedCalled, 1); + + o->setProperty("obj2", QVariant::fromValue(derived)); + + // the propertyAChanged _signal_ is sent once (initially). + QCOMPARE(o->property("a").toInt(), 1); + + // We get to receive both signalA() signals since we only match by name. + QCOMPARE(o->property("b").toInt(), 2); + + // We shouldn't be allowed to define such things in QML, though. + + const QUrl c2Url = testFileUrl("qmlOverriddenSignal.qml"); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(c2Url.toString() + QLatin1String( + ":6:14: Duplicate method name: " + "invalid override of property change signal or superclass signal"))); + QQmlComponent c2(&engine, c2Url); + // Should be an error, but we can't enforce it yet. + + const QUrl c3Url = testFileUrl("qmlOverriddenSignal2.qml"); + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(c3Url.toString() + QLatin1String( + ":5:12: Duplicate signal name: " + "invalid override of property change signal or superclass signal"))); + QQmlComponent c3(&engine, c3Url); + // Should be an error, but we can't enforce it yet. +} + +void tst_qqmlpropertycache::duplicateIdsAndGeneralizedGroupProperties() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("duplicateIdsAndGeneralizedGroupProperties.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage(QtDebugMsg, "1 true true true"); + QTest::ignoreMessage(QtDebugMsg, "2 false true true"); + QTest::ignoreMessage(QtDebugMsg, "3 false false true"); + QTest::ignoreMessage(QtDebugMsg, "4 false false false"); + + QScopedPointer<QObject> o(c.create()); +} + QTEST_MAIN(tst_qqmlpropertycache) diff --git a/tests/auto/qml/qqmlpropertymap/CMakeLists.txt b/tests/auto/qml/qqmlpropertymap/CMakeLists.txt index bb314ba006..2a7bd742fa 100644 --- a/tests/auto/qml/qqmlpropertymap/CMakeLists.txt +++ b/tests/auto/qml/qqmlpropertymap/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlpropertymap Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlpropertymap LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp index 689202346c..2d91a8502d 100644 --- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp +++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQml/qqmlengine.h> @@ -346,9 +346,8 @@ void tst_QQmlPropertyMap::controlledWrite() component.setData(qmlSource, QUrl::fromLocalFile("")); QVERIFY(component.isReady()); - QObject *obj = component.create(); - QVERIFY(obj); - delete obj; + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get()); QCOMPARE(map.value(QLatin1String("key1")), QVariant("HELLO WORLD")); QCOMPARE(map.value(QLatin1String("key2")), QVariant("Goodbye")); @@ -364,8 +363,7 @@ void tst_QQmlPropertyMap::crashBug() QQmlComponent c(&engine); c.setData("import QtQuick 2.0\nBinding { target: map; property: \"myProp\"; value: 10 + 23 }",QUrl()); - QObject *obj = c.create(&context); - delete obj; + std::unique_ptr<QObject> obj { c.create(&context) }; } void tst_QQmlPropertyMap::QTBUG_17868() @@ -378,10 +376,9 @@ void tst_QQmlPropertyMap::QTBUG_17868() map.insert("key", 1); QQmlComponent c(&engine); c.setData("import QtQuick 2.0\nItem {property bool error:false; Component.onCompleted: {try{ map.keys(); }catch(e) {error=true;}}}",QUrl()); - QObject *obj = c.create(&context); - QVERIFY(obj); + std::unique_ptr<QObject> obj { c.create(&context) }; + QVERIFY(obj.get()); QVERIFY(!obj->property("error").toBool()); - delete obj; } @@ -434,17 +431,14 @@ void tst_QQmlPropertyMap::QTBUG_31226() " Timer { interval: 5; running: true; onTriggered: { myProp = qmlPropertyMap.greeting; } }\n" "}", QUrl()); - QObject *obj = c.create(&context); - QVERIFY(obj); + std::unique_ptr<QObject> obj { c.create(&context) }; + QVERIFY(obj.get()); QQmlPropertyMap *qmlPropertyMap = obj->findChild<QQmlPropertyMap*>(QString("qmlPropertyMap")); QVERIFY(qmlPropertyMap); qmlPropertyMap->insert("greeting", QString("Hello world!")); QTRY_COMPARE(obj->property("myProp").toString(), QString("Hello world!")); - - delete obj; - } void tst_QQmlPropertyMap::QTBUG_29836() @@ -461,13 +455,10 @@ void tst_QQmlPropertyMap::QTBUG_29836() " Timer { interval: 5; running: true; onTriggered: enhancedMap.testSlot() }\n" "}", QUrl()); - QObject *obj = c.create(&context); - QVERIFY(obj); + std::unique_ptr<QObject> obj { c.create(&context) }; + QVERIFY(obj.get()); QTRY_COMPARE(map.testSlotCalled(), true); - - delete obj; - } void tst_QQmlPropertyMap::QTBUG_35233() diff --git a/tests/auto/qml/qqmlqt/CMakeLists.txt b/tests/auto/qml/qqmlqt/CMakeLists.txt index f43c12d907..31f86d8877 100644 --- a/tests/auto/qml/qqmlqt/CMakeLists.txt +++ b/tests/auto/qml/qqmlqt/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlqt Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlqt LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlqt/data/qtbug_125495.qml b/tests/auto/qml/qqmlqt/data/qtbug_125495.qml new file mode 100644 index 0000000000..88f9cb259f --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/qtbug_125495.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property font fontProperty: Qt.font({ styleName: "Some Style" }); +} diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 5ba1f6f848..8139ec48d7 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <private/qqmlengine_p.h> @@ -27,7 +27,8 @@ #include <QTimeZone> #include <QtQuickTestUtils/private/qmlutils_p.h> -#include <private/qglobal_p.h> +#include <private/qtenvironmentvariables_p.h> // for qTzSet() +#include <private/qqmlengine_p.h> class tst_qqmlqt : public QQmlDataTest { @@ -56,8 +57,10 @@ private slots: void alpha(); void tint(); void color(); +#if QT_CONFIG(desktopservices) void openUrlExternally(); void openUrlExternally_pragmaLibrary(); +#endif void md5(); void createComponent(); void createComponent_pragmaLibrary(); @@ -82,6 +85,8 @@ private slots: void timeRoundtrip_data(); void timeRoundtrip(); + void fontSetsStyleName(); + private: QQmlEngine engine; }; @@ -612,6 +617,7 @@ public slots: void noteCall(const QUrl &url) { called++; last = url; } }; +#if QT_CONFIG(desktopservices) void tst_qqmlqt::openUrlExternally() { MyUrlHandler handler; @@ -658,6 +664,7 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary() QCOMPARE(handler.called,2); QCOMPARE(handler.last, htmlTestFile); } +#endif void tst_qqmlqt::md5() { @@ -805,7 +812,7 @@ void tst_qqmlqt::dateTimeFormatting() << component.url().toString() + ":40: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed." << component.url().toString() + ":43: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed."; - foreach (const QString &warning, warnings) + for (const QString &warning : std::as_const(warnings)) QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); warnings.clear(); @@ -820,7 +827,7 @@ void tst_qqmlqt::dateTimeFormatting() << "Could not convert argument 1 at" << "expression for err_dateTime2@"; - foreach (const QString &warning, warnings) + for (const QString &warning : std::as_const(warnings)) QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning)); QScopedPointer<QObject> object(component.createWithInitialProperties({ @@ -833,7 +840,7 @@ void tst_qqmlqt::dateTimeFormatting() QVERIFY(inputProperties.size() > 0); QVariant result; - foreach(const QString &prop, inputProperties) { + for (const QString &prop : std::as_const(inputProperties)) { QVERIFY(QMetaObject::invokeMethod(object.data(), method.toUtf8().constData(), Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, prop))); @@ -1280,12 +1287,13 @@ void tst_qqmlqt::later_data() void tst_qqmlqt::later() { + QQmlEngine engine; QFETCH(QString, function); QFETCH(QStringList, expectedWarnings); QFETCH(QStringList, propNames); QFETCH(QVariantList, values); - foreach (const QString &w, expectedWarnings) + for (const QString &w : std::as_const(expectedWarnings)) QTest::ignoreMessage(QtWarningMsg, qPrintable(w)); QQmlComponent component(&engine, testFileUrl("later.qml")); @@ -1300,7 +1308,7 @@ void tst_qqmlqt::later() QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } else if (propNames.at(i) == QLatin1String("collectGarbage")) { - engine.collectGarbage(); + gc(engine, GCFlags::DontSendPostedEvents); } else { QCOMPARE(root->property(qPrintable(propNames.at(i))), values.at(i)); } @@ -1463,6 +1471,18 @@ void tst_qqmlqt::timeRoundtrip() QCOMPARE(tp.m_getTime, tp.m_putTime); } +void tst_qqmlqt::fontSetsStyleName() { + QQmlComponent component(&engine, testFileUrl("qtbug_125495.qml")); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + + QFont f; + f.setStyleName("Some Style"); + + QCOMPARE(qvariant_cast<QFont>(object->property("fontProperty")), f); +} + QTEST_MAIN(tst_qqmlqt) #include "tst_qqmlqt.moc" diff --git a/tests/auto/qml/qqmlsettings/CMakeLists.txt b/tests/auto/qml/qqmlsettings/CMakeLists.txt index fab4502c65..a444324120 100644 --- a/tests/auto/qml/qqmlsettings/CMakeLists.txt +++ b/tests/auto/qml/qqmlsettings/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlsettings_labs Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlsettings_labs LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlsettings/data/aliases.qml b/tests/auto/qml/qqmlsettings/data/aliases.qml index 25ca431a6d..21bc558799 100644 --- a/tests/auto/qml/qqmlsettings/data/aliases.qml +++ b/tests/auto/qml/qqmlsettings/data/aliases.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.1 import QtQuick 2.2 import Qt.labs.settings 1.0 diff --git a/tests/auto/qml/qqmlsettings/data/basic.qml b/tests/auto/qml/qqmlsettings/data/basic.qml index ce02d65a87..64520b33f2 100644 --- a/tests/auto/qml/qqmlsettings/data/basic.qml +++ b/tests/auto/qml/qqmlsettings/data/basic.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import Qt.labs.settings 1.0 Settings { diff --git a/tests/auto/qml/qqmlsettings/data/categories.qml b/tests/auto/qml/qqmlsettings/data/categories.qml index c333ec17eb..a6407f9cb2 100644 --- a/tests/auto/qml/qqmlsettings/data/categories.qml +++ b/tests/auto/qml/qqmlsettings/data/categories.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import Qt.labs.settings 1.0 Settings { diff --git a/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml b/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml index 0ee95641c4..84f8923705 100644 --- a/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml +++ b/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.1 import QtQuick 2.2 import Qt.labs.settings 1.0 diff --git a/tests/auto/qml/qqmlsettings/data/siblings.qml b/tests/auto/qml/qqmlsettings/data/siblings.qml index 548a0869da..962cbc128b 100644 --- a/tests/auto/qml/qqmlsettings/data/siblings.qml +++ b/tests/auto/qml/qqmlsettings/data/siblings.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.2 import Qt.labs.settings 1.0 diff --git a/tests/auto/qml/qqmlsettings/data/types.qml b/tests/auto/qml/qqmlsettings/data/types.qml index fc2209375d..eec4bb709b 100644 --- a/tests/auto/qml/qqmlsettings/data/types.qml +++ b/tests/auto/qml/qqmlsettings/data/types.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.1 import QtQuick 2.2 import Qt.labs.settings 1.0 diff --git a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp index 88d8c58e2c..46ab2be3c9 100644 --- a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp +++ b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtCore/QCoreApplication> #include <QtCore/QScopedPointer> diff --git a/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt b/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt index 7537a92803..92809d0963 100644 --- a/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt +++ b/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlsqldatabase Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlsqldatabase LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml b/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml index fbd770740d..74226808f2 100644 --- a/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml +++ b/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.2 import QtQuick.LocalStorage 2.0 diff --git a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp index 67c44684a4..0269756225 100644 --- a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp +++ b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -164,9 +165,9 @@ void tst_qqmlsqldatabase::testQml_cleanopen() engine->collectGarbage(); - foreach (QString dbname, QSqlDatabase::connectionNames()) { + const QStringList connectionNames = QSqlDatabase::connectionNames(); + for (const QString &dbname : connectionNames) QSqlDatabase::removeDatabase(dbname); - } } void tst_qqmlsqldatabase::totalDatabases() diff --git a/tests/auto/qml/qqmltablemodel/CMakeLists.txt b/tests/auto/qml/qqmltablemodel/CMakeLists.txt index ebd3e94835..8edd052e3c 100644 --- a/tests/auto/qml/qqmltablemodel/CMakeLists.txt +++ b/tests/auto/qml/qqmltablemodel/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmltablemodel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmltablemodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml index a862af35d2..2b41837bac 100644 --- a/tests/auto/qml/qqmltablemodel/data/TestModel.qml +++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js index 83ac1b80a8..f6a5d4bb20 100644 --- a/tests/auto/qml/qqmltablemodel/data/TestUtils.js +++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only function testModelRoleDataProvider(index, role, cellData) { switch (role) { diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml index 7e9f46dd6f..1b3b8b5846 100644 --- a/tests/auto/qml/qqmltablemodel/data/common.qml +++ b/tests/auto/qml/qqmltablemodel/data/common.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.13 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/complex.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml index 9075a6a26f..41c69d44f4 100644 --- a/tests/auto/qml/qqmltablemodel/data/complex.qml +++ b/tests/auto/qml/qqmltablemodel/data/complex.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.13 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml index a1d9463665..dcdfd42a28 100644 --- a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml +++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml index 77c0021ee6..164a38b9d1 100644 --- a/tests/auto/qml/qqmltablemodel/data/empty.qml +++ b/tests/auto/qml/qqmltablemodel/data/empty.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml b/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml index c08525bc95..d20650f3be 100644 --- a/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml +++ b/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml index bb278ff9d1..60f502d4b1 100644 --- a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml +++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml index 04c76098dd..847a89667f 100644 --- a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml +++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml index 884427d24b..f5ece4bcf3 100644 --- a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml +++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 import Qt.labs.qmlmodels 1.0 diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp index cd6eb3e9c5..f2eec6e79c 100644 --- a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp +++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> diff --git a/tests/auto/qml/qqmltimer/CMakeLists.txt b/tests/auto/qml/qqmltimer/CMakeLists.txt index 1853a545d7..88d596be85 100644 --- a/tests/auto/qml/qqmltimer/CMakeLists.txt +++ b/tests/auto/qml/qqmltimer/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmltimer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmltimer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qqmltimer SOURCES tst_qqmltimer.cpp @@ -15,6 +21,7 @@ qt_internal_add_test(tst_qqmltimer Qt::Gui Qt::GuiPrivate Qt::QmlPrivate + Qt::QmlMetaPrivate Qt::QuickPrivate ) diff --git a/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp b/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp index 736907d5f0..495f7044f6 100644 --- a/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp +++ b/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp @@ -1,14 +1,18 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <QtTest/QSignalSpy> -#include <qtest.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlcomponent.h> -#include <QtQml/private/qqmltimer_p.h> -#include <QtQuick/qquickitem.h> -#include <QDebug> -#include <QtCore/QPauseAnimation> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <private/qabstractanimation_p.h> +#include <private/qqmltimer_p.h> + +#include <QtQuick/qquickitem.h> + +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlengine.h> + +#include <QtTest/qsignalspy.h> +#include <QtTest/qtest.h> + +#include <QtCore/qpauseanimation.h> void consistentWait(int ms) { @@ -69,7 +73,8 @@ void tst_qqmltimer::notRepeating() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); + std::unique_ptr<QObject> o { component.create() }; + QQmlTimer *timer = qobject_cast<QQmlTimer*>(o.get()); QVERIFY(timer != nullptr); QVERIFY(timer->isRunning()); QVERIFY(!timer->isRepeating()); @@ -91,12 +96,12 @@ void tst_qqmltimer::notRepeatingStart() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100 }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != nullptr); + std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) }; + QVERIFY(timer.get()); QVERIFY(!timer->isRunning()); TimerHelper helper; - connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); + connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout())); consistentWait(200); QCOMPARE(helper.count, 0); @@ -107,8 +112,6 @@ void tst_qqmltimer::notRepeatingStart() consistentWait(200); QCOMPARE(helper.count, 1); QVERIFY(!timer->isRunning()); - - delete timer; } void tst_qqmltimer::repeat() @@ -116,11 +119,11 @@ void tst_qqmltimer::repeat() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; repeat: true; running: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != nullptr); + std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) }; + QVERIFY(timer); TimerHelper helper; - connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); + connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout())); QCOMPARE(helper.count, 0); consistentWait(200); @@ -138,7 +141,7 @@ void tst_qqmltimer::repeat() QCOMPARE(helper.count, oldCount); QVERIFY(!timer->isRunning()); - QSignalSpy spy(timer, SIGNAL(repeatChanged())); + QSignalSpy spy(timer.get(), SIGNAL(repeatChanged())); timer->setRepeating(false); QVERIFY(!timer->isRepeating()); @@ -149,8 +152,6 @@ void tst_qqmltimer::repeat() timer->setRepeating(true); QCOMPARE(spy.size(),2); - - delete timer; } void tst_qqmltimer::triggeredOnStart() @@ -158,12 +159,12 @@ void tst_qqmltimer::triggeredOnStart() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != nullptr); + std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) }; + QVERIFY(timer); QVERIFY(timer->triggeredOnStart()); TimerHelper helper; - connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); + connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout())); consistentWait(1); QCOMPARE(helper.count, 1); consistentWait(200); @@ -172,7 +173,7 @@ void tst_qqmltimer::triggeredOnStart() QCOMPARE(helper.count, 2); QVERIFY(!timer->isRunning()); - QSignalSpy spy(timer, SIGNAL(triggeredOnStartChanged())); + QSignalSpy spy(timer.get(), SIGNAL(triggeredOnStartChanged())); timer->setTriggeredOnStart(false); QVERIFY(!timer->triggeredOnStart()); @@ -183,8 +184,6 @@ void tst_qqmltimer::triggeredOnStart() timer->setTriggeredOnStart(true); QCOMPARE(spy.size(),2); - - delete timer; } void tst_qqmltimer::triggeredOnStartRepeat() @@ -192,11 +191,11 @@ void tst_qqmltimer::triggeredOnStartRepeat() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true; repeat: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != nullptr); + std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) }; + QVERIFY(timer); TimerHelper helper; - connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); + connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout())); consistentWait(1); QCOMPARE(helper.count, 1); @@ -206,8 +205,6 @@ void tst_qqmltimer::triggeredOnStartRepeat() consistentWait(200); QVERIFY(helper.count > oldCount); QVERIFY(timer->isRunning()); - - delete timer; } void tst_qqmltimer::noTriggerIfNotRunning() @@ -221,12 +218,10 @@ void tst_qqmltimer::noTriggerIfNotRunning() "property Timer timer2: Timer { interval: 10; running: true; onTriggered: t1.running=false }" "}" ), QUrl::fromLocalFile("")); - QObject *item = component.create(); - QVERIFY(item != nullptr); + std::unique_ptr<QObject> item { component.create() }; + QVERIFY(item); consistentWait(200); QCOMPARE(item->property("ok").toBool(), true); - - delete item; } void tst_qqmltimer::changeDuration() @@ -234,11 +229,11 @@ void tst_qqmltimer::changeDuration() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; repeat: true; running: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != nullptr); + std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) }; + QVERIFY(timer); TimerHelper helper; - connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); + connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout())); QCOMPARE(helper.count, 0); consistentWait(500); @@ -250,7 +245,7 @@ void tst_qqmltimer::changeDuration() QCOMPARE(helper.count, 3); QVERIFY(timer->isRunning()); - QSignalSpy spy(timer, SIGNAL(intervalChanged())); + QSignalSpy spy(timer.get(), SIGNAL(intervalChanged())); timer->setInterval(200); QCOMPARE(timer->interval(), 200); @@ -261,8 +256,6 @@ void tst_qqmltimer::changeDuration() timer->setInterval(300); QCOMPARE(spy.size(),2); - - delete timer; } void tst_qqmltimer::restart() @@ -270,11 +263,11 @@ void tst_qqmltimer::restart() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 1000; repeat: true; running: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != nullptr); + std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) }; + QVERIFY(timer); TimerHelper helper; - connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); + connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout())); QCOMPARE(helper.count, 0); consistentWait(1200); @@ -290,8 +283,6 @@ void tst_qqmltimer::restart() QCOMPARE(helper.count, 2); QVERIFY(timer->isRunning()); - - delete timer; } void tst_qqmltimer::restartFromTriggered() @@ -356,14 +347,12 @@ void tst_qqmltimer::parentProperty() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQuick 2.0\nItem { Timer { objectName: \"timer\"; running: parent.visible } }"), QUrl::fromLocalFile("")); - QQuickItem *item = qobject_cast<QQuickItem*>(component.create()); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component.create()) }; + QVERIFY(item); QQmlTimer *timer = item->findChild<QQmlTimer*>("timer"); QVERIFY(timer != nullptr); QVERIFY(timer->isRunning()); - - delete timer; } void tst_qqmltimer::stopWhenEventPosted() @@ -371,7 +360,8 @@ void tst_qqmltimer::stopWhenEventPosted() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); + std::unique_ptr<QObject> o { component.create() }; + QQmlTimer *timer = qobject_cast<QQmlTimer*>(o.get()); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -395,7 +385,8 @@ void tst_qqmltimer::restartWhenEventPosted() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), QUrl::fromLocalFile("")); - QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); + std::unique_ptr<QObject> o { component.create() }; + QQmlTimer *timer = qobject_cast<QQmlTimer*>(o.get()); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); diff --git a/tests/auto/qml/qqmltranslation/CMakeLists.txt b/tests/auto/qml/qqmltranslation/CMakeLists.txt index 86198e7867..12d98b95a9 100644 --- a/tests/auto/qml/qqmltranslation/CMakeLists.txt +++ b/tests/auto/qml/qqmltranslation/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmltranslation Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmltranslation LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmltranslation/data/jstranslation.qml b/tests/auto/qml/qqmltranslation/data/jstranslation.qml index 7adde019fa..cca8bfa9e8 100644 --- a/tests/auto/qml/qqmltranslation/data/jstranslation.qml +++ b/tests/auto/qml/qqmltranslation/data/jstranslation.qml @@ -17,4 +17,5 @@ QtObject { property string singular2: Js.singular2() property string plural: Js.plural() property string plural2: Js.plural2() + property string emptyContext: Js.emptyContext() } diff --git a/tests/auto/qml/qqmltranslation/data/pragmacontext.qml b/tests/auto/qml/qqmltranslation/data/pragmacontext.qml new file mode 100644 index 0000000000..e158cfe4bf --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/pragmacontext.qml @@ -0,0 +1,8 @@ +import QtQml 2.12 + +pragma Translator: contextSetWithPragma + +QtObject { + property string german1: qsTr("English in translation") + property string german2: qsTranslate("setContext","English in translation") +} diff --git a/tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml b/tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml new file mode 100644 index 0000000000..31a3d0e009 --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml @@ -0,0 +1,8 @@ +import QtQml 2.12 + +pragma Translator: "contextSetWithPragmaStringLiteral" + +QtObject { + property string german1: qsTr("English in translation") + property string german2: qsTranslate("setContext","English in translation") +} diff --git a/tests/auto/qml/qqmltranslation/data/translation.js b/tests/auto/qml/qqmltranslation/data/translation.js index 11117fd36e..5269926604 100644 --- a/tests/auto/qml/qqmltranslation/data/translation.js +++ b/tests/auto/qml/qqmltranslation/data/translation.js @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only function basic() { return qsTr("hello") @@ -58,3 +58,7 @@ function plural2() { return qsTr("%n duck(s)", "", 2) return ""; } + +function emptyContext() { + return qsTranslate("", "hello") +} diff --git a/tests/auto/qml/qqmltranslation/data/translation.qml b/tests/auto/qml/qqmltranslation/data/translation.qml index b96471c9dd..127b5cb5b4 100644 --- a/tests/auto/qml/qqmltranslation/data/translation.qml +++ b/tests/auto/qml/qqmltranslation/data/translation.qml @@ -1,8 +1,8 @@ import QtQuick 2.0 - +// The translation data starts at line == 4. QCoreApplication::translate() tolerates this. QtObject { objectName: "test" - + property string emptyContext: qsTranslate("", "hello") property string basic: qsTr("hello") property string basic2: qsTranslate("CustomContext", "goodbye") property string basic3: if (objectName.length > 0) qsTr("hello") diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index f78bd3ac76..69dbd6179b 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -24,6 +24,8 @@ private slots: void idTranslation(); void translationChange(); void preferJSContext(); + void pragmaContext(); + void pragmaContextStringLiteral(); void listModel(); }; @@ -50,11 +52,11 @@ void tst_qqmltranslation::translation() QQmlEngine engine; QQmlComponent component(&engine, testFile); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); if (verifyCompiledData) { - QQmlContext *context = qmlContext(object); + QQmlContext *context = qmlContext(object.get()); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); QQmlRefPointer<QQmlTypeData> typeData = engine->typeLoader.getType(context->baseUrl()); QVERIFY(!typeData->backupSourceCode().isValid()); @@ -66,7 +68,9 @@ void tst_qqmltranslation::translation() << QStringLiteral("basic2") << QStringLiteral("disambiguation") << QStringLiteral("disambiguation2") - << QStringLiteral("singular") << QStringLiteral("plural"); + << QStringLiteral("singular") + << QStringLiteral("plural") + << QStringLiteral("emptyContext"); const QV4::CompiledData::Object *rootObject = compilationUnit->qmlData->objectAt(/*root object*/0); @@ -100,9 +104,9 @@ void tst_qqmltranslation::translation() QCOMPARE(object->property("singular2").toString(), QLatin1String("1 canard")); QCOMPARE(object->property("plural").toString(), QLatin1String("2 canards")); QCOMPARE(object->property("plural2").toString(), QLatin1String("2 canards")); + QCOMPARE(object->property("emptyContext").toString(), QLatin1String("hello")); QCoreApplication::removeTranslator(&translator); - delete object; } void tst_qqmltranslation::idTranslation() @@ -113,11 +117,11 @@ void tst_qqmltranslation::idTranslation() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("idtranslation.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); { - QQmlContext *context = qmlContext(object); + QQmlContext *context = qmlContext(object.get()); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); QQmlRefPointer<QQmlTypeData> typeData = engine->typeLoader.getType(context->baseUrl()); QVERIFY(!typeData->backupSourceCode().isValid()); @@ -144,7 +148,6 @@ void tst_qqmltranslation::idTranslation() QCOMPARE(object->property("idTranslation3").toString(), QLatin1String("bonjour tout le monde")); QCoreApplication::removeTranslator(&translator); - delete object; } class CppTranslationBase : public QQuickItem @@ -174,6 +177,12 @@ class DummyTranslator : public QTranslator return QString::fromUtf8("Deutsch in mylibrary"); if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation")) return QString::fromUtf8("Deutsch in Setzung"); + if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragma")) + return QString::fromUtf8("Deutsch in Setzung pragma"); + if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragmaStringLiteral")) + return QString::fromUtf8("Deutsch in Setzung pragma string literal"); + if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "setContext")) + return QString::fromUtf8("Deutsch in Setzung set"); if (!qstrcmp(sourceText, "soup")) return QString::fromUtf8("Suppe"); if (!qstrcmp(sourceText, "fish")) @@ -245,6 +254,42 @@ void tst_qqmltranslation::preferJSContext() QCoreApplication::removeTranslator(&translator); } +void tst_qqmltranslation::pragmaContext() +{ + DummyTranslator translator; + QCoreApplication::installTranslator(&translator); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("pragmacontext.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("german1").toString(), + QStringLiteral("Deutsch in Setzung pragma")); + QCOMPARE(object->property("german2").toString(), + QStringLiteral("Deutsch in Setzung set")); + + QCoreApplication::removeTranslator(&translator); +} + +void tst_qqmltranslation::pragmaContextStringLiteral() +{ + DummyTranslator translator; + QCoreApplication::installTranslator(&translator); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("pragmacontextstringliteral.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("german1").toString(), + QStringLiteral("Deutsch in Setzung pragma string literal")); + QCOMPARE(object->property("german2").toString(), + QStringLiteral("Deutsch in Setzung set")); + + QCoreApplication::removeTranslator(&translator); +} + void tst_qqmltranslation::listModel() { QQmlEngine engine; diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt b/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt index 7c55146ddc..f260ca8287 100644 --- a/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt +++ b/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qqmltreemodeltotablemodel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmltreemodeltotablemodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp index 58587da79c..7cf077f265 100644 --- a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp +++ b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testmodel.h" @@ -129,3 +129,31 @@ bool TestModel::insertRows(int position, int rows, const QModelIndex &parent) endInsertRows(); return true; } + +void insertColumnsRecursive(TreeItem *item, int cols) +{ + int pos = item->m_entries.size(); + for (int col = 0; col < cols; col++) + item->m_entries << QVariant(QString("%1, %2 (inserted)").arg(pos + col).arg(col)); + for (auto child : item->m_childItems) + insertColumnsRecursive(child, cols); +} + +bool TestModel::insertColumns(int position, int cols, const QModelIndex &parent) +{ + if (!parent.isValid()) { + qWarning() << "Cannot insert columns on an invalid parent!"; + return false; + } + + beginInsertColumns(parent, position, position + cols - 1); + TreeItem *parentItem = treeItem(parent); + + TreeItem *item = m_rootItem.data(); + + insertColumnsRecursive(item, cols); + m_columnCount += cols; + + endInsertColumns(); + return true; +} diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h index f886c56d4a..c67410d5f1 100644 --- a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h +++ b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTMODEL_H #define TESTMODEL_H @@ -38,6 +38,7 @@ public: QModelIndex parent(const QModelIndex &index) const override; bool insertRows(int position, int rows, const QModelIndex &parent) override; + bool insertColumns(int position, int rows, const QModelIndex &parent) override; private: QScopedPointer<TreeItem> m_rootItem; diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp b/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp index cd9aed4b30..47bbf1440b 100644 --- a/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp +++ b/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/qtest.h> #include <QAbstractItemModelTester> diff --git a/tests/auto/qml/qqmltypeloader/CMakeLists.txt b/tests/auto/qml/qqmltypeloader/CMakeLists.txt index d20449579e..1bf4da34f9 100644 --- a/tests/auto/qml/qqmltypeloader/CMakeLists.txt +++ b/tests/auto/qml/qqmltypeloader/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmltypeloader Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmltypeloader LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp index e0ff775cb5..b1726f190f 100644 --- a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp +++ b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "plugin.h" diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h index dfaafe278f..2a5a1b2a4e 100644 --- a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h +++ b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SLOW_PLUGIN_H #define SLOW_PLUGIN_H diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp b/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp index 910f3eaeb6..c17da49e93 100644 --- a/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp +++ b/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "slow.h" diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/slow.h b/tests/auto/qml/qqmltypeloader/SlowImport/slow.h index e9f6341e5a..c3ec73bc53 100644 --- a/tests/auto/qml/qqmltypeloader/SlowImport/slow.h +++ b/tests/auto/qml/qqmltypeloader/SlowImport/slow.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SLOWSTUFF_H #define SLOWSTUFF_H diff --git a/tests/auto/qml/qqmltypeloader/data/GenericView.qml b/tests/auto/qml/qqmltypeloader/data/GenericView.qml index 2a708d425d..0f90ef26da 100644 --- a/tests/auto/qml/qqmltypeloader/data/GenericView.qml +++ b/tests/auto/qml/qqmltypeloader/data/GenericView.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import Slow 1.0 diff --git a/tests/auto/qml/qqmltypeloader/data/Intercept.qml b/tests/auto/qml/qqmltypeloader/data/Intercept.qml index 57290832ea..d0de7e04ec 100644 --- a/tests/auto/qml/qqmltypeloader/data/Intercept.qml +++ b/tests/auto/qml/qqmltypeloader/data/Intercept.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 import Fast 1.0 diff --git a/tests/auto/qml/qqmltypeloader/data/NiceView.qml b/tests/auto/qml/qqmltypeloader/data/NiceView.qml index cb73650551..4f1782000a 100644 --- a/tests/auto/qml/qqmltypeloader/data/NiceView.qml +++ b/tests/auto/qml/qqmltypeloader/data/NiceView.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmltypeloader/data/doesExist.qml b/tests/auto/qml/qqmltypeloader/data/doesExist.qml new file mode 100644 index 0000000000..54531c4bdc --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/doesExist.qml @@ -0,0 +1,2 @@ +import QtQml +QtObject {} diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml new file mode 100644 index 0000000000..8fc36a40da --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml @@ -0,0 +1,3 @@ +import QtQml + +QtObject {} diff --git a/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml b/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml index 10e1784ff1..c22a880fbd 100644 --- a/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml +++ b/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.2 diff --git a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml index cd6a726f9b..930d481f7b 100644 --- a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml +++ b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml b/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml index 6cba31c434..95439b8308 100644 --- a/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml +++ b/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmltypeloader/declarativetesttype.h b/tests/auto/qml/qqmltypeloader/declarativetesttype.h index 5a33f25be5..b767342951 100644 --- a/tests/auto/qml/qqmltypeloader/declarativetesttype.h +++ b/tests/auto/qml/qqmltypeloader/declarativetesttype.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DECLARATIVETESTTYPE_H #define DECLARATIVETESTTYPE_H diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 89e208f441..7828a869e1 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQml/qqmlengine.h> @@ -38,6 +38,7 @@ private slots: void redirect(); void qmlSingletonWithinModule(); void multiSingletonModule(); + void multiSingletonModuleNoWarning(); void implicitComponentModule(); void customDiskCachePath(); void qrcRootPathUrl(); @@ -47,6 +48,7 @@ private slots: void circularDependency(); void declarativeCppAndQmlDir(); void signalHandlersAreCompatible(); + void loadTypeOnShutdown(); private: void checkSingleton(const QString & dataDirectory); @@ -62,19 +64,18 @@ void tst_QQMLTypeLoader::testLoadComplete() #ifdef Q_OS_ANDROID QSKIP("Loading dynamic plugins does not work on Android"); #endif - QQuickView *window = new QQuickView(); + std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>(); window->engine()->addImportPath(QT_TESTCASE_BUILDDIR); qDebug() << window->engine()->importPathList(); window->setGeometry(0,0,240,320); window->setSource(testFileUrl("test_load_complete.qml")); window->show(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowExposed(window.get())); QObject *rootObject = window->rootObject(); QTRY_VERIFY(rootObject != nullptr); QTRY_COMPARE(rootObject->property("created").toInt(), 2); QTRY_COMPARE(rootObject->property("loaded").toInt(), 2); - delete window; } void tst_QQMLTypeLoader::loadComponentSynchronously() @@ -92,7 +93,7 @@ void tst_QQMLTypeLoader::trimCache() QQmlEngine engine; QQmlTypeLoader &loader = QQmlEnginePrivate::get(&engine)->typeLoader; QVector<QQmlTypeData *> releaseLater; - QVector<QV4::ExecutableCompilationUnit *> releaseCompilationUnitLater; + QVector<QV4::CompiledData::CompilationUnit *> releaseCompilationUnitLater; for (int i = 0; i < 256; ++i) { QUrl url = testFileUrl("trim_cache.qml"); url.setQuery(QString::number(i)); @@ -534,6 +535,18 @@ void tst_QQMLTypeLoader::multiSingletonModule() checkCleanCacheLoad(QLatin1String("multiSingletonModule")); } +void tst_QQMLTypeLoader::multiSingletonModuleNoWarning() +{ + // Should not warn about a "cyclic" dependency between the singletons + QTest::failOnWarning(QRegularExpression(".*")); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("imports/multisingletonmodule/a.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); +} + void tst_QQMLTypeLoader::implicitComponentModule() { #ifdef Q_OS_ANDROID @@ -673,7 +686,7 @@ static void getCompilationUnitAndRuntimeInfo(QQmlRefPointer<QV4::ExecutableCompi QVERIFY(!typeData->isError()); // this returns } - unit = typeData->compilationUnit(); + unit = engine->handle()->executableCompilationUnit(typeData->compilationUnit()); QVERIFY(unit); // the QmlIR::Document is deleted once loader.getType() is complete, so @@ -724,6 +737,49 @@ void tst_QQMLTypeLoader::signalHandlersAreCompatible() QVERIFY(unitFromCachegen->url() != unitFromTypeCompiler->url()); } +void tst_QQMLTypeLoader::loadTypeOnShutdown() +{ + bool dead1 = false; + bool dead2 = false; + + { + QQmlEngine engine; + auto good = new QQmlComponent( + &engine, testFileUrl("doesExist.qml"), + QQmlComponent::CompilationMode::Asynchronous, &engine); + QObject::connect( + good, &QQmlComponent::statusChanged, &engine, + [&](QQmlComponent::Status) { + + // Must not call this if the engine is already dead. + QVERIFY(engine.rootContext()); + + }); + + QObject::connect(good, &QQmlComponent::destroyed, good, [&]() { dead1 = true; }); + QVERIFY(good->isLoading()); + + auto bad = new QQmlComponent( + &engine, testFileUrl("doesNotExist.qml"), + QQmlComponent::CompilationMode::Asynchronous, &engine); + QObject::connect( + bad, &QQmlComponent::statusChanged, &engine, + [&](QQmlComponent::Status) { + + // Must not call this if the engine is already dead. + // Must also not leak memory from the events the error produces. + QVERIFY(engine.rootContext()); + + }); + + QObject::connect(bad, &QQmlComponent::destroyed, bad, [&]() { dead2 = true; }); + QVERIFY(bad->isLoading()); + } + + QVERIFY(dead1); + QVERIFY(dead2); +} + QTEST_MAIN(tst_QQMLTypeLoader) #include "tst_qqmltypeloader.moc" diff --git a/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt b/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt index 156022bac1..93460d72a9 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt +++ b/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlvaluetypeproviders Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlvaluetypeproviders LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml index 95a588d1bd..6081d460b8 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml +++ b/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml index d6707e37cd..e2c39d2d72 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml +++ b/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml @@ -3,11 +3,16 @@ import Test MyTypeObject { property list<structured> l: [{i : 21}, {c: 22}, {p: {x: 199, y: 222}}] + property int aa: 5 Component.onCompleted: { l[2].i = 4 l[1].p.x = 88 l[0].sizes[1].width = 19 structured.p.x = 76 + + var sizesDetached = l[0].sizesDetached(); + sizesDetached[1].width = 12; + aa = sizesDetached[1].width; } } diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml index 6a42a4f1a8..abd6f6af08 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml +++ b/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml @@ -40,7 +40,22 @@ MyTypeObject { r2 = {x: 9, y: 10, width: 11, height: 12}; c3 = 99; b2 = {i: 11, c: 15, p: {x: 4} } - + acceptConstructible(object) c4 = {foo: 11}; } + + barren: ({i: 17}) + function changeBarren() { barren = "foos" } + + property QtObject object: QtObject {} + property constructible fromObject: object + property int listResult: acceptConstructibles([Qt.resolvedUrl("foo/bar.baz"), fromObject, 12]) + + property var insanity: ({}) + property structured fromInsanity: acceptStructured(insanity) + + property rect newItemPadding + function updatePadding() { + setEffectPadding(newItemPadding); + } } diff --git a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp index 1746957872..fd85c117e1 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp @@ -1,11 +1,16 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testtypes.h" +QList<Padding::LogEntry> Padding::log; + void registerTypes() { qmlRegisterType<MyTypeObject>("Test", 1, 0, "MyTypeObject"); qmlRegisterTypesAndRevisions<ConstructibleValueType>("Test", 1); qmlRegisterTypesAndRevisions<ConstructibleFromQReal>("Test", 1); qmlRegisterTypesAndRevisions<StructuredValueType>("Test", 1); + qmlRegisterTypesAndRevisions<ForeignAnonymousStructuredValueType>("Test", 1); + qmlRegisterTypesAndRevisions<Padding>("Test", 1); + qmlRegisterTypesAndRevisions<MyItem>("Test", 1); } diff --git a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h index ca1d1b2b7a..8130ea2912 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H @@ -33,6 +33,8 @@ struct ConstructibleValueType public: ConstructibleValueType() = default; Q_INVOKABLE ConstructibleValueType(int foo) : m_foo(foo) {} + Q_INVOKABLE ConstructibleValueType(QObject *) : m_foo(67) {} + Q_INVOKABLE ConstructibleValueType(const QUrl &) : m_foo(68) {} int foo() const { return m_foo; } @@ -96,6 +98,8 @@ public: const QList<QSizeF> &sizes() const { return m_sizes; } void setSizes(const QList<QSizeF> &sizes) { m_sizes = sizes; } + Q_INVOKABLE QList<QSizeF> sizesDetached() const { return m_sizes; } + private: friend bool operator==(const StructuredValueType &a, const StructuredValueType &b) @@ -109,6 +113,35 @@ private: QList<QSizeF> m_sizes = { QSizeF(1, 1), QSizeF(2, 2) }; }; +struct BarrenValueType +{ + Q_GADGET + Q_PROPERTY(int i READ i WRITE setI) + +public: + BarrenValueType() = default; + Q_INVOKABLE BarrenValueType(const QString &) : m_i(25) {} + + int i() const { return m_i; } + void setI(int newI) { m_i = newI; } + +private: + friend bool operator==(const BarrenValueType &a, const BarrenValueType &b) + { + return a.m_i == b.m_i; + } + + int m_i = 0; +}; + +struct ForeignAnonymousStructuredValueType +{ + Q_GADGET + QML_ANONYMOUS + QML_FOREIGN(BarrenValueType) + QML_STRUCTURED_VALUE +}; + class MyTypeObject : public QObject { Q_OBJECT @@ -133,6 +166,7 @@ class MyTypeObject : public QObject Q_PROPERTY(QVariant variant READ variant NOTIFY changed) Q_PROPERTY(ConstructibleValueType constructible READ constructible WRITE setConstructible NOTIFY constructibleChanged) Q_PROPERTY(StructuredValueType structured READ structured WRITE setStructured NOTIFY structuredChanged) + Q_PROPERTY(BarrenValueType barren READ barren WRITE setBarren NOTIFY barrenChanged) Q_PROPERTY(QDateTime aDateTime READ aDateTime WRITE setADateTime NOTIFY aDateTimeChanged) Q_PROPERTY(QDate aDate READ aDate WRITE setADate NOTIFY aDateChanged) @@ -275,7 +309,7 @@ public: { return m_aDate; } - void setADate(const QDate &newADate) + void setADate(QDate newADate) { if (m_aDate == newADate) return; @@ -287,7 +321,7 @@ public: { return m_aTime; } - void setATime(const QTime &newATime) + void setATime(QTime newATime) { if (m_aTime == newATime) return; @@ -307,6 +341,47 @@ public: emit aVariantChanged(); } + BarrenValueType barren() const + { + return m_barren; + } + + void setBarren(const BarrenValueType &newBarren) + { + if (m_barren == newBarren) + return; + m_barren = newBarren; + emit barrenChanged(); + } + + Q_INVOKABLE void acceptConstructible(const ConstructibleValueType &a) + { + setAVariant(QVariant::fromValue(a)); + } + + Q_INVOKABLE int acceptConstructibles(const QList<ConstructibleValueType> &constructibles) + { + int result = 0; + for (const auto &c: constructibles) { + result += c.foo(); + } + return result; + } + + Q_INVOKABLE StructuredValueType acceptStructured(const StructuredValueType &a) + { + return a; + } + + Q_INVOKABLE void setEffectPadding(const QRect &r) + { + m_hasEffectPadding = true; + m_effectPadding = r; + } + + bool hasEffectPadding() const { return m_hasEffectPadding; } + QRectF effectPadding() const { return m_effectPadding; } + signals: void changed(); void runScript(); @@ -319,6 +394,8 @@ signals: void aTimeChanged(); void aVariantChanged(); + void barrenChanged(); + public slots: QSize method() { return QSize(13, 14); } private: @@ -329,6 +406,139 @@ private: QDate m_aDate; QTime m_aTime; QVariant m_aVariant; + BarrenValueType m_barren; + QRectF m_effectPadding; + bool m_hasEffectPadding = false; +}; + +class Padding +{ + Q_GADGET + + Q_PROPERTY(int left READ left WRITE setLeft) + Q_PROPERTY(int right READ right WRITE setRight) + + QML_VALUE_TYPE(padding) + QML_STRUCTURED_VALUE + +public: + enum LogType { + DefaultCtor, + CopyCtor, + MoveCtor, + CopyAssign, + MoveAssign, + InvokableCtor, + CustomCtor, + Invalid, + }; + + Q_ENUM(LogType); + + struct LogEntry { + LogType type = Invalid; + int left = 0; + int right = 0; + + friend QDebug operator<<(QDebug &debug, const LogEntry &self) + { + return debug << self.type << " " << self.left << " " << self.right; + } + }; + + static QList<LogEntry> log; + + void doLog(LogType type) { + log.append({ + type, m_left, m_right + }); + } + + Padding() + { + doLog(DefaultCtor); + } + + Padding(const Padding &other) + : m_left(other.m_left) + , m_right(other.m_right) + { + doLog(CopyCtor); + } + + Padding(Padding &&other) + : m_left(other.m_left) + , m_right(other.m_right) + { + doLog(MoveCtor); + } + + Padding(int left, int right) + : m_left( left ) + , m_right( right ) + { + doLog(CustomCtor); + } + + Padding &operator=(const Padding &other) { + if (this != &other) { + m_left = other.m_left; + m_right = other.m_right; + } + doLog(CopyAssign); + return *this; + } + + Padding &operator=(Padding &&other) { + if (this != &other) { + m_left = other.m_left; + m_right = other.m_right; + } + doLog(MoveAssign); + return *this; + } + + Q_INVOKABLE Padding(int padding ) + : m_left( padding ) + , m_right( padding ) + { + doLog(InvokableCtor); + } + + void setLeft(int padding) { m_left = padding; } + int left() const { return m_left; } + + void setRight(int padding) { m_right = padding; } + int right() const { return m_right; } + +private: + int m_left = 0; + int m_right = 0; +}; + +class MyItem : public QObject +{ + Q_OBJECT + Q_PROPERTY(Padding padding READ padding WRITE setPadding NOTIFY paddingChanged) + QML_ELEMENT + +public: + void setPadding(const Padding &padding) + { + if (padding.left() == m_padding.left() && padding.right() == m_padding.right()) + return; + + m_padding = padding; + emit paddingChanged(); + } + + const Padding &padding() const{ return m_padding; } + +signals: + void paddingChanged(); + +private: + Padding m_padding{ 17, 17 }; }; void registerTypes(); diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp index 5a36c6edd3..da7e14411d 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -41,6 +41,7 @@ private slots: void structured(); void recursive(); void date(); + void constructors(); }; void tst_qqmlvaluetypeproviders::initTestCase() @@ -253,7 +254,6 @@ void tst_qqmlvaluetypeproviders::structured() " with value [object Object]", "Could not find any constructor for value type ConstructibleValueType to call" " with value QVariant(QJSValue, )", - "Could not convert QLocale(English, Latin, Australia) to double for property y", "Could not find any constructor for value type ConstructibleValueType to call" " with value [object Object]", "Could not find any constructor for value type ConstructibleValueType to call" @@ -292,8 +292,16 @@ void tst_qqmlvaluetypeproviders::structured() QCOMPARE(o->property("c3").value<ConstructibleValueType>(), ConstructibleValueType(99)); QCOMPARE(o->property("c4").value<ConstructibleValueType>(), ConstructibleValueType(0)); - QCOMPARE(o->property("ps").value<QList<QPointF>>(), - QList<QPointF>({QPointF(1, 2), QPointF(3, 4), QPointF(55, 0)})); + const QList<QPointF> actual = o->property("ps").value<QList<QPointF>>(); + const QList<QPointF> expected = { + QPointF(1, 2), QPointF(3, 4), QPointF(55, std::numeric_limits<double>::quiet_NaN()) + }; + QCOMPARE(actual.size(), expected.size()); + QCOMPARE(actual[0], expected[0]); + QCOMPARE(actual[1], expected[1]); + QCOMPARE(actual[2].x(), expected[2].x()); + QVERIFY(std::isnan(actual[2].y())); + QCOMPARE(o->property("ss").value<QList<QSizeF>>(), QList<QSizeF>({QSizeF(5, 6), QSizeF(7, 8), QSizeF(-1, 99)})); QCOMPARE(o->property("cs").value<QList<ConstructibleValueType>>(), @@ -343,6 +351,112 @@ void tst_qqmlvaluetypeproviders::structured() ConstructibleFromQReal(-112.5)); QCOMPARE(o->property("cr7").value<ConstructibleFromQReal>(), ConstructibleFromQReal(50)); + + BarrenValueType barren; + barren.setI(17); + QCOMPARE(o->property("barren").value<BarrenValueType>(), barren); + + QMetaObject::invokeMethod(o.data(), "changeBarren"); + QCOMPARE(o->property("barren").value<BarrenValueType>(), BarrenValueType(QString())); + + QCOMPARE(o->property("fromObject").value<ConstructibleValueType>(), + ConstructibleValueType(nullptr)); + QCOMPARE(o->property("aVariant").value<ConstructibleValueType>(), + ConstructibleValueType(nullptr)); + + QCOMPARE(o->property("listResult").toInt(), 12 + 67 + 68); + + + // You can store all kinds of insanity in a VariantObject, but we generally don't. + // Since we cannot rule out the possibility of there being such VariantObjects, we need to test + // their conversions. + + + QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), StructuredValueType()); + + QV4::Scope scope(e.handle()); + QV4::ScopedString name(scope, scope.engine->newString("insanity")); + + QObject *po = o.data(); + QV4::ScopedObject js( + scope, scope.engine->metaTypeToJS(QMetaType::fromType<MyTypeObject *>(), &po)); + + const QVariantHash hash { + {"i", 12}, + {"c", QUrl("http://example.com")}, + {"p", QVariantMap { + {"x", 17}, + {"y", 18} + }} + }; + QV4::ScopedValue hashValue( + scope, e.handle()->newVariantObject(QMetaType::fromType<QVariantHash>(), &hash)); + + js->put(name, hashValue); + + StructuredValueType fromHash; + fromHash.setI(12); + fromHash.setC(ConstructibleValueType(QUrl())); + fromHash.setP(QPointF(17, 18)); + + QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromHash); + + const QVariantMap map { + {"i", 13}, + {"c", QVariant::fromValue(po) }, + {"p", QVariantMap { + {"x", 19}, + {"y", 20} + }} + }; + QV4::ScopedValue mapValue( + scope, e.handle()->newVariantObject(QMetaType::fromType<QVariantMap>(), &map)); + js->put(name, mapValue); + + StructuredValueType fromMap; + fromMap.setI(13); + fromMap.setC(ConstructibleValueType(po)); + fromMap.setP(QPointF(19, 20)); + + QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromMap); + + BarrenValueType immediate; + immediate.setI(14); + QV4::ScopedValue immediateValue( + scope, e.handle()->newVariantObject(QMetaType::fromType<BarrenValueType>(), &immediate)); + js->put(name, immediateValue); + + StructuredValueType fromImmediate; + fromImmediate.setI(14); + + QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromImmediate); + + QQmlComponent c2(&e); + c2.setData( + "import QtQml; QtObject { property int i: 99; property point p: ({x: 3, y: 4}) }", QUrl()); + QVERIFY(c2.isReady()); + QScopedPointer<QObject> o2(c2.create()); + QVERIFY(!o2.isNull()); + QObject *object = o2.data(); + QV4::ScopedValue objectValue( + scope, e.handle()->newVariantObject(QMetaType::fromType<QObject *>(), &object)); + js->put(name, objectValue); + + StructuredValueType fromObject; + fromObject.setI(99); + fromObject.setP(QPointF(3, 4)); + + QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromObject); + + const MyTypeObject *m = static_cast<const MyTypeObject *>(po); + QVERIFY(!m->hasEffectPadding()); + QMetaObject::invokeMethod(po, "updatePadding"); + QVERIFY(m->hasEffectPadding()); + QCOMPARE(m->effectPadding(), QRectF()); + po->setProperty("newItemPadding", QRectF(1, 2, 3, 4)); + QMetaObject::invokeMethod(po, "updatePadding"); + QVERIFY(m->hasEffectPadding()); + QCOMPARE(m->effectPadding(), QRectF(1, 2, 3, 4)); } void tst_qqmlvaluetypeproviders::recursive() @@ -363,6 +477,9 @@ void tst_qqmlvaluetypeproviders::recursive() MyTypeObject *m = qobject_cast<MyTypeObject *>(o.data()); QCOMPARE(m->structured().p().x(), 76); + + // Recursive write back into a list detached from the property. + QCOMPARE(m->property("aa").toInt(), 12); } void tst_qqmlvaluetypeproviders::date() @@ -379,6 +496,72 @@ void tst_qqmlvaluetypeproviders::date() QCOMPARE(o->property("aVariant").value<QDateTime>().time().minute(), 44); } +void tst_qqmlvaluetypeproviders::constructors() +{ + + QQmlEngine e; + + { + const auto guard = qScopeGuard([]() { Padding::log.clear(); }); + QQmlComponent component(&e); + component.setData("import Test\nMyItem { padding : 50 }", QUrl()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + MyItem *item = qobject_cast<MyItem *>(o.data()); + QVERIFY(item); + QCOMPARE(item->padding().left(), 50); + QCOMPARE(item->padding().right(), 50); + + QCOMPARE(Padding::log.length(), 3); + + // Created by default ctor of MyItem + QCOMPARE(Padding::log[0].type, Padding::CustomCtor); + QCOMPARE(Padding::log[0].left, 17); + QCOMPARE(Padding::log[0].right, 17); + + // Created by assignment of integer + QCOMPARE(Padding::log[1].type, Padding::InvokableCtor); + QCOMPARE(Padding::log[1].left, 50); + QCOMPARE(Padding::log[1].right, 50); + + // In MyItem::setPadding() + QCOMPARE(Padding::log[2].type, Padding::CopyAssign); + QCOMPARE(Padding::log[2].left, 50); + QCOMPARE(Padding::log[2].right, 50); + } + + { + const auto guard = qScopeGuard([]() { Padding::log.clear(); }); + QQmlComponent component(&e); + component.setData("import Test\nMyItem { padding: ({ left: 10, right: 20 }) }", QUrl()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + MyItem *item = qobject_cast<MyItem *>(o.data()); + QVERIFY(item); + QCOMPARE(item->padding().left(), 10); + QCOMPARE(item->padding().right(), 20); + + QCOMPARE(Padding::log.length(), 3); + + // Created by default ctor of MyItem + QCOMPARE(Padding::log[0].type, Padding::CustomCtor); + QCOMPARE(Padding::log[0].left, 17); + QCOMPARE(Padding::log[0].right, 17); + + // Preparing for setting properties of structured value + QCOMPARE(Padding::log[1].type, Padding::DefaultCtor); + QCOMPARE(Padding::log[1].left, 0); + QCOMPARE(Padding::log[1].right, 0); + + // In MyItem::setPadding() + QCOMPARE(Padding::log[2].type, Padding::CopyAssign); + QCOMPARE(Padding::log[2].left, 10); + QCOMPARE(Padding::log[2].right, 20); + } +} + QTEST_MAIN(tst_qqmlvaluetypeproviders) #include "tst_qqmlvaluetypeproviders.moc" diff --git a/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt b/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt index fd4bbc1c3f..e167aade13 100644 --- a/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt +++ b/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlvaluetypes Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlvaluetypes LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlvaluetypes/data/constructors.qml b/tests/auto/qml/qqmlvaluetypes/data/constructors.qml new file mode 100644 index 0000000000..d94d6d8ad4 --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/constructors.qml @@ -0,0 +1,14 @@ +import QtQuick as Q + +Q.QtObject { + property var point: new Q.point() + property var size: new Q.size() + property var rect: new Q.rect() + property var color: new Q.color() + property var vector2d: new Q.vector2d() + property var vector3d: new Q.vector3d() + property var vector4d: new Q.vector4d() + property var quaternion: new Q.quaternion() + property var matrix4x4: new Q.matrix4x4() + property var font: new Q.font() +} diff --git a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml index c28901956d..1827b57ca9 100644 --- a/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml +++ b/tests/auto/qml/qqmlvaluetypes/data/matrix4x4_invokables.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick Item { property bool success: false @@ -6,6 +6,7 @@ Item { property variant m1: Qt.matrix4x4(1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4) property variant m2: Qt.matrix4x4(5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8) property variant m3: Qt.matrix4x4(123,22,6,42,55,54,67,77,777,1,112,22,55,6696,77,777) + property matrix4x4 m4: PlanarTransform.fromAffineMatrix(1, 2, 3, 4, 5, 6) property variant v1: Qt.vector4d(1,2,3,4) property variant v2: Qt.vector3d(1,2,3) property real factor: 2.23 @@ -101,6 +102,7 @@ Item { if (m1.transposed() != Qt.matrix4x4(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)) success = false; if (m1.fuzzyEquals(m2)) success = false; if (!m1.fuzzyEquals(m2, 10)) success = false; + if (m4 != Qt.matrix4x4(1, 3, 0, 5, 2, 4, 0, 6, 0, 0, 1, 0, 0, 0, 0, 1)) success = false; if (!testTransformation()) success = false; if (!testMatrixMapping()) success = false; } diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.cpp b/tests/auto/qml/qqmlvaluetypes/testtypes.cpp index 4344445514..d6304bf086 100644 --- a/tests/auto/qml/qqmlvaluetypes/testtypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/testtypes.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testtypes.h" void registerTypes() diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h index 2c431c64af..19cfc25d23 100644 --- a/tests/auto/qml/qqmlvaluetypes/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTTYPES_H #define TESTTYPES_H diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index 1f9dfb7fd4..ea521053ae 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -78,6 +78,7 @@ private slots: void writeBackOnFunctionCall(); void valueTypeConversions(); void readReferenceOnGetOwnProperty(); + void constructors(); private: QQmlEngine engine; @@ -93,30 +94,26 @@ void tst_qqmlvaluetypes::point() { { QQmlComponent component(&engine, testFileUrl("point_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->property("p_x").toInt(), 10); QCOMPARE(object->property("p_y").toInt(), 4); QCOMPARE(object->property("copy"), QVariant(QPoint(10, 4))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("point_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->point(), QPoint(11, 12)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("point_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QPoint(10, 4)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -129,8 +126,6 @@ void tst_qqmlvaluetypes::point() QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); QCOMPARE(object->property("pointEqualsPointf").toBool(), true); - - delete object; } } @@ -138,30 +133,26 @@ void tst_qqmlvaluetypes::pointf() { { QQmlComponent component(&engine, testFileUrl("pointf_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(float(object->property("p_x").toDouble()), float(11.3)); QCOMPARE(float(object->property("p_y").toDouble()), float(-10.9)); QCOMPARE(object->property("copy"), QVariant(QPointF(11.3, -10.9))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("pointf_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->pointf(), QPointF(6.8, 9.3)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("pointf_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QPointF(11.3, -10.9)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -174,8 +165,6 @@ void tst_qqmlvaluetypes::pointf() QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); QCOMPARE(object->property("pointfEqualsPoint").toBool(), true); - - delete object; } } @@ -183,30 +172,26 @@ void tst_qqmlvaluetypes::size() { { QQmlComponent component(&engine, testFileUrl("size_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->property("s_width").toInt(), 1912); QCOMPARE(object->property("s_height").toInt(), 1913); QCOMPARE(object->property("copy"), QVariant(QSize(1912, 1913))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("size_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->size(), QSize(13, 88)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("size_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QSize(1912, 1913)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -219,8 +204,6 @@ void tst_qqmlvaluetypes::size() QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); QCOMPARE(object->property("sizeEqualsSizef").toBool(), true); - - delete object; } } @@ -228,30 +211,26 @@ void tst_qqmlvaluetypes::sizef() { { QQmlComponent component(&engine, testFileUrl("sizef_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(float(object->property("s_width").toDouble()), float(0.1)); QCOMPARE(float(object->property("s_height").toDouble()), float(100923.2)); QCOMPARE(object->property("copy"), QVariant(QSizeF(0.1, 100923.2))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("sizef_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->sizef(), QSizeF(44.3, 92.8)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("sizef_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QSizeF(0.1, 100923)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -264,8 +243,6 @@ void tst_qqmlvaluetypes::sizef() QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); QCOMPARE(object->property("sizefEqualsSize").toBool(), true); - - delete object; } } @@ -299,9 +276,9 @@ void tst_qqmlvaluetypes::locale() QCOMPARE(object->property("textDirection").toInt(), int(locale.textDirection())); QCOMPARE(object->property("uiLanguages").toStringList(), locale.uiLanguages()); QList<Qt::DayOfWeek> weekDays; - foreach (const QVariant &weekDay, object->property("weekDays").toList()) { + const QVariantList weekDaysProperty = object->property("weekDays").toList(); + for (const QVariant &weekDay : weekDaysProperty) weekDays.append(Qt::DayOfWeek(weekDay.toInt())); - } QCOMPARE(weekDays, locale.weekdays()); QCOMPARE(object->property("zeroDigit").toString().at(0), locale.zeroDigit()); #endif // im @@ -311,29 +288,25 @@ void tst_qqmlvaluetypes::locale() void tst_qqmlvaluetypes::qmlproperty() { QQmlComponent component(&engine, testFileUrl("qmlproperty_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); - QCOMPARE(object->property("colorPropertyObject").value<QObject *>(), object); + QCOMPARE(object->property("colorPropertyObject").value<QObject *>(), object.get()); QCOMPARE(object->property("colorPropertyName").toString(), "color"); QCOMPARE(object->property("invalidPropertyObject").value<QObject *>(), nullptr); QCOMPARE(object->property("invalidPropertyName").toString(), ""); - - delete object; } void tst_qqmlvaluetypes::sizereadonly() { { QQmlComponent component(&engine, testFileUrl("sizereadonly_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->property("s_width").toInt(), 1912); QCOMPARE(object->property("s_height").toInt(), 1913); QCOMPARE(object->property("copy"), QVariant(QSize(1912, 1913))); - - delete object; } { @@ -357,12 +330,10 @@ void tst_qqmlvaluetypes::sizereadonly() { QQmlComponent component(&engine, testFileUrl("sizereadonly_writeerror4.qml")); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object); QCOMPARE(object->property("sizereadonly").toSize(), QSize(1912, 1913)); - - delete object; } } @@ -370,8 +341,8 @@ void tst_qqmlvaluetypes::rect() { { QQmlComponent component(&engine, testFileUrl("rect_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->property("r_x").toInt(), 2); QCOMPARE(object->property("r_y").toInt(), 3); @@ -382,24 +353,20 @@ void tst_qqmlvaluetypes::rect() QCOMPARE(object->property("r_top").toInt(), 3); QCOMPARE(object->property("r_bottom").toInt(), 104); QCOMPARE(object->property("copy"), QVariant(QRect(2, 3, 109, 102))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("rect_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect(), QRect(1234, 7, 56, 63)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("rect_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QRect(2, 3, 109, 102)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -412,8 +379,6 @@ void tst_qqmlvaluetypes::rect() QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); QCOMPARE(object->property("rectEqualsRectf").toBool(), true); - - delete object; } } @@ -421,8 +386,8 @@ void tst_qqmlvaluetypes::rectf() { { QQmlComponent component(&engine, testFileUrl("rectf_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(float(object->property("r_x").toDouble()), float(103.8)); QCOMPARE(float(object->property("r_y").toDouble()), float(99.2)); @@ -433,24 +398,20 @@ void tst_qqmlvaluetypes::rectf() QCOMPARE(float(object->property("r_top").toDouble()), float(99.2)); QCOMPARE(float(object->property("r_bottom").toDouble()), float(176.8)); QCOMPARE(object->property("copy"), QVariant(QRectF(103.8, 99.2, 88.1, 77.6))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("rectf_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rectf(), QRectF(70.1, -113.2, 80924.8, 99.2)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("rectf_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QRectF(103.8, 99.2, 88.1, 77.6)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -463,8 +424,6 @@ void tst_qqmlvaluetypes::rectf() QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); QCOMPARE(object->property("rectfEqualsRect").toBool(), true); - - delete object; } } @@ -472,30 +431,26 @@ void tst_qqmlvaluetypes::vector2d() { { QQmlComponent component(&engine, testFileUrl("vector2d_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE((float)object->property("v_x").toDouble(), (float)32.88); QCOMPARE((float)object->property("v_y").toDouble(), (float)1.3); QCOMPARE(object->property("copy"), QVariant(QVector2D(32.88f, 1.3f))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector2d_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->vector2(), QVector2D(-0.3f, -12.9f)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector2d_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QVector2D(32.88, 1.3)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -506,16 +461,13 @@ void tst_qqmlvaluetypes::vector2d() QCOMPARE(object->property("equalsPoint").toBool(), false); QCOMPARE(object->property("equalsRect").toBool(), false); QCOMPARE(object->property("equalsSelf").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector2d_invokables.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - delete object; } } @@ -523,31 +475,27 @@ void tst_qqmlvaluetypes::vector3d() { { QQmlComponent component(&engine, testFileUrl("vector3d_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE((float)object->property("v_x").toDouble(), (float)23.88); QCOMPARE((float)object->property("v_y").toDouble(), (float)3.1); QCOMPARE((float)object->property("v_z").toDouble(), (float)4.3); QCOMPARE(object->property("copy"), QVariant(QVector3D(23.88f, 3.1f, 4.3f))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector3d_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->vector(), QVector3D(-0.3f, -12.9f, 907.4f)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector3d_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QVector3D(23.88, 3.1, 4.3)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -559,16 +507,13 @@ void tst_qqmlvaluetypes::vector3d() QCOMPARE(object->property("equalsRect").toBool(), false); QCOMPARE(object->property("equalsSelf").toBool(), true); QCOMPARE(object->property("equalsOther").toBool(), false); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector3d_invokables.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - delete object; } } @@ -576,32 +521,28 @@ void tst_qqmlvaluetypes::vector4d() { { QQmlComponent component(&engine, testFileUrl("vector4d_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2); QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88); QCOMPARE((float)object->property("v_z").toDouble(), (float)3.1); QCOMPARE((float)object->property("v_w").toDouble(), (float)4.3); QCOMPARE(object->property("copy"), QVariant(QVector4D(54.2f, 23.88f, 3.1f, 4.3f))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector4d_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->vector4(), QVector4D(-0.3f, -12.9f, 907.4f, 88.5f)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector4d_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QVector4D(54.2, 23.88, 3.1, 4.3)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -612,16 +553,13 @@ void tst_qqmlvaluetypes::vector4d() QCOMPARE(object->property("equalsPoint").toBool(), false); QCOMPARE(object->property("equalsRect").toBool(), false); QCOMPARE(object->property("equalsSelf").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("vector4d_invokables.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - delete object; } } @@ -629,32 +567,28 @@ void tst_qqmlvaluetypes::quaternion() { { QQmlComponent component(&engine, testFileUrl("quaternion_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE((float)object->property("v_scalar").toDouble(), (float)4.3); QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2); QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88); QCOMPARE((float)object->property("v_z").toDouble(), (float)3.1); QCOMPARE(object->property("copy"), QVariant(QQuaternion(4.3f, 54.2f, 23.88f, 3.1f))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("quaternion_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->quaternion(), QQuaternion(88.5f, -0.3f, -12.9f, 907.4f)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("quaternion_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QQuaternion(4.3, 54.2, 23.88, 3.1)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -665,16 +599,13 @@ void tst_qqmlvaluetypes::quaternion() QCOMPARE(object->property("equalsPoint").toBool(), false); QCOMPARE(object->property("equalsRect").toBool(), false); QCOMPARE(object->property("equalsSelf").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("quaternion_invokables.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - delete object; } } @@ -682,8 +613,8 @@ void tst_qqmlvaluetypes::matrix4x4() { { QQmlComponent component(&engine, testFileUrl("matrix4x4_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE((float)object->property("v_m11").toDouble(), (float)1); QCOMPARE((float)object->property("v_m12").toDouble(), (float)2); @@ -706,27 +637,23 @@ void tst_qqmlvaluetypes::matrix4x4() 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))); - - delete object; } { QQmlComponent component(&engine, testFileUrl("matrix4x4_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->matrix(), QMatrix4x4(11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("matrix4x4_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QMatrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -737,16 +664,13 @@ void tst_qqmlvaluetypes::matrix4x4() QCOMPARE(object->property("equalsPoint").toBool(), false); QCOMPARE(object->property("equalsRect").toBool(), false); QCOMPARE(object->property("equalsSelf").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("matrix4x4_invokables.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("success").toBool(), true); - delete object; } } @@ -754,9 +678,9 @@ void tst_qqmlvaluetypes::font() { { QQmlComponent component(&engine, testFileUrl("font_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QVERIFY(object != nullptr); + QVERIFY(object); QCOMPARE(object->property("f_family").toString(), object->font().family()); QCOMPARE(object->property("f_bold").toBool(), object->font().bold()); @@ -783,14 +707,12 @@ void tst_qqmlvaluetypes::font() QCOMPARE(object->property("f_wordSpacing").toDouble(), object->font().wordSpacing()); QCOMPARE(object->property("copy"), QVariant(object->font())); - - delete object; } { QQmlComponent component(&engine, testFileUrl("font_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QFont font; font.setFamily("Helvetica"); @@ -815,46 +737,38 @@ void tst_qqmlvaluetypes::font() QCOMPARE(f.capitalization(), font.capitalization()); QCOMPARE(f.letterSpacing(), font.letterSpacing()); QCOMPARE(f.wordSpacing(), font.wordSpacing()); - - delete object; } // Test pixelSize { QQmlComponent component(&engine, testFileUrl("font_write.2.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().pixelSize(), 10); - - delete object; } // Test pixelSize and pointSize { QQmlComponent component(&engine, testFileUrl("font_write.3.qml")); QTest::ignoreMessage(QtWarningMsg, "Both point size and pixel size set. Using pixel size."); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().pixelSize(), 10); - - delete object; } { QQmlComponent component(&engine, testFileUrl("font_write.4.qml")); QTest::ignoreMessage(QtWarningMsg, "Both point size and pixel size set. Using pixel size."); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().pixelSize(), 10); - - delete object; } { QQmlComponent component(&engine, testFileUrl("font_write.5.qml")); - QObject *object = qobject_cast<QObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { qobject_cast<QObject *>(component.create()) }; + QVERIFY(object); MyTypeObject *object1 = object->findChild<MyTypeObject *>("object1"); QVERIFY(object1 != nullptr); MyTypeObject *object2 = object->findChild<MyTypeObject *>("object2"); @@ -862,14 +776,12 @@ void tst_qqmlvaluetypes::font() QCOMPARE(object1->font().pixelSize(), 19); QCOMPARE(object2->font().pointSize(), 14); - - delete object; } { QQmlComponent component(&engine, testFileUrl("font_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QString tostring = QLatin1String("QFont(") + object->font().toString() + QLatin1Char(')'); QCOMPARE(object->property("tostring").toString(), tostring); @@ -880,8 +792,6 @@ void tst_qqmlvaluetypes::font() QCOMPARE(object->property("equalsPoint").toBool(), false); QCOMPARE(object->property("equalsRect").toBool(), false); QCOMPARE(object->property("equalsSelf").toBool(), true); - - delete object; } } @@ -889,8 +799,8 @@ void tst_qqmlvaluetypes::color() { { QQmlComponent component(&engine, testFileUrl("color_read.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(float(object->property("v_r").toDouble()), 0.2f); QCOMPARE(float(object->property("v_g").toDouble()), 0.88f); @@ -916,14 +826,12 @@ void tst_qqmlvaluetypes::color() comparison.setBlueF(0.6f); comparison.setAlphaF(0.34f); QCOMPARE(object->property("copy"), QVariant(comparison)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("color_write.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QColor newColor; newColor.setRedF(0.5f); @@ -931,38 +839,32 @@ void tst_qqmlvaluetypes::color() newColor.setBlueF(0.3f); newColor.setAlphaF(0.7f); QCOMPARE(object->color(), newColor); - - delete object; } { QQmlComponent component(&engine, testFileUrl("color_write_HSV.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QColor newColor; newColor.setHsvF(0.43f, 0.77f, 0.88f, 0.7f); QCOMPARE(object->color(), newColor); - - delete object; } { QQmlComponent component(&engine, testFileUrl("color_write_HSL.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QColor newColor; newColor.setHslF(0.43f, 0.74f, 0.54f, 0.7f); QCOMPARE(object->color(), newColor); - - delete object; } { QQmlComponent component(&engine, testFileUrl("color_compare.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QColor comparison; comparison.setRedF(0.2f); comparison.setGreenF(0.88f); @@ -990,8 +892,6 @@ void tst_qqmlvaluetypes::color() QCOMPARE(object->property("equalsColorRHS").toBool(), object->property("equalsColor").toBool()); QCOMPARE(object->property("colorEqualsCopy").toBool(), true); QCOMPARE(object->property("copyEqualsColor").toBool(), object->property("colorEqualsCopy").toBool()); - - delete object; } } @@ -1001,8 +901,8 @@ void tst_qqmlvaluetypes::bindingAssignment() // binding declaration { QQmlComponent component(&engine, testFileUrl("bindingAssignment.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect().x(), 10); QCOMPARE(object->rect().y(), 15); @@ -1011,8 +911,6 @@ void tst_qqmlvaluetypes::bindingAssignment() QCOMPARE(object->rect().x(), 92); QCOMPARE(object->rect().y(), 97); - - delete object; } // function assignment should fail without crashing @@ -1022,12 +920,11 @@ void tst_qqmlvaluetypes::bindingAssignment() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QQmlComponent component(&engine, testFileUrl("bindingAssignment.2.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect().x(), 5); object->setProperty("value", QVariant(92)); QCOMPARE(object->rect().x(), 5); - delete object; } } @@ -1035,42 +932,36 @@ void tst_qqmlvaluetypes::bindingAssignment() void tst_qqmlvaluetypes::bindingRead() { QQmlComponent component(&engine, testFileUrl("bindingRead.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->property("value").toInt(), 2); object->setRect(QRect(19, 3, 88, 2)); QCOMPARE(object->property("value").toInt(), 19); - - delete object; } // Test static values can assign to value types void tst_qqmlvaluetypes::staticAssignment() { QQmlComponent component(&engine, testFileUrl("staticAssignment.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect().x(), 9); - - delete object; } // Test scripts can read/write value types void tst_qqmlvaluetypes::scriptAccess() { QQmlComponent component(&engine, testFileUrl("scriptAccess.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->property("valuePre").toInt(), 2); QCOMPARE(object->rect().x(), 19); QCOMPARE(object->property("valuePost").toInt(), 19); - - delete object; } // Test that assigning a constant from script removes any binding @@ -1078,8 +969,8 @@ void tst_qqmlvaluetypes::autoBindingRemoval() { { QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect().x(), 10); @@ -1094,14 +985,12 @@ void tst_qqmlvaluetypes::autoBindingRemoval() object->setProperty("value", QVariant(92)); QCOMPARE(object->rect().x(), 42); - - delete object; } { QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.2.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect().x(), 10); @@ -1116,16 +1005,14 @@ void tst_qqmlvaluetypes::autoBindingRemoval() object->setProperty("value", QVariant(92)); QCOMPARE(object->rect(), QRect(10, 10, 10, 10)); - - delete object; } { QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.3.qml")); QString warning = component.url().toString() + ":6:5: Unable to assign [undefined] to QRect"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); object->setProperty("value", QVariant(QRect(9, 22, 33, 44))); @@ -1138,8 +1025,6 @@ void tst_qqmlvaluetypes::autoBindingRemoval() object->setProperty("value", QVariant(QRect(19, 3, 4, 8))); QCOMPARE(object->rect(), QRect(44, 22, 33, 44)); - - delete object; } } @@ -1147,12 +1032,10 @@ void tst_qqmlvaluetypes::autoBindingRemoval() void tst_qqmlvaluetypes::valueSources() { QQmlComponent component(&engine, testFileUrl("valueSources.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect().x(), 3345); - - delete object; } static void checkNoErrors(QQmlComponent& component) @@ -1170,17 +1053,15 @@ static void checkNoErrors(QQmlComponent& component) void tst_qqmlvaluetypes::valueInterceptors() { QQmlComponent component(&engine, testFileUrl("valueInterceptors.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; checkNoErrors(component); - QVERIFY(object != nullptr); + QVERIFY(object); QCOMPARE(object->rect().x(), 13); object->setProperty("value", 99); QCOMPARE(object->rect().x(), 112); - - delete object; } // Test that you can't assign a binding to the "root" value type, and a sub-property @@ -1192,11 +1073,10 @@ void tst_qqmlvaluetypes::bindingConflict() #define CPP_TEST(type, v) \ { \ - type *t = new type; \ + std::unique_ptr<type> t = std::make_unique<type>(); \ QVariant value(v); \ t->setValue(value); \ QCOMPARE(t->value(), value); \ - delete t; \ } // Test that accessing a reference to a valuetype after the owning object is deleted @@ -1205,87 +1085,76 @@ void tst_qqmlvaluetypes::deletedObject() { QQmlComponent component(&engine, testFileUrl("deletedObject.qml")); QTest::ignoreMessage(QtDebugMsg, "Test: 2"); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); - QObject *dObject = qvariant_cast<QObject *>(object->property("object")); - QVERIFY(dObject != nullptr); - delete dObject; + std::unique_ptr<QObject> dObject { qvariant_cast<QObject *>(object->property("object")) }; + QVERIFY(dObject); + dObject.reset(); QTest::ignoreMessage(QtDebugMsg, "Test: undefined"); object->emitRunScript(); - - delete object; } // Test that value types can be assigned to another value type property in a binding void tst_qqmlvaluetypes::bindingVariantCopy() { QQmlComponent component(&engine, testFileUrl("bindingVariantCopy.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect(), QRect(19, 33, 5, 99)); - - delete object; } // Test that value types can be assigned to another value type property in script void tst_qqmlvaluetypes::scriptVariantCopy() { QQmlComponent component(&engine, testFileUrl("scriptVariantCopy.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->rect(), QRect(2, 3, 109, 102)); object->emitRunScript(); QCOMPARE(object->rect(), QRect(19, 33, 5, 99)); - - delete object; } void tst_qqmlvaluetypes::enums() { { QQmlComponent component(&engine, testFileUrl("enums.1.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); - delete object; } { QQmlComponent component(&engine, testFileUrl("enums.2.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); - delete object; } { QQmlComponent component(&engine, testFileUrl("enums.3.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); - delete object; } { QQmlComponent component(&engine, testFileUrl("enums.4.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); - delete object; } { QQmlComponent component(&engine, testFileUrl("enums.5.qml")); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != nullptr); + std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) }; + QVERIFY(object); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); - delete object; } } @@ -1295,83 +1164,73 @@ void tst_qqmlvaluetypes::conflictingBindings() { { QQmlComponent component(&engine, testFileUrl("conflicting.1.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); - QMetaObject::invokeMethod(object, "toggle"); + QMetaObject::invokeMethod(object.get(), "toggle"); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6); - QMetaObject::invokeMethod(object, "toggle"); + QMetaObject::invokeMethod(object.get(), "toggle"); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); - - delete object; } { QQmlComponent component(&engine, testFileUrl("conflicting.2.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6); - QMetaObject::invokeMethod(object, "toggle"); + QMetaObject::invokeMethod(object.get(), "toggle"); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); - QMetaObject::invokeMethod(object, "toggle"); + QMetaObject::invokeMethod(object.get(), "toggle"); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6); - - delete object; } { QQmlComponent component(&engine, testFileUrl("conflicting.3.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); - QMetaObject::invokeMethod(object, "toggle"); + QMetaObject::invokeMethod(object.get(), "toggle"); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 24); - QMetaObject::invokeMethod(object, "toggle"); + QMetaObject::invokeMethod(object.get(), "toggle"); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); - - delete object; } } void tst_qqmlvaluetypes::returnValues() { QQmlComponent component(&engine, testFileUrl("returnValues.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); QCOMPARE(object->property("size").toSize(), QSize(13, 14)); - - delete object; } void tst_qqmlvaluetypes::varAssignment() { QQmlComponent component(&engine, testFileUrl("varAssignment.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("x").toInt(), 1); QCOMPARE(object->property("y").toInt(), 2); QCOMPARE(object->property("z").toInt(), 3); - - delete object; } // Test bindings splice together correctly @@ -1379,77 +1238,63 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly() { { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.1.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.2.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.3.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.4.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.5.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test").toBool(), true); - - delete object; } } void tst_qqmlvaluetypes::nonValueTypeComparison() { QQmlComponent component(&engine, testFileUrl("nonValueTypeComparison.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); - - delete object; } void tst_qqmlvaluetypes::initializeByWrite() { QQmlComponent component(&engine, testFileUrl("initializeByWrite.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QCOMPARE(object->property("test").toBool(), true); - - delete object; } void tst_qqmlvaluetypes::groupedInterceptors_data() @@ -1483,8 +1328,8 @@ void tst_qqmlvaluetypes::groupedInterceptors() QFETCH(QColor, expectedFinalColor); QQmlComponent component(&engine, testFileUrl(qmlfile)); - QObject *object = component.create(); - QVERIFY2(object != nullptr, qPrintable(component.errorString())); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY2(object.get(), qPrintable(component.errorString())); QColor initialColor = object->property("color").value<QColor>(); QVERIFY(fuzzyCompare(initialColor.redF(), expectedInitialColor.redF())); @@ -1499,8 +1344,6 @@ void tst_qqmlvaluetypes::groupedInterceptors() QVERIFY(fuzzyCompare(finalColor.greenF(), expectedFinalColor.greenF())); QVERIFY(fuzzyCompare(finalColor.blueF(), expectedFinalColor.blueF())); QVERIFY(fuzzyCompare(finalColor.alphaF(), expectedFinalColor.alphaF())); - - delete object; } struct MyDesk @@ -1990,6 +1833,28 @@ void tst_qqmlvaluetypes::readReferenceOnGetOwnProperty() QVERIFY(o->property("allo").toBool()); } +void tst_qqmlvaluetypes::constructors() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("constructors.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("point"), QVariant(QPointF())); + QCOMPARE(o->property("size"), QVariant(QSizeF())); + QCOMPARE(o->property("rect"), QVariant(QRectF())); + QCOMPARE(o->property("color"), QVariant(QColor())); + QCOMPARE(o->property("vector2d"), QVariant(QVector2D())); + QCOMPARE(o->property("vector3d"), QVariant(QVector3D())); + QCOMPARE(o->property("vector4d"), QVariant(QVector4D())); + QCOMPARE(o->property("quaternion"), QVariant(QQuaternion())); + QCOMPARE(o->property("matrix4x4"), QVariant(QMatrix4x4())); + QCOMPARE(o->property("font"), QVariant(QFont())); + +} + #undef CHECK_TYPE_IS_NOT_VALUETYPE QTEST_MAIN(tst_qqmlvaluetypes) diff --git a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt index 529840c484..3d93585812 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt +++ b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qqmlxmlhttprequest Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlxmlhttprequest LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect index 6dba52d2de..15274c5e58 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect @@ -1,13 +1,13 @@ PROPFIND /container/ HTTP/1.1 -Depth: 1 -Content-Length: 95 -Connection: Keep-Alive, Upgrade, HTTP2-Settings -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -Accept-Language: en-US,* -Content-type:i application/xml; charset="utf-8" -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +depth: 1 +content-length: 95 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +accept-language: {{Ignore}} +content-type:i application/xml; charset="utf-8" +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} <?xml version="1.0" encoding="utf-8" ?> <D:propfind xmlns:D="DAV:"> diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect index dc7217d45c..f7bb57ad89 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect @@ -1,12 +1,12 @@ PROPFIND /file HTTP/1.1 -Content-Length: 192 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -Accept-Language: en-US,* -Content-type: text/xml; charset="utf-8" -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +content-length: 192 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +accept-language: {{Ignore}} +content-type: text/xml; charset="utf-8" +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} <?xml version="1.0" encoding="utf-8" ?> <D:propfind xmlns:D="DAV:"> diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml index 5c47a27420..624cdc4a41 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml index dbb57b3b1b..5249ad84c1 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml index ecc3627060..eb1dd53a97 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect b/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect index 97c1661851..90e15895a6 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect @@ -1,11 +1,11 @@ PUT /testdocument.html HTTP/1.1 -Accept-Language: en-US -Content-Type: text/plain;charset=UTF-8 -Content-Length: 10 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: text/plain;charset=UTF-8 +content-length: 10 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} Test Data diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml b/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml index c8d8382c8f..f07ba2dbc6 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml @@ -13,32 +13,25 @@ QtObject { var x = new XMLHttpRequest; x.open("GET", urlDummy); x.setRequestHeader("Test-header", "TestValue"); - x.setRequestHeader("Accept-Language", "en-US"); x.send(); x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) seenDone = true; - } else if (x.readyState == XMLHttpRequest.UNSENT) { + else if (x.readyState == XMLHttpRequest.UNSENT) didNotSeeUnsent = false; - } } x.abort(); - if (x.readyState == XMLHttpRequest.UNSENT) { + if (x.readyState == XMLHttpRequest.UNSENT) endStateUnsent = true; - } x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.open("PUT", url); - x.setRequestHeader("Accept-Language", "en-US"); x.send("Test Data\n"); } } - - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml b/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml index 7fcbc9367a..5933c3062d 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml @@ -21,14 +21,10 @@ QtObject { readyState = true; x.open("PUT", url); - x.setRequestHeader("Accept-Language", "en-US"); - x.abort(); x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -53,8 +49,6 @@ QtObject { } } - x.send() } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml b/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml index 0a85c6b16f..36febc41dc 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml @@ -21,9 +21,7 @@ QtObject { readyState = true; x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -43,13 +41,10 @@ QtObject { // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } - x.send() } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml b/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml index a9066093d4..cf38ca22df 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml @@ -31,9 +31,7 @@ QtObject { readyState = true; x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -43,7 +41,11 @@ QtObject { openedException = true; } - var headers = "connection: close\r\ncontent-type: text/html; charset=UTF-8\r\ntest-header: TestValue\r\nmultitest-header: TestValue, SecondTestValue\r\ncontent-length: 11"; + var headers = [ "connection: close", + "content-type: text/html; charset=UTF-8", + "test-header: TestValue", + "multitest-header: TestValue, SecondTestValue", + "content-length: 11" ].join("\r\n"); // Test to the end x.onreadystatechange = function() { @@ -52,7 +54,7 @@ QtObject { headersReceivedHeader = (x.getAllResponseHeaders() == headers); } else if (x.readyState == XMLHttpRequest.DONE) { - doneState = headersReceivedState && true; + doneState = headersReceivedState; doneHeader = (x.getAllResponseHeaders() == headers); dataOK = (x.responseText == "QML Rocks!\n"); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect index 2270e7f3aa..299bfd4689 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect @@ -1,8 +1,8 @@ GET /testdocument.html HTTP/1.1 -Accept-Language: en-US -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml index 0219d7b2fc..02c1b6ef47 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml @@ -37,9 +37,7 @@ QtObject { readyState = true; x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -56,15 +54,20 @@ QtObject { headersReceivedNullHeader = (x.getResponseHeader("Nonexistant-header") == ""); headersReceivedValidHeader = (x.getResponseHeader("Test-HEAder") == "TestValue"); - headersReceivedMultiValidHeader = (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue"); - headersReceivedCookieHeader = (x.getResponseHeader("Set-Cookie") == "" && x.getResponseHeader("Set-Cookie2") == ""); + headersReceivedMultiValidHeader = + (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue"); + headersReceivedCookieHeader = + (x.getResponseHeader("Set-Cookie") == "" + && x.getResponseHeader("Set-Cookie2") == ""); } else if (x.readyState == XMLHttpRequest.DONE) { - doneState = headersReceivedState && true; + doneState = headersReceivedState; doneNullHeader = (x.getResponseHeader("Nonexistant-header") == ""); doneValidHeader = (x.getResponseHeader("Test-HEAder") == "TestValue"); - doneMultiValidHeader = (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue"); - doneCookieHeader = (x.getResponseHeader("Set-Cookie") == "" && x.getResponseHeader("Set-Cookie2") == ""); + doneMultiValidHeader = + (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue"); + doneCookieHeader = (x.getResponseHeader("Set-Cookie") == "" + && x.getResponseHeader("Set-Cookie2") == ""); dataOK = (x.responseText == "QML Rocks!\n"); } } diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js index adb7269310..4c592bc8de 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js +++ b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js @@ -1,11 +1,9 @@ (function(url, resultCollector) { var x = new XMLHttpRequest; x.open("GET", url); - x.setRequestHeader("Accept-Language","en-US"); x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) resultCollector.responseText = x.responseText - } } x.send() }) diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open.qml index 6e7681dfb4..26934fabe9 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/open.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/open.qml @@ -20,9 +20,7 @@ QtObject { readyState = true; x.open("GET", url); - x.setRequestHeader("Accept-Language","en-US"); - - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -42,13 +40,10 @@ QtObject { // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } - x.send() } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect b/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect index 2270e7f3aa..299bfd4689 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect @@ -1,8 +1,8 @@ GET /testdocument.html HTTP/1.1 -Accept-Language: en-US -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml index 3c73141954..24711de2b3 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml @@ -7,7 +7,6 @@ QtObject { Component.onCompleted: { var x = new XMLHttpRequest; x.open("GET", url, false); - x.setRequestHeader("Accept-Language", "en-US"); x.send(); responseText = x.responseText; } diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml index 4eaef536b3..f22118a1b0 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml @@ -20,9 +20,7 @@ QtObject { readyState = true; x.open("GET", url, true, "username", "password"); - x.setRequestHeader("Accept-Language","en-US"); - - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -42,13 +40,10 @@ QtObject { // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } - x.send() } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml index b8ce5361f3..b62b09097e 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml @@ -21,7 +21,7 @@ QtObject { x.open("GET", url, true, "sampleusername", "password"); - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml new file mode 100644 index 0000000000..922147bc4e --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml @@ -0,0 +1,20 @@ +import QtQuick + +QtObject { + property string url + property bool dataOK: false + + Component.onCompleted: { + let xhr = new XMLHttpRequest; + xhr.open("GET", url); + xhr.overrideMimeType('text/xml'); + + // Test to the end + xhr.onreadystatechange = function() { + if (xhr.readyState === XMLHttpRequest.DONE) + dataOK = xhr.responseXML !== null; + } + + xhr.send(); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply new file mode 100644 index 0000000000..fed050b72d --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Connection: close +Content-Type: text/plain diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml b/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml index b9f0ab6e66..15503d1bc6 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml @@ -4,6 +4,7 @@ QtObject { property string url property int readSize: 0 property int status: 0 + id: object Component.onCompleted: { @@ -13,7 +14,7 @@ QtObject { request.onreadystatechange = function() { if (request.readyState == XMLHttpRequest.DONE) { - status = request.status; + object.status = request.status; var arrayBuffer = request.response; if (arrayBuffer) { var byteArray = new Uint8Array(arrayBuffer); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect index 35d28ec76d..d18a95c7dd 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect @@ -1,8 +1,8 @@ GET /gml_logo.png HTTP/1.1 -Accept-Language: en-US,* -Content-Type: image/png -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: image/png +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect b/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect index 9784ba4a80..0df27ead2c 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect @@ -1,8 +1,8 @@ GET /json.data HTTP/1.1 -Accept-Language: en-US,* -Content-Type: application/jsonrequest -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: application/jsonrequest +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml b/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml index 4b216d9c85..cf06b6fda6 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml @@ -22,8 +22,6 @@ QtObject { unsent = (x.responseText == ""); x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - opened = (x.responseText == ""); // Test to the end @@ -40,8 +38,6 @@ QtObject { dataOK = (x.responseText == expectedText); x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - reset = (x.responseText == ""); } } @@ -51,4 +47,3 @@ QtObject { sent = (x.responseText == ""); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml b/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml new file mode 100644 index 0000000000..3a35dcc882 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +QtObject { + property string url + property string expectedURL + + property bool dataOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("GET", url); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState === XMLHttpRequest.DONE) + dataOK = (x.responseURL === expectedURL); + } + + x.send() + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml index 65ce90853d..1a3a5285c0 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml @@ -7,13 +7,11 @@ QtObject { Component.onCompleted: { var x = new XMLHttpRequest; x.open("GET", "testdocument.html"); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send(); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect index 64bd2b3818..708aa1a7f0 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect @@ -1,11 +1,11 @@ POST /testdocument.html HTTP/1.1 -Accept-Language: en-US -Content-Type: text/plain;charset=UTF-8 -Content-Length: 13 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: text/plain;charset=UTF-8 +content-length: 13 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} My Sent Data diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml index 57203433c0..2f794650ba 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml @@ -2,19 +2,16 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect index 2bf3b5a081..bb82e8ba74 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect @@ -1,11 +1,11 @@ OPTIONS /testdocument.html HTTP/1.1 -Accept-Language: en-US,* -Content-Type: text/plain;charset=UTF-8 -Content-Length: 13 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: text/plain;charset=UTF-8 +content-length: 13 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} My Sent Data diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect Binary files differindex 7132819c72..341fb36f2a 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml index 25584a662b..136243bbb2 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml @@ -2,19 +2,16 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } var data = new Uint8Array([1, 2, 3, 0, 3, 2, 1, 10]) diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml index e63d58fdb6..e3d55889d6 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml @@ -2,23 +2,19 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); x.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml index cfd67a6a72..eaf5a057ae 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml @@ -2,23 +2,19 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); x.setRequestHeader("Content-Type", "text/plain;charset=latin1"); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect index 270ee838d9..75e6758f14 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect @@ -1,11 +1,11 @@ POST /testdocument.html HTTP/1.1 -Accept-Language: en-US -Content-Type: charset=UTF-8;text/plain -Content-Length: 13 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: charset=UTF-8;text/plain +content-length: 13 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} My Sent Data diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml index 808fb3a18d..e65982e6cf 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml @@ -2,23 +2,19 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); x.setRequestHeader("Content-Type", "charset=UTF-8;text/plain"); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml index c8735173d3..b3c24a5328 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml @@ -2,23 +2,19 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); x.setRequestHeader("Content-Type", "charset=latin1;text/plain"); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect index f6c8083875..02062e905b 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect @@ -1,11 +1,11 @@ PUT /testdocument.html HTTP/1.1 -Accept-Language: en-US -Content-Type: text/plain;charset=UTF-8 -Content-Length: 13 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +content-type: text/plain;charset=UTF-8 +content-length: 13 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} My Sent Data diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml index ee6a9caa06..345d486669 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml @@ -2,19 +2,16 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("PUT", url); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml index 003a912818..a378624803 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml @@ -2,23 +2,19 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; x.open("POST", url); x.setRequestHeader("Content-Type", "text/plain"); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } x.send("My Sent Data\n"); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect index bcd7e79fc1..c580208eca 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect @@ -1,8 +1,8 @@ OPTIONS / HTTP/1.1 -Content-Length: 0 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -Accept-Language: en-US,* -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +content-length: 0 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +accept-language: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect index 2d4f2c3146..3a7ae3730d 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect @@ -1,8 +1,8 @@ OPTIONS /testdocument.html HTTP/1.1 -Content-Length: 0 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -Accept-Language: en-US,* -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +content-length: 0 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +accept-language: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml index 336971c919..38fd3800d8 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml @@ -9,19 +9,14 @@ QtObject { Component.onCompleted: { var x = new XMLHttpRequest; x.open(reqType, url); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { - if (reqType == "HEAD" || reqType == "DELETE") - dataOK = (x.responseText == ""); - else - dataOK = (x.responseText == "QML Rocks!\n"); - } + let expect = reqType == "HEAD" || reqType == "DELETE" ? "" : "QML Rocks!\n"; + if (x.readyState == XMLHttpRequest.DONE) + dataOK = (x.responseText == expect); } x.send("Data To Ignore"); } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect index 7d96c00101..feca475b75 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect @@ -1,8 +1,8 @@ DELETE /testdocument.html HTTP/1.1 -Accept-Language: en-US -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect index 2270e7f3aa..299bfd4689 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect @@ -1,8 +1,8 @@ GET /testdocument.html HTTP/1.1 -Accept-Language: en-US -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect index 064cf1b6b7..fa757aeac3 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect @@ -1,8 +1,8 @@ HEAD /testdocument.html HTTP/1.1 -Accept-Language: en-US -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect index 55f7f7dceb..d6ff9854a5 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect @@ -1,13 +1,13 @@ PATCH /qqmlxmlhttprequest.cpp HTTP/1.1 -Accept-Language: en-US -If-Match: "ETagNumber" -Content-Type: application/example -Content-Length: 247 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +if-match: "ETagNumber" +content-type: application/example +content-length: 247 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} --- a/qqmlxmlhttprequest.cpp +++ b/qqmlxmlhttprequest.cpp @@ -15,4 +15,3 @@ Host: {{ServerHostUrl}} - } 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 index 35a629fc92..d7d57b445c 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 Canonical Limited and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 @@ -12,7 +12,6 @@ QtObject { 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 diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect index 003e54f33c..29d2d22030 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect @@ -1,10 +1,10 @@ GET /testdocument.html HTTP/1.1 -Accept-Language: en-US -Test-header: value -Test-header2: value,value2 -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +test-header: value +test-header2: value,value2 +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml index 4229584af2..1490d0f108 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml @@ -2,14 +2,11 @@ import QtQuick 2.0 QtObject { property string url - property bool dataOK: false Component.onCompleted: { var x = new XMLHttpRequest; - x.open("GET", url); - x.setRequestHeader("Accept-Language","en-US"); x.setRequestHeader("Test-header", "value"); x.setRequestHeader("Test-header2", "value"); @@ -25,5 +22,3 @@ QtObject { x.send(); } } - - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml index e03f73431a..015682b5d5 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml @@ -9,12 +9,10 @@ QtObject { var x = new XMLHttpRequest; x.open("GET", url); - x.setRequestHeader("Accept-Language","en-US"); - x.setRequestHeader("Test-header", "value"); - //Setting headers with just different cases - //will be treated as the same header, and accepted - //as the last setting. + + // Setting headers differing only in case will treat them as one header, + // and the values are joined with a comma. x.setRequestHeader("Test-hEADEr2", "value"); x.setRequestHeader("Test-header2", "value2"); diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml index cd047cf8dd..23a3c42fcc 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml @@ -21,11 +21,9 @@ QtObject { readyState = true; x.open("GET", url); - x.setRequestHeader("Accept-Language","en-US"); - x.setRequestHeader(header, "Value"); - if (x.readyState == XMLHttpRequest.OPENED) + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; try { @@ -45,14 +43,10 @@ QtObject { // Test to the end x.onreadystatechange = function() { - if (x.readyState == XMLHttpRequest.DONE) { + if (x.readyState == XMLHttpRequest.DONE) dataOK = (x.responseText == "QML Rocks!\n"); - } } - x.send() } } - - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml index 49888fdac8..2f37006de9 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml @@ -8,9 +8,7 @@ QtObject { Component.onCompleted: { var x = new XMLHttpRequest; - x.open("GET", url); - x.setRequestHeader("Accept-Language","en-US"); // Test to the end x.onreadystatechange = function() { @@ -29,4 +27,3 @@ QtObject { } } } - diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/status.expect b/tests/auto/qml/qqmlxmlhttprequest/data/status.expect index 2270e7f3aa..299bfd4689 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/status.expect +++ b/tests/auto/qml/qqmlxmlhttprequest/data/status.expect @@ -1,8 +1,8 @@ GET /testdocument.html HTTP/1.1 -Accept-Language: en-US -Connection: Keep-Alive{{Ignore}} -HTTP2-Settings: {{Ignore}} -Accept-Encoding: {{Ignore}} -User-Agent: Mozilla/5.0 -Host: {{ServerHostUrl}} +accept-language: {{Ignore}} +connection: Keep-Alive{{Ignore}} +http2-settings: {{Ignore}} +accept-encoding: {{Ignore}} +user-agent: Mozilla/5.0 +host: {{ServerHostUrl}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/status.qml b/tests/auto/qml/qqmlxmlhttprequest/data/status.qml index 94908f63c0..ed7268d6b3 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/status.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/status.qml @@ -30,8 +30,6 @@ QtObject { } x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - try { var a = x.status; } catch (e) { @@ -54,8 +52,6 @@ QtObject { dataOK = (x.responseText == "QML Rocks!\n"); x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - try { var a = x.status; } catch (e) { diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml b/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml index b47a0f1af0..cbc15b47f0 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml @@ -27,8 +27,6 @@ QtObject { } x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - try { var a = x.statusText; } catch (e) { @@ -51,8 +49,6 @@ QtObject { dataOK = (x.responseText == "QML Rocks!\n") && (x.response == "QML Rocks!\n"); x.open("GET", url); - x.setRequestHeader("Accept-Language", "en-US"); - try { var a = x.statusText; } catch (e) { diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/text.expect b/tests/auto/qml/qqmlxmlhttprequest/data/text.expect new file mode 100644 index 0000000000..2b4314273f --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/text.expect @@ -0,0 +1,6 @@ +GET /text.xml HTTP/1.1 +host: {{ServerHostUrl}} +connection: Keep-Alive{{Ignore}} +accept-encoding: {{Ignore}} +accept-language: {{Ignore}} +user-agent: {{Ignore}} diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 6517c58670..f908633193 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -75,6 +75,7 @@ private slots: void statusText_data(); void responseText(); void responseText_data(); + void responseURL(); void responseXML_invalid(); void invalidMethodUsage(); void redirects(); @@ -89,6 +90,8 @@ private slots: void sendFileRequestNoRead(); #endif + void overrideMime(); + // WebDAV void sendPropfind(); void sendPropfind_data(); @@ -637,8 +640,6 @@ void tst_qqmlxmlhttprequest::send_options() void tst_qqmlxmlhttprequest::send_options_data() { - if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates)) - QSKIP("Test is locale dependent"); QTest::addColumn<QString>("url_suffix"); QTest::addColumn<QString>("file_expected"); QTest::addColumn<QString>("file_qml"); @@ -863,8 +864,6 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_args() void tst_qqmlxmlhttprequest::getBinaryData() { - if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates)) - QSKIP("Test is locale dependent"); TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("receive_binary_data.expect"), @@ -877,15 +876,13 @@ void tst_qqmlxmlhttprequest::getBinaryData() object->setProperty("url", server.urlString("/gml_logo.png")); component.completeCreate(); - QFileInfo fileInfo("data/qml_logo.png"); + const QFileInfo fileInfo(testFile("qml_logo.png")); QTRY_COMPARE(object->property("readSize").toInt(), fileInfo.size()); QCOMPARE(object->property("status").toInt(), 200); } void tst_qqmlxmlhttprequest::getJsonData() { - if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates)) - QSKIP("Test is locale dependent"); TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("receive_json_data.expect"), @@ -1025,6 +1022,64 @@ void tst_qqmlxmlhttprequest::responseText_data() QTest::newRow("Internal server error") << testFileUrl("status.500.reply") << testFileUrl("testdocument.html") << "QML Rocks!\n"; } + +void tst_qqmlxmlhttprequest::responseURL() +{ + // 200 OK + { + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("status.expect"), + testFileUrl("status.200.reply"), + testFileUrl("testdocument.html"))); + + QQmlComponent component(engine.get(), testFileUrl("responseURL.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/testdocument.html")); + object->setProperty("expectedURL", server.urlString("/testdocument.html")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + } + + // 200 OK with the exclude fragment flag set + { + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("status.expect"), + testFileUrl("status.200.reply"), + testFileUrl("testdocument.html"))); + + QQmlComponent component(engine.get(), testFileUrl("responseURL.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/testdocument.html#fragment")); + object->setProperty("expectedURL", server.urlString("/testdocument.html")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + } + + // 302 Found + { + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + server.addRedirect("redirect.html", server.urlString("/redirecttarget.html")); + server.serveDirectory(dataDirectory()); + + QQmlComponent component(engine.get(), testFileUrl("responseURL.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/redirect.html")); + object->setProperty("expectedURL", server.urlString("/redirecttarget.html")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + } +} + + void tst_qqmlxmlhttprequest::nonUtf8() { QFETCH(QString, fileName); @@ -1076,7 +1131,7 @@ void tst_qqmlxmlhttprequest::doFileRequest(std::function<void(QObject *component QTemporaryFile writeFile; QTemporaryFile readFile; - writeFile.open(); + QVERIFY(writeFile.open()); writeFile.close(); QVERIFY(readFile.open()); @@ -1242,8 +1297,6 @@ void tst_qqmlxmlhttprequest::sendFileRequestNoRead() { void tst_qqmlxmlhttprequest::sendPropfind() { - if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates)) - QSKIP("Test is locale dependent"); const QString prefix = "WebDAV//"; QFETCH(QString, qml); @@ -1277,14 +1330,17 @@ void tst_qqmlxmlhttprequest::sendPropfind_data() QTest::addColumn<QString>("replyHeader"); QTest::addColumn<QString>("replyBody"); - QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). Get response with responseXML.") + QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). " + "Get response with responseXML.") << "sendPropfind.responseXML.qml" << "/file" << "propfind.file.expect" << "propfind.file.reply.header" << "propfind.file.reply.body"; - QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). Get response with response.") + QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). " + "Get response with response.") << "sendPropfind.response.qml" << "/file" << "propfind.file.expect" << "propfind.file.reply.header" << "propfind.file.reply.body"; QTest::newRow("Send PROPFIND \"allprop\" request for collection.") - << "sendPropfind.collection.allprop.qml" << "/container/" << "propfind.collection.allprop.expect" + << "sendPropfind.collection.allprop.qml" << "/container/" + << "propfind.collection.allprop.expect" << "propfind.file.reply.header" << "propfind.collection.allprop.reply.body"; } @@ -1323,7 +1379,6 @@ void tst_qqmlxmlhttprequest::redirects() QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); - object->setProperty("expectedText", ""); component.completeCreate(); QTRY_VERIFY(object->property("done").toBool()); @@ -1340,7 +1395,6 @@ void tst_qqmlxmlhttprequest::redirects() QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); - object->setProperty("expectedText", ""); component.completeCreate(); QTRY_VERIFY(object->property("done").toBool()); @@ -1357,7 +1411,6 @@ void tst_qqmlxmlhttprequest::redirects() QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); - object->setProperty("expectedText", ""); component.completeCreate(); for (int ii = 0; ii < 60; ++ii) { @@ -1489,6 +1542,26 @@ void tst_qqmlxmlhttprequest::stateChangeCallingContext() QTRY_VERIFY(object->property("success").toBool()); } +void tst_qqmlxmlhttprequest::overrideMime() +{ + // overrideMimeType.reply sets the Content-Type to text/plain + // overrideMimeType.qml overrides it to text/xml and checks the responseXML property. + + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("text.expect"), + testFileUrl("overrideMimeType.reply"), + testFileUrl("text.xml"))); + + QQmlComponent component(engine.get(), testFileUrl("overrideMimeType.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/text.xml")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); +} + QTEST_MAIN(tst_qqmlxmlhttprequest) #include "tst_qqmlxmlhttprequest.moc" diff --git a/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt b/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt index d0a8270faa..96ae64cd8d 100644 --- a/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt +++ b/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmlxmllistmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} data/*) diff --git a/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp b/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp index 0745853e02..bb6e59cb17 100644 --- a/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp +++ b/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtQmlXmlListModel/private/qqmlxmllistmodel_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -321,14 +321,12 @@ void tst_QQmlXmlListModel::headers() QTRY_COMPARE_WITH_TIMEOUT(qvariant_cast<QQmlXmlListModel::Status>(model->property("status")), QQmlXmlListModel::Error, 10000); - QVariantMap expectedHeaders; - expectedHeaders["Accept"] = "application/xml,*/*"; + QLatin1String expectedAcceptHeader = "application/xml,*/*"_L1; - QCOMPARE(factory.lastSentHeaders.size(), expectedHeaders.size()); - for (auto it = expectedHeaders.cbegin(), end = expectedHeaders.cend(); it != end; ++it) { - QVERIFY(factory.lastSentHeaders.contains(it.key())); - QCOMPARE(factory.lastSentHeaders[it.key()].toString(), it.value().toString()); - } + QCOMPARE(factory.lastSentHeaders.size(), 1); + QVariant acceptHeader = factory.lastSentHeaders["accept"]; + QVERIFY(acceptHeader.isValid()); + QCOMPARE(acceptHeader.toString(), expectedAcceptHeader); } void tst_QQmlXmlListModel::source() @@ -435,8 +433,8 @@ void tst_QQmlXmlListModel::reload() QVERIFY(model != nullptr); QTRY_COMPARE(model->rowCount(), 9); - QSignalSpy spyInsert(model.get(), SIGNAL(rowsInserted(QModelIndex, int, int))); - QSignalSpy spyRemove(model.get(), SIGNAL(rowsRemoved(QModelIndex, int, int))); + QSignalSpy spyInsert(model.get(), SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy spyRemove(model.get(), SIGNAL(rowsRemoved(QModelIndex,int,int))); QSignalSpy spyCount(model.get(), SIGNAL(countChanged())); // reload multiple times to test the xml query aborting QMetaObject::invokeMethod(model.get(), "reload"); diff --git a/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt b/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt index 37bd47de3a..39abeacbdf 100644 --- a/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt +++ b/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickfolderlistmodel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickfolderlistmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp index 6209e62a8f..f246e1af6d 100644 --- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp +++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtTest/QSignalSpy> #include <QtQml/qqmlengine.h> diff --git a/tests/auto/qml/qquickworkerscript/CMakeLists.txt b/tests/auto/qml/qquickworkerscript/CMakeLists.txt index 9ba3446659..e96fc74300 100644 --- a/tests/auto/qml/qquickworkerscript/CMakeLists.txt +++ b/tests/auto/qml/qquickworkerscript/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickworkerscript Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickworkerscript LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp index 966aac6aaf..e209c3cdde 100644 --- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp +++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtCore/qdebug.h> #include <QtCore/qtimer.h> @@ -102,21 +102,20 @@ void tst_QQuickWorkerScript::messaging() QFETCH(QVariant, value); QQmlComponent component(&m_engine, testFileUrl("worker.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.get()); const QMetaObject *mo = worker->metaObject(); - QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(); + QVariant response = mo->property(mo->indexOfProperty("response")).read(worker.get()).value<QVariant>(); if (response.userType() == qMetaTypeId<QJSValue>()) response = response.value<QJSValue>().toVariant(); QCOMPARE(response, value); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::messaging_data() @@ -142,29 +141,28 @@ void tst_QQuickWorkerScript::messaging_sendQObjectList() // js values. QQmlComponent component(&m_engine, testFileUrl("worker.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); QVariantList objects; for (int i=0; i<3; i++) objects << QVariant::fromValue(new QObject(this)); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(objects)))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, QVariant::fromValue(objects)))); + waitForEchoMessage(worker.get()); const QMetaObject *mo = worker->metaObject(); - QVariantList result = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariantList>(); + QVariantList result = mo->property(mo->indexOfProperty("response")).read(worker.get()).value<QVariantList>(); QCOMPARE(result, (QVariantList() << QVariant() << QVariant() << QVariant())); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::messaging_sendJsObject() { QQmlComponent component(&m_engine, testFileUrl("worker.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); // Properties are in alphabetical order to enable string-based comparison after // QVariant roundtrip, since the properties will be stored in a QVariantMap. @@ -175,26 +173,24 @@ void tst_QQuickWorkerScript::messaging_sendJsObject() map.insert("name", "zyz"); map.insert("spell power", 3101); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(map)))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, QVariant::fromValue(map)))); + waitForEchoMessage(worker.get()); QVariant result = QVariant::fromValue(false); - QVERIFY(QMetaObject::invokeMethod(worker, "compareLiteralResponse", Qt::DirectConnection, + QVERIFY(QMetaObject::invokeMethod(worker.get(), "compareLiteralResponse", Qt::DirectConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, jsObject))); QVERIFY(result.toBool()); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::messaging_sendExternalObject() { QQmlComponent component(&m_engine, testFileUrl("externalObjectWorker.qml")); - QObject *obj = component.create(); - QVERIFY(obj); - QMetaObject::invokeMethod(obj, "testExternalObject"); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj.get()); + QMetaObject::invokeMethod(obj.get(), "testExternalObject"); QTest::qWait(100); // shouldn't crash. - delete obj; } void tst_QQuickWorkerScript::script_with_pragma() @@ -202,35 +198,33 @@ void tst_QQuickWorkerScript::script_with_pragma() QVariant value(100); QQmlComponent component(&m_engine, testFileUrl("worker_pragma.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.get()); const QMetaObject *mo = worker->metaObject(); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), value); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).value<QVariant>(), value); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::script_included() { QQmlComponent component(&m_engine, testFileUrl("worker_include.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); QString value("Hello"); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.get()); const QMetaObject *mo = worker->metaObject(); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World"); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).toString(), value + " World"); qApp->processEvents(); - delete worker; } static QString qquickworkerscript_lastWarning; @@ -245,69 +239,65 @@ void tst_QQuickWorkerScript::scriptError_onLoad() QQmlComponent component(&m_engine, testFileUrl("worker_error_onLoad.qml")); QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); QTRY_COMPARE(qquickworkerscript_lastWarning, testFileUrl("script_error_onLoad.js").toString() + QLatin1String(":3:10: SyntaxError: Expected token `,'")); qInstallMessageHandler(previousMsgHandler); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::scriptError_onCall() { QQmlComponent component(&m_engine, testFileUrl("worker_error_onCall.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler); QVariant value; - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value))); QTRY_COMPARE(qquickworkerscript_lastWarning, testFileUrl("script_error_onCall.js").toString() + QLatin1String(":4: ReferenceError: getData is not defined")); qInstallMessageHandler(previousMsgHandler); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::script_function() { QQmlComponent component(&m_engine, testFileUrl("worker_function.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); QString value("Hello"); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.get()); const QMetaObject *mo = worker->metaObject(); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World"); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).toString(), value + " World"); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::script_var() { QQmlComponent component(&m_engine, testFileUrl("worker_var.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != nullptr); + std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) }; + QVERIFY(worker); QString value("Hello"); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); + QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.get()); const QMetaObject *mo = worker->metaObject(); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World"); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).toString(), value + " World"); qApp->processEvents(); - delete worker; } // Rapidly create and destroy worker scripts to test resources are being disposed @@ -317,9 +307,8 @@ void tst_QQuickWorkerScript::stressDispose() for (int ii = 0; ii < 100; ++ii) { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("stressDispose.qml")); - QObject *o = component.create(); - QVERIFY(o); - delete o; + std::unique_ptr<QObject> o { component.create() }; + QVERIFY(o.get()); } } diff --git a/tests/auto/qml/qrcqml/CMakeLists.txt b/tests/auto/qml/qrcqml/CMakeLists.txt index de081545cb..c793f1363c 100644 --- a/tests/auto/qml/qrcqml/CMakeLists.txt +++ b/tests/auto/qml/qrcqml/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qrcqml Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qrcqml LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qrcqml SOURCES tst_qrcqml.cpp diff --git a/tests/auto/qml/qrcqml/tst_qrcqml.cpp b/tests/auto/qml/qrcqml/tst_qrcqml.cpp index 5f77083c4e..a9be307c23 100644 --- a/tests/auto/qml/qrcqml/tst_qrcqml.cpp +++ b/tests/auto/qml/qrcqml/tst_qrcqml.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QObject> @@ -59,10 +59,9 @@ void tst_qrcqml::basicLoad() QQmlEngine e; QQmlComponent c(&e, QUrl(url)); QVERIFY(c.isReady()); - QObject* o = c.create(); - QVERIFY(o); + std::unique_ptr<QObject> o { c.create() }; + QVERIFY(o.get()); QCOMPARE(o->property("tokenProperty").toString(), token); - delete o; } void tst_qrcqml::qrcImport_data() @@ -88,10 +87,9 @@ void tst_qrcqml::qrcImport() e.addImportPath(importPath); QQmlComponent c(&e, QUrl("qrc:///importtest.qml")); QVERIFY(c.isReady()); - QObject *o = c.create(); - QVERIFY(o); + std::unique_ptr<QObject> o { c.create() }; + QVERIFY(o.get()); QCOMPARE(o->property("tokenProperty").toString(), token); - delete o; } QTEST_MAIN(tst_qrcqml) diff --git a/tests/auto/qml/qtqmlmodules/CMakeLists.txt b/tests/auto/qml/qtqmlmodules/CMakeLists.txt index 59033ae690..a6ae2ec9f7 100644 --- a/tests/auto/qml/qtqmlmodules/CMakeLists.txt +++ b/tests/auto/qml/qtqmlmodules/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qtqmlmodules Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtqmlmodules LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp index 88fd0e6bc0..62637c97ff 100644 --- a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp +++ b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Research in Motion. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QDebug> @@ -23,33 +23,27 @@ void tst_qtqmlmodules::baseTypes() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("base.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - - delete object; } void tst_qtqmlmodules::modelsTypes() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("models.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - - delete object; } void tst_qtqmlmodules::unavailableTypes() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("unavailable.qml")); - QObject *object = component.create(); - QVERIFY(object != nullptr); + std::unique_ptr<QObject> object { component.create() }; + QVERIFY(object); QVERIFY(object->property("success").toBool()); - - delete object; } QTEST_MAIN(tst_qtqmlmodules) diff --git a/tests/auto/qml/qv4assembler/CMakeLists.txt b/tests/auto/qml/qv4assembler/CMakeLists.txt index dfac16468a..8ae3c891d5 100644 --- a/tests/auto/qml/qv4assembler/CMakeLists.txt +++ b/tests/auto/qml/qv4assembler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qv4assembler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4assembler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qv4assembler/data/crash.qml b/tests/auto/qml/qv4assembler/data/crash.qml index ffae69b321..b083497a70 100644 --- a/tests/auto/qml/qv4assembler/data/crash.qml +++ b/tests/auto/qml/qv4assembler/data/crash.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.2 import Crash 1.0 diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp index 7e9e070fa6..4be0acd73e 100644 --- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp +++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #if QT_CONFIG(process) diff --git a/tests/auto/qml/qv4estable/CMakeLists.txt b/tests/auto/qml/qv4estable/CMakeLists.txt new file mode 100644 index 0000000000..01d2663a04 --- /dev/null +++ b/tests/auto/qml/qv4estable/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qv4estable Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4estable LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qv4estable + SOURCES + tst_qv4estable.cpp + LIBRARIES + Qt::Gui + Qt::Qml + Qt::QmlPrivate +) + +## Scopes: +##################################################################### diff --git a/tests/auto/qml/qv4estable/tst_qv4estable.cpp b/tests/auto/qml/qv4estable/tst_qv4estable.cpp new file mode 100644 index 0000000000..7d137ae7d2 --- /dev/null +++ b/tests/auto/qml/qv4estable/tst_qv4estable.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <qtest.h> +#include <private/qv4estable_p.h> + +class tst_qv4estable : public QObject +{ + Q_OBJECT + +private slots: + void checkRemoveAvoidsHeapBufferOverflow(); +}; + +// QTBUG-123999 +void tst_qv4estable::checkRemoveAvoidsHeapBufferOverflow() +{ + QV4::ESTable estable; + + // Fill the ESTable with values so it is at max capacity. + QCOMPARE_EQ(estable.m_capacity, 8U); + for (uint i = 0; i < estable.m_capacity; ++i) { + estable.set(QV4::Value::fromUInt32(i), QV4::Value::fromUInt32(i)); + } + // Our |m_keys| array should now contain eight values. + // > [v0, v1, v2, v3, v4, v5, v6, v7] + for (uint i = 0; i < estable.m_capacity; ++i) { + QVERIFY(estable.m_keys[i].sameValueZero(QV4::Value::fromUInt32(i))); + } + QCOMPARE_EQ(estable.m_capacity, 8U); + QCOMPARE_EQ(estable.m_size, 8U); + + // Remove the first item from the set to verify that asan does not trip. + // Relies on the CI platform propagating asan flag to all tests. + estable.remove(QV4::Value::fromUInt32(0)); +} + +QTEST_MAIN(tst_qv4estable) + +#include "tst_qv4estable.moc" diff --git a/tests/auto/qml/qv4identifiertable/CMakeLists.txt b/tests/auto/qml/qv4identifiertable/CMakeLists.txt index 5c785b0b74..c0350d6ef7 100644 --- a/tests/auto/qml/qv4identifiertable/CMakeLists.txt +++ b/tests/auto/qml/qv4identifiertable/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qv4identifiertable Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4identifiertable LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qv4identifiertable SOURCES tst_qv4identifiertable.cpp diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp index a3f5c4bb70..6222f20c10 100644 --- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp +++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2019 The Qt Company Ltd. // Copyright (C) 2016 basysKom GmbH. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> diff --git a/tests/auto/qml/qv4mm/CMakeLists.txt b/tests/auto/qml/qv4mm/CMakeLists.txt index bfafd5819c..7c8a52038e 100644 --- a/tests/auto/qml/qv4mm/CMakeLists.txt +++ b/tests/auto/qml/qv4mm/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qv4mm Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4mm LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/qml/qv4mm/data/createdestroy.qml b/tests/auto/qml/qv4mm/data/createdestroy.qml index fd8a8cb2a2..5cc689b1ea 100644 --- a/tests/auto/qml/qv4mm/data/createdestroy.qml +++ b/tests/auto/qml/qv4mm/data/createdestroy.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.2 diff --git a/tests/auto/qml/qv4mm/data/createobjects.qml b/tests/auto/qml/qv4mm/data/createobjects.qml index 4163d34d34..bd1897e04e 100644 --- a/tests/auto/qml/qv4mm/data/createobjects.qml +++ b/tests/auto/qml/qv4mm/data/createobjects.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml 2.2 diff --git a/tests/auto/qml/qv4mm/data/simpleObject.qml b/tests/auto/qml/qv4mm/data/simpleObject.qml new file mode 100644 index 0000000000..8fc36a40da --- /dev/null +++ b/tests/auto/qml/qv4mm/data/simpleObject.qml @@ -0,0 +1,3 @@ +import QtQml + +QtObject {} diff --git a/tests/auto/qml/qv4mm/data/storeLocal.qml b/tests/auto/qml/qv4mm/data/storeLocal.qml new file mode 100644 index 0000000000..8d48a8b23c --- /dev/null +++ b/tests/auto/qml/qv4mm/data/storeLocal.qml @@ -0,0 +1,34 @@ +import QtQml + +QtObject { + id: root + property bool wasNotMarkedBefore: false + property bool wasMarkedAfter: false + property int result: -2 + function f() { + let a = [1, 2, 3]; + function inner() { + a = [4, 5, 6]; + } + __forceJit(inner); + __setupGC(); + root.wasNotMarkedBefore = !__isMarked(a); + inner(); + root.wasMarkedAfter = __isMarked(a); + return a[0]; + } + Component.onCompleted: { + if (!__forceJit(f)) { + root.result = -1; + return; + } + if (f() !== 4) + root.result = 1; + else if (!wasNotMarkedBefore) + root.result = 2; + else if (!wasMarkedAfter) + root.result = 3; + else + root.result = 0; // success + } +} diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index e5f8951825..7714beb3c7 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 basysKom GmbH. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QQmlEngine> @@ -9,6 +9,15 @@ #include <private/qv4mm_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qjsvalue_p.h> +#include <private/qqmlengine_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4arraydata_p.h> +#include <private/qqmlcomponentattached_p.h> +#include <private/qv4mapobject_p.h> +#include <private/qv4setobject_p.h> +#if QT_CONFIG(qml_jit) +#include <private/qv4baselinejit_p.h> +#endif #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -23,22 +32,153 @@ public: private slots: void gcStats(); + void arrayDataWriteBarrierInteraction(); + void persistentValueMarking_data(); + void persistentValueMarking(); void multiWrappedQObjects(); void accessParentOnDestruction(); void cleanInternalClasses(); void createObjectsOnDestruction(); + void sharedInternalClassDataMarking(); + void gcTriggeredInOnDestroyed(); + void weakValuesAssignedAfterThePhaseThatShouldHandleWeakValues(); + void mapAndSetKeepValuesAlive(); + void jittedStoreLocalMarksValue(); }; tst_qv4mm::tst_qv4mm() : QQmlDataTest(QT_QMLTEST_DATADIR) { + QV4::ExecutionEngine engine; + QV4::Scope scope(engine.rootContext()); } void tst_qv4mm::gcStats() { QLoggingCategory::setFilterRules("qt.qml.gc.*=true"); QQmlEngine engine; - engine.collectGarbage(); + gc(engine); + QLoggingCategory::setFilterRules("qt.qml.gc.*=false"); +} + +void tst_qv4mm::arrayDataWriteBarrierInteraction() +{ + QV4::ExecutionEngine engine; + QCOMPARE(engine.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked); + engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection; + QV4::Heap::Object *unprotectedObject = engine.newObject(); + QV4::Scope scope(&engine); + QV4::ScopedArrayObject array(scope, engine.newArrayObject()); + constexpr int initialCapacity = 8; // compare qv4arraydata.cpp + for (int i = 0; i < initialCapacity; ++i) { + array->push_back(unprotectedObject->asReturnedValue()); + } + QVERIFY(!unprotectedObject->isMarked()); + engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; + + // initialize gc + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::MarkGlobalObject) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + + array->push_back(QV4::Value::fromUInt32(42)); + QVERIFY(!unprotectedObject->isMarked()); + // we should have pushed the new arraydata on the mark stack + // so if we call drain... + engine.memoryManager->markStack()->drain(); + // the unprotectedObject should have been marked + QVERIFY(unprotectedObject->isMarked()); +} + +enum PVSetOption { + CopyCtor, + ValueCtor, + ObjectCtor, + ReturnedValueCtor, + WeakValueAssign, + ObjectAssign, +}; + +void tst_qv4mm::persistentValueMarking_data() +{ + QTest::addColumn<PVSetOption>("setOption"); + + QTest::addRow("copy") << CopyCtor; + QTest::addRow("valueCtor") << ValueCtor; + QTest::addRow("ObjectCtor") << ObjectCtor; + QTest::addRow("ReturnedValueCtor") << ReturnedValueCtor; + QTest::addRow("WeakValueAssign") << WeakValueAssign; + QTest::addRow("ObjectAssign") << ObjectAssign; +} + +void tst_qv4mm::persistentValueMarking() +{ + QFETCH(PVSetOption, setOption); + QV4::ExecutionEngine engine; + QV4::PersistentValue persistentOrigin; // used for copy ctor + QV4::Heap::Object *unprotectedObject = engine.newObject(); + { + QV4::Scope scope(engine.rootContext()); + QV4::ScopedObject object {scope, unprotectedObject}; + persistentOrigin.set(&engine, object); + QVERIFY(!unprotectedObject->isMarked()); + } + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::MarkGlobalObject) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QVERIFY(engine.isGCOngoing); + QVERIFY(!unprotectedObject->isMarked()); + switch (setOption) { + case CopyCtor: { + QV4::PersistentValue persistentCopy(persistentOrigin); + QVERIFY(unprotectedObject->isMarked()); + break; + } + case ValueCtor: { + QV4::Value val = QV4::Value::fromHeapObject(unprotectedObject); + QV4::PersistentValue persistent(&engine, val); + QVERIFY(unprotectedObject->isMarked()); + break; + } + case ObjectCtor: { + QV4::Scope scope(&engine); + QV4::ScopedObject o(scope, unprotectedObject); + // scoped object without scan shouldn't result in marking + QVERIFY(!unprotectedObject->isMarked()); + QV4::PersistentValue persistent(&engine, o.getPointer()); + QVERIFY(unprotectedObject->isMarked()); + break; + } + case ReturnedValueCtor: { + QV4::PersistentValue persistent(&engine, unprotectedObject->asReturnedValue()); + QVERIFY(unprotectedObject->isMarked()); + break; + } + case WeakValueAssign: { + QV4::WeakValue wv; + wv.set(&engine, unprotectedObject); + QVERIFY(!unprotectedObject->isMarked()); + QV4::PersistentValue persistent; + persistent = wv; + break; + } + case ObjectAssign: { + QV4::Scope scope(&engine); + QV4::ScopedObject o(scope, unprotectedObject); + // scoped object without scan shouldn't result in marking + QVERIFY(!unprotectedObject->isMarked()); + QV4::PersistentValue persistent; + persistent = o; + QVERIFY(unprotectedObject->isMarked()); + break; + } + } } void tst_qv4mm::multiWrappedQObjects() @@ -48,7 +188,7 @@ void tst_qv4mm::multiWrappedQObjects() { QObject object; for (int i = 0; i < 10; ++i) - QV4::QObjectWrapper::wrap(i % 2 ? &engine1 : &engine2, &object); + QV4::QObjectWrapper::ensureWrapper(i % 2 ? &engine1 : &engine2, &object); QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); @@ -62,27 +202,27 @@ void tst_qv4mm::multiWrappedQObjects() // The additional WeakValue from m_multiplyWrappedQObjects hasn't been moved // to m_pendingFreedObjectWrapperValue yet. It's still alive after all. - engine1.memoryManager->runGC(); + gc(engine1); QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1); // engine2 doesn't own the object as engine1 was the first to wrap it above. // Therefore, no effect here. - engine2.memoryManager->runGC(); + gc(engine2); QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); } // Clears m_pendingFreedObjectWrapperValue. Now it's really dead. - engine1.memoryManager->runGC(); + gc(engine1); QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); - engine2.memoryManager->runGC(); + gc(engine2); QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); } void tst_qv4mm::accessParentOnDestruction() { - QLoggingCategory::setFilterRules("qt.qml.gc.*=false"); QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("createdestroy.qml")); std::unique_ptr<QObject> obj(component.create()); QVERIFY(obj); @@ -91,6 +231,9 @@ void tst_qv4mm::accessParentOnDestruction() QTRY_VERIFY(!timer->property("running").toBool()); QCOMPARE(obj->property("iterations").toInt(), 100); QCOMPARE(obj->property("creations").toInt(), 100); + gc(engine); // ensure incremental gc has finished, and collected all objects + // TODO: investigaet whether we really need two gc rounds for incremental gc + gc(engine); // ensure incremental gc has finished, and collected all objects QCOMPARE(obj->property("destructions").toInt(), 100); } @@ -181,7 +324,11 @@ void tst_qv4mm::cleanInternalClasses() } // Make sure that all dangling ICs are actually gone. - scope.engine->memoryManager->runGC(); + gc(engine); + // NOTE: If we allocate new ICs during gc (potentially triggered on alloc), + // then they will survive the previous gc call + // run gc again to ensure that a full gc cycle happens + gc(engine); // Now the GC has removed the ICs we originally added by adding properties. QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr); @@ -197,8 +344,8 @@ void tst_qv4mm::cleanInternalClasses() void tst_qv4mm::createObjectsOnDestruction() { - QLoggingCategory::setFilterRules("qt.qml.gc.*=false"); QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("createobjects.qml")); std::unique_ptr<QObject> obj(component.create()); QVERIFY(obj); @@ -206,6 +353,383 @@ void tst_qv4mm::createObjectsOnDestruction() QCOMPARE(obj->property("ok").toBool(), true); } +void tst_qv4mm::sharedInternalClassDataMarking() +{ + QV4::ExecutionEngine engine; + QV4::Scope scope(engine.rootContext()); + QV4::ScopedObject object(scope, engine.newObject()); + QVERIFY(!engine.memoryManager->gcBlocked); + // no scoped classes, as that would defeat the point of the test + // we block the gc instead so that the allocation can't trigger the gc + engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection; + QV4::Heap::String *s = engine.newString(QString::fromLatin1("test")); + QV4::PropertyKey id = engine.identifierTable->asPropertyKeyImpl(s); + engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; + QVERIFY(!id.asStringOrSymbol()->isMarked()); + + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::MarkGlobalObject) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + + // simulate partial marking caused by drain due mark stack running out of space + // and running out of time during drain phase for complete marking + // the last part is necessary for us to find not-already marked name/value pair to put into + // the object + + QVERIFY(engine.memoryManager->markStack()->isEmpty()); + QVERIFY(!id.asStringOrSymbol()->isMarked()); + { + + // for simplcity's sake we create a new PropertyKey - if gc were actually ongoing that would + // already mark it. In practice we would need to retrieve an existing one from an unmarked + // object, and then make that object unreachable afterwards. + object->put(id, QV4::Value::fromUInt32(42)); + engine.memoryManager->markStack()->drain(); + QVERIFY(id.asStringOrSymbol()->isMarked()); + } + gc(engine); + // sanity check that we still can lookup the value + QV4::ScopedString s2(scope, engine.newString(QString::fromLatin1("test"))); + auto val = QV4::Value::fromReturnedValue(object->get(s2->toPropertyKey())); + QCOMPARE(val.toUInt32(), 42u); +} + +void tst_qv4mm::gcTriggeredInOnDestroyed() +{ + QQmlEngine engine; + QV4::ExecutionEngine &v4 = *engine.handle(); + + QPointer<QObject> testObject = new QObject; // unparented, will be deleted + auto cleanup = qScopeGuard([&]() { + if (testObject) + testObject->deleteLater(); + }); + + QQmlComponent component(&engine, testFileUrl("simpleObject.qml")); + auto toBeCollected = component.create(); + QVERIFY(toBeCollected); + QJSEngine::setObjectOwnership(toBeCollected, QJSEngine::JavaScriptOwnership); + QV4::QObjectWrapper::ensureWrapper(&v4, toBeCollected); + QVERIFY(qmlEngine(toBeCollected)); + QQmlComponentAttached *attached = QQmlComponent::qmlAttachedProperties(toBeCollected); + QVERIFY(attached); + + + QV4::Scope scope(v4.rootContext()); + QCOMPARE(v4.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked); + + + + // let the gc run up to CallDestroyObjects + auto sm = v4.memoryManager->gcStateMachine.get(); + sm->reset(); + v4.memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked; + while (sm->state != QV4::GCState::CallDestroyObjects && sm->state != QV4::GCState::Invalid) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QCOMPARE(sm->state, QV4::GCState::CallDestroyObjects); + + QV4::ScopedValue val(scope); + bool calledOnDestroyed = false; + auto con = connect(attached, &QQmlComponentAttached::destruction, this, [&]() { + calledOnDestroyed = true; + // we trigger uncommon code paths: + // create ObjectWrapper in destroyed hadnler + auto ddata = QQmlData::get(testObject.get(), false); + QVERIFY(!ddata); // we don't have ddata yet (otherwise we'd already have an object wrapper) + val = QV4::QObjectWrapper::wrap(&v4, testObject.get()); + QJSEngine::setObjectOwnership(testObject, QJSEngine::JavaScriptOwnership); + + // and also try to trigger a force gc completion + bool gcComplete = v4.memoryManager->tryForceGCCompletion(); + QVERIFY(!gcComplete); + }); + while (!calledOnDestroyed && sm->state != QV4::GCState::Invalid) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QVERIFY(!QTest::currentTestFailed()); + QObject::disconnect(con); + QVERIFY(calledOnDestroyed); + + bool gcComplete = v4.memoryManager->tryForceGCCompletion(); + QVERIFY(gcComplete); + val = QV4::Value::undefinedValue(); // no longer keep a reference on the stack + QCOMPARE(sm->state, QV4::GCState::Invalid); + QVERIFY(testObject); // must not have be deleted, referenced by val + + gc(v4); // run another gc cycle + QVERIFY(!testObject); // now collcted by gc +} +void tst_qv4mm::weakValuesAssignedAfterThePhaseThatShouldHandleWeakValues() +{ + QObject testObject; + QV4::ExecutionEngine v4; + + QCOMPARE(v4.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked); + + + + // let the gc run up to CallDestroyObjects + auto sm = v4.memoryManager->gcStateMachine.get(); + sm->reset(); + v4.memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked; + + + // run just before the sweeping face + while (sm->state != QV4::GCState::DoSweep && sm->state != QV4::GCState::Invalid) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QCOMPARE(sm->state, QV4::GCState::DoSweep); + + { + // simulate code accessing the object wrapper for an object + QV4::Scope scope(v4.rootContext()); + QV4::ScopedValue value(scope); + value = QV4::QObjectWrapper::wrap(&v4, &testObject); + // let it go out of scope before any stack re-scanning could happen + } + + bool gcComplete = v4.memoryManager->tryForceGCCompletion(); + QVERIFY(gcComplete); + + auto ddata = QQmlData::get(&testObject); + QVERIFY(ddata); + if (ddata->jsWrapper.isUndefined()) { + // it's in principle valid for the wrapper to be reset, though the current + // implementation doesn't do it, and it requires some care + qWarning("Double-check the handling of weak values and object wrappers in the gc"); + return; + } + QVERIFY(ddata->jsWrapper.valueRef()->heapObject()->inUse()); +} + +void tst_qv4mm::mapAndSetKeepValuesAlive() +{ + { + QJSEngine jsEngine; + QV4::ExecutionEngine &engine = *jsEngine.handle(); + + QV4::Scope scope(&engine); + auto map = jsEngine.evaluate("new Map()"); + QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object + QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + + QVERIFY(!engine.memoryManager->gcBlocked); + // no scoped classes, as that would defeat the point of the test + // we block the gc instead so that the allocation can't trigger the gc + engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection; + QV4::Heap::String *key = engine.newString(QString::fromLatin1("key")); + QV4::Heap::String *value = engine.newString(QString::fromLatin1("value")); + QV4::Value values[2] = { QV4::Value::fromHeapObject(key), QV4::Value::fromHeapObject(value) }; + engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; + QVERIFY(!key->isMarked()); + QVERIFY(!value->isMarked()); + + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::HandleQObjectWrappers) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QV4::MapPrototype::method_set(afunction.getPointer(), &thisObject, values, 2); + + // check that we can still insert primitve values - they don't get marked + // but they also should not casue any corrpution - note that a weak map + // only accepts object keys + values[0] = QV4::Value::fromInt32(12); + values[1] = QV4::Value::fromInt32(13); + QV4::MapPrototype::method_set(afunction.getPointer(), &thisObject, values, 2); + + QVERIFY(key->isMarked()); + QVERIFY(value->isMarked()); + bool gcComplete = engine.memoryManager->tryForceGCCompletion(); + QVERIFY(gcComplete); + QVERIFY(key->inUse()); + QVERIFY(value->inUse()); + gc(engine); + QCOMPARE(map.property("size").toInt(), 2); + } + { + QJSEngine jsEngine; + QV4::ExecutionEngine &engine = *jsEngine.handle(); + + QV4::Scope scope(&engine); + auto map = jsEngine.evaluate("new WeakMap()"); + QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object + QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + + QVERIFY(!engine.memoryManager->gcBlocked); + // no scoped classes, as that would defeat the point of the test + // we block the gc instead so that the allocation can't trigger the gc + engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection; + QV4::Heap::Object *key = engine.newObject(); + QV4::Heap::String *value = engine.newString(QString::fromLatin1("value")); + QV4::Value values[2] = { QV4::Value::fromHeapObject(key), QV4::Value::fromHeapObject(value) }; + engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; + QVERIFY(!key->isMarked()); + QVERIFY(!value->isMarked()); + + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::HandleQObjectWrappers) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QV4::WeakMapPrototype::method_set(afunction.getPointer(), &thisObject, values, 2); + QVERIFY(!engine.hasException); + QVERIFY(key->isMarked()); + QVERIFY(value->isMarked()); + bool gcComplete = engine.memoryManager->tryForceGCCompletion(); + QVERIFY(gcComplete); + QVERIFY(key->inUse()); + QVERIFY(value->inUse()); + gc(engine); + QCOMPARE(map.property("size").toInt(), 0); + } + { + QJSEngine jsEngine; + QV4::ExecutionEngine &engine = *jsEngine.handle(); + + QV4::Scope scope(&engine); + auto map = jsEngine.evaluate("new Set()"); + QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object + QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + + QVERIFY(!engine.memoryManager->gcBlocked); + // no scoped classes, as that would defeat the point of the test + // we block the gc instead so that the allocation can't trigger the gc + engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection; + QV4::Heap::Object *key = engine.newObject(); + QV4::Value values[1] = { QV4::Value::fromHeapObject(key) }; + engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; + QVERIFY(!key->isMarked()); + + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::HandleQObjectWrappers) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QV4::SetPrototype::method_add(afunction.getPointer(), &thisObject, values, 1); + values[0] = QV4::Value::fromInt32(13); + QV4::SetPrototype::method_add(afunction.getPointer(), &thisObject, values, 1); + QVERIFY(!engine.hasException); + QVERIFY(key->isMarked()); + bool gcComplete = engine.memoryManager->tryForceGCCompletion(); + QVERIFY(gcComplete); + QVERIFY(key->inUse()); + gc(engine); + QCOMPARE(map.property("size").toInt(), 2); + } + { + QJSEngine jsEngine; + QV4::ExecutionEngine &engine = *jsEngine.handle(); + + QV4::Scope scope(&engine); + auto map = jsEngine.evaluate("new WeakSet()"); + QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object + QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + + QVERIFY(!engine.memoryManager->gcBlocked); + // no scoped classes, as that would defeat the point of the test + // we block the gc instead so that the allocation can't trigger the gc + engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection; + QV4::Heap::Object *key = engine.newObject(); + QV4::Value values[1] = { QV4::Value::fromHeapObject(key) }; + engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; + QVERIFY(!key->isMarked()); + + auto sm = engine.memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::HandleQObjectWrappers) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + QV4::WeakSetPrototype::method_add(afunction.getPointer(), &thisObject, values, 1); + QVERIFY(!engine.hasException); + QVERIFY(key->isMarked()); + bool gcComplete = engine.memoryManager->tryForceGCCompletion(); + QVERIFY(gcComplete); + QVERIFY(key->inUse()); + gc(engine); + QCOMPARE(map.property("size").toInt(), 0); + } +} + +QV4::ReturnedValue method_force_jit(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc) +{ +#if QT_CONFIG(qml_jit) + auto *v4 =b->engine(); + + Q_ASSERT(argc == 1); + QV4::Scope scope(v4); + QV4::Scoped<QV4::JavaScriptFunctionObject> functionObject(scope, argv[0]); + auto *func = static_cast<QV4::Heap::JavaScriptFunctionObject *>(functionObject->heapObject())->function; + Q_ASSERT(func); + func->interpreterCallCount = std::numeric_limits<int>::max(); + if (!v4->canJIT(func)) + return QV4::StaticValue::fromBoolean(false).asReturnedValue(); + QV4::JIT::BaselineJIT(func).generate(); + return QV4::StaticValue::fromBoolean(true).asReturnedValue(); +#else + return QV4::StaticValue::fromBoolean(false).asReturnedValue(); +#endif +} + +QV4::ReturnedValue method_setup_gc_for_test(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int) +{ + auto *v4 =b->engine(); + auto *mm = v4->memoryManager; + mm->runFullGC(); + + auto sm = v4->memoryManager->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::MarkGlobalObject) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + + return QV4::Encode::undefined(); +} + +QV4::ReturnedValue method_is_marked(const QV4::FunctionObject *, const QV4::Value *, const QV4::Value *argv, int argc) +{ + Q_ASSERT(argc == 1); + + auto h = argv[0].heapObject(); + Q_ASSERT(h); + return QV4::Encode(h->isMarked()); +} + +void tst_qv4mm::jittedStoreLocalMarksValue() +{ + QQmlEngine engine; + + auto *v4 = engine.handle(); + auto globalObject = v4->globalObject; + globalObject->defineDefaultProperty(QStringLiteral("__setupGC"), method_setup_gc_for_test); + globalObject->defineDefaultProperty(QStringLiteral("__forceJit"), method_force_jit); + globalObject->defineDefaultProperty(QStringLiteral("__isMarked"), method_is_marked); + + QQmlComponent comp(&engine, testFileUrl("storeLocal.qml")); + QVERIFY(comp.isReady()); + std::unique_ptr<QObject> root {comp.create()}; + + QVERIFY(root); + bool ok = false; + int result = root->property("result").toInt(&ok); + QVERIFY(ok); + if (result == -1) + QSKIP("Could not run JIT"); + QCOMPARE(result, 0); +} + QTEST_MAIN(tst_qv4mm) #include "tst_qv4mm.moc" diff --git a/tests/auto/qml/qv4regexp/CMakeLists.txt b/tests/auto/qml/qv4regexp/CMakeLists.txt index bdaf5e52e4..6f2c47c4b2 100644 --- a/tests/auto/qml/qv4regexp/CMakeLists.txt +++ b/tests/auto/qml/qv4regexp/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qv4regexp Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4regexp LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qv4regexp SOURCES tst_qv4regexp.cpp diff --git a/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp index 534fa53956..99a91a9b0f 100644 --- a/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp +++ b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtQml/qjsengine.h> diff --git a/tests/auto/qml/qv4urlobject/CMakeLists.txt b/tests/auto/qml/qv4urlobject/CMakeLists.txt new file mode 100644 index 0000000000..f255413d84 --- /dev/null +++ b/tests/auto/qml/qv4urlobject/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Generated from qv4urlobject.pro. + +##################################################################### +## tst_qv4urlobject Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qv4urlobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qv4urlobject + SOURCES + tst_qv4urlobject.cpp + LIBRARIES + Qt::Gui + Qt::Qml + Qt::QmlPrivate +) + +## Scopes: +##################################################################### diff --git a/tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp b/tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp new file mode 100644 index 0000000000..992d1ef808 --- /dev/null +++ b/tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp @@ -0,0 +1,153 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <qtest.h> +#include <QtQml/qjsengine.h> + +class tst_urlobject : public QObject +{ + Q_OBJECT + +private slots: + void searchParams_set(); + void searchParams_nullUrlPointer(); + void urlObject_search(); + void urlObject_search_data(); + void urlObject_href(); + void urlObject_href_data(); +}; + +void tst_urlobject::searchParams_set() +{ + QJSEngine engine; + QJSValue result = + engine.evaluate(QLatin1String("var url = new URL(\"http://www.google.com/search\");" + "url.href")); + QVERIFY(!result.isError()); + QCOMPARE(result.toString(), "http://www.google.com/search"); + + result = engine.evaluate(QLatin1String("url.toString()")); + QVERIFY(!result.isError()); + QCOMPARE(result.toString(), "http://www.google.com/search"); + + result = engine.evaluate(QLatin1String("url.searchParams.set(\"q\", \"value\");" + "url.href;")); + QVERIFY(!result.isError()); + QCOMPARE(result.toString(), "http://www.google.com/search?q=value"); + + result = engine.evaluate(QLatin1String("url.toString()")); + QVERIFY(!result.isError()); + QCOMPARE(result.toString(), "http://www.google.com/search?q=value"); + + result = engine.evaluate(QLatin1String("url.searchParams.set(\"t\", \"otherValue\");" + "url.href;")); + QVERIFY(!result.isError()); + QCOMPARE(result.toString(), "http://www.google.com/search?q=value&t=otherValue"); + + result = engine.evaluate(QLatin1String("url.toString()")); + QVERIFY(!result.isError()); + QCOMPARE(result.toString(), "http://www.google.com/search?q=value&t=otherValue"); +} + +void tst_urlobject::searchParams_nullUrlPointer() +{ + QJSEngine engine; + QJSValue result = engine.evaluate(QLatin1String("let params = new URLSearchParams();" + "params.set(\"foo\", \"bar\");")); + QVERIFY(!result.isError()); +} + +void tst_urlobject::urlObject_search() +{ + QFETCH(QString, test); + QFETCH(QString, expected); + + QJSEngine engine; + + QCOMPARE(engine.evaluate(test).toString(), expected); +} + +void tst_urlobject::urlObject_search_data() +{ + QTest::addColumn<QString>("test"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("base case") + << "var url = new URL(\"http://www.google.com/search?q=123\");" + "url.search" + << "?q=123"; + QTest::newRow("space") + << "var url = new URL(\"http://www.google.com/search?a=b ~\");" + "url.search" + << "?a=b%20~"; + QTest::newRow("empty search") + << "var url = new URL(\"http://www.google.com/search?\");" + "url.search" + << ""; + QTest::newRow("no search") + << "var url = new URL(\"http://www.google.com/search\");" + "url.search" + << ""; + QTest::newRow("Question mark") + // the embedded ""'s break trigraph sequences: + << "var url = new URL(\"http://www.google.com/search?""?=?\");" + "url.search" + << "?""?=?"; + QTest::newRow("equal sign") + << "var url = new URL(\"http://www.google.com/search?a==&b=!\");" + "url.search" + << "?a==&b=!"; + QTest::newRow("percent sign") + << "var url = new URL(\"http://www.google.com/search?a=%20\");" + "url.search" + << "?a=%20"; + QTest::newRow("multiple key-value pairs") + << "var url = new URL(\"http://www.google.com/search?a=b&c=d\");" + "url.search" + << "?a=b&c=d"; + QTest::newRow("unreserved") + << "var url = new URL(\"http://www.google.com/search?a=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~\");" + "url.search" + << "?a=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; + QTest::newRow("reserved + illegal") + << "var url = new URL(\"http://google.com/search/?a=!*();:@&=+$,/?#[]\");" + "url.search" + << "?a=!*();:@&=+$,/?"; + QTest::newRow("unicode (U+327D)") + << "var url = new URL(\"http://google.com/search/?a=㉽\");" + "url.search" + << "?a=%E3%89%BD"; + QTest::newRow("backslash") + // The JS string in the C++ source ends in 4 backslashes. + // The C++ compiler turns that into 2 backslashes. + // QV4 receives source code containing a string literal ending in two backslashes. + // The resulting JS string ends in a single backslash. + << "var url = new URL('http://google.com/search/?q=\\\\');" + "url.search" + << "?q=\\"; +} + +void tst_urlobject::urlObject_href() +{ + QFETCH(QString, test); + QFETCH(QString, expected); + + QJSEngine engine; + + QCOMPARE(engine.evaluate(test).toString(), expected); +} + +void tst_urlobject::urlObject_href_data() +{ + QTest::addColumn<QString>("test"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("QTBUG-110454") + << "var url = new URL(\"https://example.com/?a=b ~\");" + "url.href" + << "https://example.com/?a=b%20~"; +} + +QTEST_MAIN(tst_urlobject) + +#include "tst_qv4urlobject.moc" diff --git a/tests/auto/qml/qwidgetsinqml/CMakeLists.txt b/tests/auto/qml/qwidgetsinqml/CMakeLists.txt index 4daa98889a..d2374997f2 100644 --- a/tests/auto/qml/qwidgetsinqml/CMakeLists.txt +++ b/tests/auto/qml/qwidgetsinqml/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qwidgetsinqml Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetsinqml LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetsinqml SOURCES tst_qwidgetsinqml.cpp @@ -15,6 +21,8 @@ qt_internal_add_test(tst_qwidgetsinqml Qt::GuiPrivate Qt::Qml Qt::Widgets + Qt::QuickTestUtilsPrivate + TESTDATA "dummy_imports.qml" ) ## Scopes: diff --git a/tests/auto/qml/qwidgetsinqml/dummy_imports.qml b/tests/auto/qml/qwidgetsinqml/dummy_imports.qml new file mode 100644 index 0000000000..afe2b33adf --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/dummy_imports.qml @@ -0,0 +1,7 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQml + +QtObject { } diff --git a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp index 15236c20d8..12058fbba2 100644 --- a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp +++ b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp @@ -1,10 +1,11 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QQmlEngine> #include <QtQml> #include <QWidget> +#include <QtQuickTestUtils/private/qmlutils_p.h> class tst_QWidgetsInQml : public QObject { @@ -20,13 +21,6 @@ private slots: void widgetAsDefaultPropertyKeptDuringCreation(); }; -static void gc(QQmlEngine &engine) -{ - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - QCoreApplication::processEvents(); -} - // Like QtObject, but with default property class QObjectContainer : public QObject { @@ -40,7 +34,8 @@ public: {} QQmlListProperty<QObject> data() { - return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear); + return QQmlListProperty<QObject>( + this, nullptr, children_append, children_count, children_at, children_clear); } static void children_append(QQmlListProperty<QObject> *prop, QObject *o) @@ -58,12 +53,12 @@ public: } } - static int children_count(QQmlListProperty<QObject> *prop) + static qsizetype children_count(QQmlListProperty<QObject> *prop) { return static_cast<QObjectContainer*>(prop->object)->dataChildren.count(); } - static QObject *children_at(QQmlListProperty<QObject> *prop, int index) + static QObject *children_at(QQmlListProperty<QObject> *prop, qsizetype index) { return static_cast<QObjectContainer*>(prop->object)->dataChildren.at(index); } @@ -71,7 +66,7 @@ public: static void children_clear(QQmlListProperty<QObject> *prop) { QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); - foreach (QObject *c, that->dataChildren) + for (QObject *c : std::as_const(that->dataChildren)) QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); that->dataChildren.clear(); } diff --git a/tests/auto/qml/registrationmacros/CMakeLists.txt b/tests/auto/qml/registrationmacros/CMakeLists.txt index 3f2b7411ac..4cae044adf 100644 --- a/tests/auto/qml/registrationmacros/CMakeLists.txt +++ b/tests/auto/qml/registrationmacros/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_registrationmacros LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_registrationmacros SOURCES tst_registrationmacros.cpp diff --git a/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp b/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp index 77798af24f..a94e045277 100644 --- a/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp +++ b/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QObject> diff --git a/tests/auto/qml/registrationmacros/types.cpp b/tests/auto/qml/registrationmacros/types.cpp index 4ca801e7f7..57d565c525 100644 --- a/tests/auto/qml/registrationmacros/types.cpp +++ b/tests/auto/qml/registrationmacros/types.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "types.h" bool Test::check() const diff --git a/tests/auto/qml/registrationmacros/types.h b/tests/auto/qml/registrationmacros/types.h index 3c365cc2fd..5579edd2d3 100644 --- a/tests/auto/qml/registrationmacros/types.h +++ b/tests/auto/qml/registrationmacros/types.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TYPES_H #define TYPES_H diff --git a/tests/auto/qml/v4misc/CMakeLists.txt b/tests/auto/qml/v4misc/CMakeLists.txt index 81ae948cf1..1390fe9e0f 100644 --- a/tests/auto/qml/v4misc/CMakeLists.txt +++ b/tests/auto/qml/v4misc/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_v4misc Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_v4misc LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_v4misc SOURCES tst_v4misc.cpp diff --git a/tests/auto/qml/v4misc/tst_v4misc.cpp b/tests/auto/qml/v4misc/tst_v4misc.cpp index 201bdb2f95..956b87d5f2 100644 --- a/tests/auto/qml/v4misc/tst_v4misc.cpp +++ b/tests/auto/qml/v4misc/tst_v4misc.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <private/qv4instr_moth_p.h> |