summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc10
-rw-r--r--tests/auto/qml/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp5
-rw-r--r--tests/auto/qml/qmlcppcodegen/CMakeLists.txt11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/AccessModelMethodsFromOutside.qml48
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/BaseMember.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/BindingExpression.qml59
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt134
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Dummy.qml44
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Enums.qml29
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Panel.qml156
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/Keyframe.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/KeyframeGroup.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml175
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml152
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/Timeline.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/built-with-Qt_Large.pngbin0 -> 49186 bytes
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/RootWithoutId.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Test.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/TestCase.qml1987
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/aliasLookup.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/anchorsFill.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/array.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/asCast.qml50
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/attachedBaseEnum.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/bindToValueType.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp87
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.h117
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/callContextPropertyLookupResult.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/childobject.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/colorAsVariant.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/colorString.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/componentReturnType.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/compositeTypeMethod.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/compositesingleton.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/construct.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/contextParam.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversions.qml130
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversions2.qml93
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h63
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/curlygrouped.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/deadShoeSize.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dialog.qml21
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dynamicscene.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumInvalid.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumScope.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumsInOtherObject.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumsUser.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/equalsUndefined.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/excessiveParameters.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/extendedTypes.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/failures.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/fileDialog.qml73
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/funcWithParams.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/globals.qml24
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/idAccess.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/immediateQuit.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/imports/Module/NotCompiled.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/imports/Module/qmldir2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/Globals.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/qmldir2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/importsFromImportPath.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/intEnumCompare.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/intOverflow.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/interactive.qml68
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/interceptor.qml53
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsMathObject.qml64
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsimport.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsmoduleimport.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/layouts.qml131
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/library.js4
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listIndices.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listlength.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/math.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/methods.qml56
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/modulePrefix.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/noQQmlData.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nonNotifyable.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/noscope.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullAccess.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h77
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/outOfBounds.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/overriddenMember.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ownProperty.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/page.qml41
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/parentProp.qml25
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/person.cpp59
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/person.h61
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/pressAndHoldButton.qml38
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/propertyChanges.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/registerelimination.qml44
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/scopeVsObject.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/script.js6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/script.mjs4
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml122
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shifts.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/signal.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/signalHandler.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/specificParent.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/stringLength.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/testlogger.js76
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/text.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/unknownParameter.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/unusedAttached.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/urlString.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeProperty.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/voidfunction.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp1623
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp116
111 files changed, 6763 insertions, 34 deletions
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index f7690e89d6..49d85f8f42 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -259,6 +259,16 @@ set_target_properties(someTarget PROPERTIES
This explicitly selects qmlcachegen as the program to be used, even if
better alternatives are available.
+Furthermore, you can pass extra arguments to qmlcachegen, by setting the
+\c QT_QMLCACHEGEN_ARGUMENTS option. In particular, the \c --only-bytecode
+option will turn off compilation of QML script code to C++. For example:
+
+\badcode
+set_target_properties(someTarget PROPERTIES
+ QT_QMLCACHEGEN_ARGUMENTS "--only-bytecode"
+)
+\endcode
+
\target qmllint-auto
\section2 Linting QML sources
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 0a43b809a7..51f824c49d 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -70,6 +70,7 @@ if(QT_FEATURE_private_tests)
# Calls qt6_target_qml_sources() directly, which needs CMake 3.19+
add_subdirectory(qmlcachegen)
endif()
+ add_subdirectory(qmlcppcodegen)
add_subdirectory(animation)
add_subdirectory(qqmlecmascript)
add_subdirectory(qqmlanybinding)
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 0ae3195234..70b1c259e1 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -736,6 +736,7 @@ void tst_qmlcachegen::inlineComponent()
QVERIFY2(ok, errors);
QQmlEngine engine;
CleanlyLoadingComponent component(&engine, testFileUrl("inlineComponentWithId.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QTest::ignoreMessage(QtMsgType::QtInfoMsg, "42");
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
@@ -749,7 +750,9 @@ void tst_qmlcachegen::posthocRequired()
CleanlyLoadingComponent component(&engine, testFileUrl("posthocrequired.qml"));
QScopedPointer<QObject> obj(component.create());
QVERIFY(obj.isNull() && component.isError());
- QVERIFY(component.errorString().contains(QStringLiteral("Required property x was not initialized")));
+ QVERIFY2(component.errorString().contains(
+ QStringLiteral("Required property x was not initialized")),
+ qPrintable(component.errorString()));
}
void tst_qmlcachegen::scriptStringCachegenInteraction()
diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
new file mode 100644
index 0000000000..4c4cf0b1e5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_subdirectory(data)
+
+qt_internal_add_test(tst_qmlcppcodegen
+ SOURCES
+ tst_qmlcppcodegen.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::Gui
+ codegen_test_module
+ codegen_test_moduleplugin
+)
diff --git a/tests/auto/qml/qmlcppcodegen/data/AccessModelMethodsFromOutside.qml b/tests/auto/qml/qmlcppcodegen/data/AccessModelMethodsFromOutside.qml
new file mode 100644
index 0000000000..6683e0be45
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/AccessModelMethodsFromOutside.qml
@@ -0,0 +1,48 @@
+/******************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Ultralite module.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+import QtQuick 2.15
+
+Item {
+ property real cost1: fruitModel.get(1).cost
+ property string name1: fruitModel.get(1).name
+ property real cost2: fruitModel.get(2).cost
+ property string name2: fruitModel.get(2).name
+ ListModel {
+ id: fruitModel
+
+ ListElement {
+ name: "Apple"
+ cost: 2.2
+ }
+ ListElement {
+ name: "Orange"
+ cost: 3
+ }
+ ListElement {
+ name: "Banana"
+ cost: 1.95
+ }
+ }
+ Component.onCompleted: {
+ console.log(fruitModel.data(fruitModel.index(1, 0), 0))
+ console.log(fruitModel.get(0).name)
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/BaseMember.qml b/tests/auto/qml/qmlcppcodegen/data/BaseMember.qml
new file mode 100644
index 0000000000..cf2567452a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/BaseMember.qml
@@ -0,0 +1,9 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property int ppp: 3
+ property int ppp2: ppp * 3
+ property int ppp3: self.ppp * 4
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/BindingExpression.qml b/tests/auto/qml/qmlcppcodegen/data/BindingExpression.qml
new file mode 100644
index 0000000000..f4961037f4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/BindingExpression.qml
@@ -0,0 +1,59 @@
+/******************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Ultralite module.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick 2.15
+
+Dummy {
+ id: root
+ y : 10;
+ width : 200;
+ height : root.width;
+
+ Dummy {
+ id: child;
+ height: 100;
+ y: root.y + (root.height - child.height) / 2;
+ }
+
+ property string mass: {
+ var result = "nothing";
+ if (child.y > 100)
+ result = "heavy";
+ else
+ result = "light";
+ return result;
+ }
+
+ property real test_division: width / 1000 + 50;
+ property real test_ternary: false ? 1 : true ? 2.2 : 1; // the type must be real
+
+ property int test_switch: {
+ switch (width % 3) {
+ case 0:
+ return 130;
+ case 1:
+ return 380;
+ case 2:
+ return 630;
+ }
+ }
+}
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
new file mode 100644
index 0000000000..fa147be78f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -0,0 +1,134 @@
+set(cpp_sources
+ birthdayparty.cpp
+ birthdayparty.h
+ cppbaseclass.h
+ objectwithmethod.h
+ person.cpp
+ person.h
+)
+
+set(qml_files
+ AccessModelMethodsFromOutside.qml
+ BaseMember.qml
+ BindingExpression.qml
+ Dummy.qml
+ Enums.qml
+ Panel.qml
+ ProgressBar/Keyframe.qml
+ ProgressBar/KeyframeGroup.qml
+ ProgressBar/ProgressBar.ui.qml
+ ProgressBar/Root.qml
+ ProgressBar/Timeline.qml
+ ProgressBar/TimelineAnimation.qml
+ RootWithoutId.qml
+ Test.qml
+ TestCase.qml
+ aliasLookup.qml
+ anchorsFill.qml
+ array.qml
+ asCast.qml
+ attachedBaseEnum.qml
+ bindToValueType.qml
+ callContextPropertyLookupResult.qml
+ childobject.qml
+ colorAsVariant.qml
+ colorString.qml
+ componentReturnType.qml
+ compositeTypeMethod.qml
+ compositesingleton.qml
+ construct.qml
+ contextParam.qml
+ conversions.qml
+ conversions2.qml
+ curlygrouped.qml
+ deadShoeSize.qml
+ dialog.qml
+ dynamicscene.qml
+ enumInvalid.qml
+ enumScope.qml
+ enumsInOtherObject.qml
+ enumsUser.qml
+ equalsUndefined.qml
+ excessiveParameters.qml
+ extendedTypes.qml
+ failures.qml
+ fileDialog.qml
+ funcWithParams.qml
+ globals.qml
+ idAccess.qml
+ immediateQuit.qml
+ imports/QmlBench/Globals.qml
+ importsFromImportPath.qml
+ intEnumCompare.qml
+ intOverflow.qml
+ interactive.qml
+ interceptor.qml
+ jsMathObject.qml
+ jsimport.qml
+ jsmoduleimport.qml
+ layouts.qml
+ library.js
+ listIndices.qml
+ listlength.qml
+ math.qml
+ methods.qml
+ modulePrefix.qml
+ noQQmlData.qml
+ nonNotifyable.qml
+ noscope.qml
+ nullAccess.qml
+ outOfBounds.qml
+ overriddenMember.qml
+ ownProperty.qml
+ page.qml
+ parentProp.qml
+ pressAndHoldButton.qml
+ registerelimination.qml
+ scopeVsObject.qml
+ script.js
+ script.mjs
+ shared/Slider.qml
+ shifts.qml
+ signal.qml
+ signalHandler.qml
+ specificParent.qml
+ stringLength.qml
+ testlogger.js
+ text.qml
+ undefinedResets.qml
+ unknownParameter.qml
+ unusedAttached.qml
+ urlString.qml
+ valueTypeProperty.qml
+ voidfunction.qml
+)
+
+set(resource_files
+ ProgressBar/built-with-Qt_Large.png
+ imports/QmlBench/qmldir
+)
+
+set_source_files_properties("shared/Slider.qml"
+ PROPERTIES QT_RESOURCE_ALIAS "Slider.qml"
+)
+
+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
+)
+
+qt6_add_qml_module(codegen_test_module
+ VERSION 1.0
+ URI TestTypes
+ IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/"
+ SOURCES
+ ${cpp_sources}
+ QML_FILES
+ ${qml_files}
+ RESOURCES
+ ${resource_files}
+ OUTPUT_DIRECTORY TestTypes # Make sure tst_qmlcachegen doesn't see our output
+)
diff --git a/tests/auto/qml/qmlcppcodegen/data/Dummy.qml b/tests/auto/qml/qmlcppcodegen/data/Dummy.qml
new file mode 100644
index 0000000000..09d4a6eb51
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Dummy.qml
@@ -0,0 +1,44 @@
+/******************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick
+
+Item {
+ enum DummyEnum { DummyValue1, DummyValue2, DummyValue3 = 33 }
+ property int value
+ property Dummy child
+ property int dummyEnum
+
+ component Group: QtObject {
+ property Item item;
+ }
+
+ property Group group
+
+ signal triggered()
+ signal signalWithArg(int one, bool two)
+ property real onValue
+ property real offValue
+
+ function someFunction(a: int, b: bool, c: Dummy, 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/Enums.qml b/tests/auto/qml/qmlcppcodegen/data/Enums.qml
new file mode 100644
index 0000000000..0324e56ae9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Enums.qml
@@ -0,0 +1,29 @@
+import QtQml
+import QtQuick.Layouts
+
+QtObject {
+ id: root
+ property int appState: Enums.AppState.Blue
+ property string color: "blue"
+
+ enum AppState {
+ Red,
+ Green,
+ Blue
+ }
+
+ onAppStateChanged: {
+ if (appState === Enums.AppState.Green)
+ root.color = "green"
+ else if (appState === Enums.AppState.Red)
+ root.color = "red"
+ }
+
+ property Timer timer: Timer {
+ onTriggered: root.appState = Enums.AppState.Green
+ running: true
+ interval: 100
+ }
+
+ Layout.alignment: Qt.AlignCenter
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Panel.qml b/tests/auto/qml/qmlcppcodegen/data/Panel.qml
new file mode 100644
index 0000000000..e28a98327d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Panel.qml
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ property string name
+ property var notes
+
+ property real horizontalVelocity: 0
+
+ id: page
+
+ Image {
+ fillMode: Image.PreserveAspectCrop
+ clip: true
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: page.focus = false;
+ }
+
+ Item {
+ property font font
+ property color color
+ property int style
+ property color styleColor
+ objectName: page.name; x: 15; y: 8; height: 40; width: 370
+ font.pixelSize: 18; font.bold: true; color: "white"
+ style: Text.Outline; styleColor: "black"
+ }
+
+ Repeater {
+ model: page.notes
+ Item {
+ id: stickyPage
+ required property string noteText
+
+ property int randomX: Math.random() * (page.ListView.view.width-0.5*stickyImage.width) +100
+ property int randomY: Math.random() * (page.ListView.view.height-0.5*stickyImage.height) +50
+
+ x: randomX; y: randomY
+
+ rotation: -page.horizontalVelocity / 100
+ Behavior on rotation {
+ SpringAnimation { spring: 2.0; damping: 0.15 }
+ }
+
+ Item {
+ id: sticky
+ scale: 0.7
+
+ Image {
+ id: stickyImage
+ x: 8 + -width * 0.6 / 2; y: -20
+ source: "note-yellow.png"
+ scale: 0.6; transformOrigin: Item.TopLeft
+ }
+
+ Item {
+ property font font
+ property bool readOnly
+ property string text
+ id: myText
+ x: -104; y: 36; width: 215; height: 200
+ font.pixelSize: 24
+ readOnly: false
+ rotation: -8
+ text: stickyPage.noteText
+ }
+
+ Item {
+ x: stickyImage.x; y: -20
+ width: stickyImage.width * stickyImage.scale
+ height: stickyImage.height * stickyImage.scale
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ drag.target: stickyPage
+ drag.minimumY: 0
+ drag.maximumY: page.height - 80
+ drag.minimumX: 100
+ drag.maximumX: page.width - 140
+ onClicked: myText.forceActiveFocus()
+ }
+ }
+ }
+
+ Image {
+ x: -width / 2; y: -height * 0.5 / 2
+ source: "tack.png"
+ scale: 0.7; transformOrigin: Item.TopLeft
+ }
+
+ states: State {
+ name: "pressed"
+ when: mouse.pressed
+ PropertyChanges { target: sticky; rotation: 8; scale: 1 }
+ PropertyChanges { target: page; z: 8 }
+ }
+
+ transitions: Transition {
+ NumberAnimation { properties: "rotation,scale"; duration: 200 }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Keyframe.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Keyframe.qml
new file mode 100644
index 0000000000..0076031fcc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Keyframe.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+UniformAnimator {
+ property real frame
+ property var value
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/KeyframeGroup.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/KeyframeGroup.qml
new file mode 100644
index 0000000000..5cf857f622
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/KeyframeGroup.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ property QtObject target
+ property string property
+ default property list<Keyframe> keyframes
+ property url keyframeSource
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
new file mode 100644
index 0000000000..3815af1c2e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Design Studio.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.9
+import QtQuick.Window 2.3
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.3
+
+Rectangle {
+ visible: true
+ width: 640
+ height: 480
+ color: "#242424"
+
+ ColumnLayout {
+ x: 20
+ y: 152
+ spacing: 20
+
+ Root {
+ id: root
+ }
+
+ Root {
+ id: root1
+ }
+
+ Root {
+ id: root2
+ }
+ }
+
+ Timeline {
+ id: timeline
+ enabled: true
+ endFrame: 4000
+ startFrame: 0
+
+ KeyframeGroup {
+ target: root
+ property: "progress"
+
+ Keyframe {
+ value: 10
+ frame: 0
+ }
+
+ Keyframe {
+ easing.bezierCurve: [0.86, 0.00, 0.07, 1.00, 1, 1]
+ value: 90
+ frame: 2000
+ }
+
+ Keyframe {
+ easing.bezierCurve: [0.86, 0.00, 0.07, 1.00, 1, 1]
+ value: 10
+ frame: 4000
+ }
+ }
+
+ KeyframeGroup {
+ target: root1
+ property: "progress"
+
+ Keyframe {
+ easing.bezierCurve: [0.17, 0.84, 0.44, 1.00, 1, 1]
+ value: 90
+ frame: 4000
+ }
+
+ Keyframe {
+ easing.bezierCurve: [0.17, 0.84, 0.44, 1.00, 1, 1]
+ value: 20
+ frame: 2000
+ }
+
+ Keyframe {
+ value: 90
+ frame: 0
+ }
+ }
+
+ KeyframeGroup {
+ target: root2
+ property: "progress"
+
+ Keyframe {
+ value: 15
+ frame: 0
+ }
+
+ Keyframe {
+ easing.bezierCurve: [0.79, 0.14, 0.15, 0.86, 1, 1]
+ value: 85
+ frame: 2000
+ }
+
+ Keyframe {
+ easing.bezierCurve: [0.79, 0.14, 0.15, 0.86, 1, 1]
+ value: 15
+ frame: 4000
+ }
+ }
+
+ animations: [
+ TimelineAnimation {
+ id: propertyAnimation
+ target: timeline
+ property: "currentFrame"
+ running: true
+ to: timeline.endFrame
+ from: timeline.startFrame
+ loops: -1
+ duration: 1000
+ }
+ ]
+ }
+
+ Image {
+ id: image
+ x: 518
+ y: 0
+ width: 102
+ height: 137
+ fillMode: Image.PreserveAspectFit
+ source: "built-with-Qt_Large.png"
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
new file mode 100644
index 0000000000..ab0ecacc4d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Design Studio.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.9
+
+Item {
+ id: root
+ width: 600
+ height: 65
+ property alias progress: timeline.currentFrame
+
+ Rectangle {
+ id: pb_back
+ color: "#9f9f9f"
+ radius: 4
+ anchors.right: parent.right
+ anchors.rightMargin: 0
+ anchors.left: parent.left
+ anchors.leftMargin: 0
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 0
+ anchors.top: parent.top
+ anchors.topMargin: 30
+
+ Rectangle {
+ id: pb_front
+ width: 200
+ color: "#ffffff"
+ anchors.bottomMargin: 5
+ anchors.left: parent.left
+ anchors.leftMargin: 5
+ anchors.bottom: parent.bottom
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+ }
+
+ Text {
+ id: text1
+ color: "#ffffff"
+ text: Math.round(root.progress)
+ font.pixelSize: 18
+ }
+
+ Timeline {
+ id: timeline
+ enabled: true
+ startFrame: 0
+ endFrame: 100
+
+ KeyframeGroup {
+ target: text1
+ property: "color"
+
+ Keyframe {
+ value: "#8de98d"
+ frame: 0
+ }
+
+ Keyframe {
+ value: "#de4f4f"
+ frame: 50
+ }
+
+ Keyframe {
+ value: "#f0c861"
+ frame: 100
+ }
+ }
+
+ KeyframeGroup {
+ target: pb_front
+ property: "width"
+
+ Keyframe {
+ value: 0
+ frame: 0
+ }
+
+ Keyframe {
+ value: 590
+ frame: 100
+ }
+ }
+
+ KeyframeGroup {
+ target: pb_front
+ property: "color"
+ Keyframe {
+ value: "#8de98d"
+ frame: 0
+ }
+
+ Keyframe {
+ value: "#de4f4f"
+ frame: 50
+ }
+
+ Keyframe {
+ value: "#f0c861"
+ frame: 100
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Timeline.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Timeline.qml
new file mode 100644
index 0000000000..503d0aefa4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Timeline.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property real startFrame
+ property real endFrame
+ property real currentFrame
+ default property list<KeyframeGroup> keyframeGroupes
+ property list<TimelineAnimation> animations
+ property bool enabled
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
new file mode 100644
index 0000000000..b97e8956bf
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+NumberAnimation {
+ property bool pingPong
+ signal finished()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/built-with-Qt_Large.png b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/built-with-Qt_Large.png
new file mode 100644
index 0000000000..75ec0fa080
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/built-with-Qt_Large.png
Binary files differ
diff --git a/tests/auto/qml/qmlcppcodegen/data/RootWithoutId.qml b/tests/auto/qml/qmlcppcodegen/data/RootWithoutId.qml
new file mode 100644
index 0000000000..c350fc3a4b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/RootWithoutId.qml
@@ -0,0 +1,33 @@
+/******************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Ultralite module.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick 2.15
+
+Item {
+ property bool foo: false
+
+ property alias bar: item.visible
+
+ Item {
+ id: item
+ visible: parent.foo
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Test.qml b/tests/auto/qml/qmlcppcodegen/data/Test.qml
new file mode 100644
index 0000000000..d7263a2bff
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Test.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import TestTypes 1.0
+
+CppBaseClass {
+ enum EE {
+ AA, BB, CC
+ }
+
+ property int foo: 1 + 2
+ property int ppp: 4
+
+ // constant, binding will be removed.
+ cppProp: 3 + 4
+
+ // An actual binding. Can't be removed because cppProp may be manually set.
+ cppProp2: cppProp * 2
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/TestCase.qml b/tests/auto/qml/qmlcppcodegen/data/TestCase.qml
new file mode 100644
index 0000000000..82e830ba88
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/TestCase.qml
@@ -0,0 +1,1987 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Window 2.0 // used for qtest_verifyItem
+import QtTest 1.2
+import "testlogger.js" as TestLogger
+
+/*!
+ \qmltype TestCase
+ \inqmlmodule QtTest
+ \brief Represents a unit test case.
+ \since 4.8
+ \ingroup qtquicktest
+
+ \section1 Introduction to QML Test Cases
+
+ Test cases are written as JavaScript functions within a TestCase
+ type:
+
+ \code
+ import QtQuick 2.0
+ import QtTest 1.2
+
+ TestCase {
+ name: "MathTests"
+
+ function test_math() {
+ compare(2 + 2, 4, "2 + 2 = 4")
+ }
+
+ function test_fail() {
+ compare(2 + 2, 5, "2 + 2 = 5")
+ }
+ }
+ \endcode
+
+ Functions whose names start with "test_" are treated as test cases
+ to be executed. The \l name property is used to prefix the functions
+ in the output:
+
+ \code
+ ********* Start testing of MathTests *********
+ Config: Using QTest library 4.7.2, Qt 4.7.2
+ PASS : MathTests::initTestCase()
+ FAIL! : MathTests::test_fail() 2 + 2 = 5
+ Actual (): 4
+ Expected (): 5
+ Loc: [/home/.../tst_math.qml(12)]
+ PASS : MathTests::test_math()
+ PASS : MathTests::cleanupTestCase()
+ Totals: 3 passed, 1 failed, 0 skipped
+ ********* Finished testing of MathTests *********
+ \endcode
+
+ Because of the way JavaScript properties work, the order in which the
+ test functions are found is unpredictable. To assist with predictability,
+ the test framework will sort the functions on ascending order of name.
+ This can help when there are two tests that must be run in order.
+
+ Multiple TestCase types can be supplied. The test program will exit
+ once they have all completed. If a test case doesn't need to run
+ (because a precondition has failed), then \l optional can be set to true.
+
+ \section1 Data-driven Tests
+
+ Table data can be provided to a test using a function name that ends
+ with "_data". Alternatively, the \c init_data() function can be used
+ to provide default test data for all test functions in a TestCase type:
+
+
+ \code
+ import QtQuick 2.0
+ import QtTest 1.2
+
+ TestCase {
+ name: "DataTests"
+
+ function init_data() {
+ return [
+ {tag:"init_data_1", a:1, b:2, answer: 3},
+ {tag:"init_data_2", a:2, b:4, answer: 6}
+ ];
+ }
+
+ function test_table_data() {
+ return [
+ {tag: "2 + 2 = 4", a: 2, b: 2, answer: 4 },
+ {tag: "2 + 6 = 8", a: 2, b: 6, answer: 8 },
+ ]
+ }
+
+ function test_table(data) {
+ //data comes from test_table_data
+ compare(data.a + data.b, data.answer)
+ }
+
+ function test__default_table(data) {
+ //data comes from init_data
+ compare(data.a + data.b, data.answer)
+ }
+ }
+ \endcode
+
+ The test framework will iterate over all of the rows in the table
+ and pass each row to the test function. As shown, the columns can be
+ extracted for use in the test. The \c tag column is special - it is
+ printed by the test framework when a row fails, to help the reader
+ identify which case failed amongst a set of otherwise passing tests.
+
+ \section1 Benchmarks
+
+ Functions whose names start with "benchmark_" will be run multiple
+ times with the Qt benchmark framework, with an average timing value
+ reported for the runs. This is equivalent to using the \c{QBENCHMARK}
+ macro in the C++ version of QTestLib.
+
+ \code
+ TestCase {
+ id: top
+ name: "CreateBenchmark"
+
+ function benchmark_create_component() {
+ var component = Qt.createComponent("item.qml")
+ var obj = component.createObject(top)
+ obj.destroy()
+ component.destroy()
+ }
+ }
+
+ RESULT : CreateBenchmark::benchmark_create_component:
+ 0.23 msecs per iteration (total: 60, iterations: 256)
+ PASS : CreateBenchmark::benchmark_create_component()
+ \endcode
+
+ To get the effect of the \c{QBENCHMARK_ONCE} macro, prefix the test
+ function name with "benchmark_once_".
+
+ \section1 Simulating Keyboard and Mouse Events
+
+ The keyPress(), keyRelease(), and keyClick() methods can be used
+ to simulate keyboard events within unit tests. The events are
+ delivered to the currently focused QML item. You can pass either
+ a Qt.Key enum value or a latin1 char (string of length one)
+
+ \code
+ Rectangle {
+ width: 50; height: 50
+ focus: true
+
+ TestCase {
+ name: "KeyClick"
+ when: windowShown
+
+ function test_key_click() {
+ keyClick(Qt.Key_Left)
+ keyClick("a")
+ ...
+ }
+ }
+ }
+ \endcode
+
+ The mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence()
+ and mouseMove() methods can be used to simulate mouse events in a
+ similar fashion.
+
+ \b{Note:} keyboard and mouse events can only be delivered once the
+ main window has been shown. Attempts to deliver events before then
+ will fail. Use the \l when and windowShown properties to track
+ when the main window has been shown.
+
+ \section1 Managing Dynamically Created Test Objects
+
+ A typical pattern with QML tests is to
+ \l {Dynamic QML Object Creation from JavaScript}{dynamically create}
+ an item and then destroy it at the end of the test function:
+
+ \code
+ TestCase {
+ id: testCase
+ name: "MyTest"
+ when: windowShown
+
+ function test_click() {
+ var item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase);
+ verify(item);
+
+ // Test item...
+
+ item.destroy();
+ }
+ }
+ \endcode
+
+ The problem with this pattern is that any failures in the test function
+ will cause the call to \c item.destroy() to be skipped, leaving the item
+ hanging around in the scene until the test case has finished. This can
+ result in interference with future tests; for example, by blocking input
+ events or producing unrelated debug output that makes it difficult to
+ follow the code's execution.
+
+ By calling \l createTemporaryQmlObject() instead, the object is guaranteed
+ to be destroyed at the end of the test function:
+
+ \code
+ TestCase {
+ id: testCase
+ name: "MyTest"
+ when: windowShown
+
+ function test_click() {
+ var item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase);
+ verify(item);
+
+ // Test item...
+
+ // Don't need to worry about destroying "item" here.
+ }
+ }
+ \endcode
+
+ For objects that are created via the \l {Component::}{createObject()} function
+ of \l Component, the \l createTemporaryObject() function can be used.
+
+ \sa {QtTest::SignalSpy}{SignalSpy}, {Qt Quick Test}
+*/
+
+
+Item {
+ id: testCase
+ visible: false
+ TestUtil {
+ id:util
+ }
+
+ /*!
+ \qmlproperty string TestCase::name
+
+ This property defines the name of the test case for result reporting.
+ The default value is an empty string.
+
+ \code
+ TestCase {
+ name: "ButtonTests"
+ ...
+ }
+ \endcode
+ */
+ property string name
+
+ /*!
+ \qmlproperty bool TestCase::when
+
+ This property should be set to true when the application wants
+ the test cases to run. The default value is true. In the following
+ example, a test is run when the user presses the mouse button:
+
+ \code
+ Rectangle {
+ id: foo
+ width: 640; height: 480
+ color: "cyan"
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ }
+
+ property bool bar: true
+
+ TestCase {
+ name: "ItemTests"
+ when: area.pressed
+ id: test1
+
+ function test_bar() {
+ verify(bar)
+ }
+ }
+ }
+ \endcode
+
+ The test application will exit once all \l TestCase types
+ have been triggered and have run. The \l optional property can
+ be used to exclude a \l TestCase type.
+
+ \sa optional, completed
+ */
+ property bool when: true
+
+ /*!
+ \qmlproperty bool TestCase::completed
+
+ This property will be set to true once the test case has completed
+ execution. Test cases are only executed once. The initial value
+ is false.
+
+ \sa running, when
+ */
+ property bool completed: false
+
+ /*!
+ \qmlproperty bool TestCase::running
+
+ This property will be set to true while the test case is running.
+ The initial value is false, and the value will become false again
+ once the test case completes.
+
+ \sa completed, when
+ */
+ property bool running: false
+
+ /*!
+ \qmlproperty bool TestCase::optional
+
+ Multiple \l TestCase types can be supplied in a test application.
+ The application will exit once they have all completed. If a test case
+ does not need to run (because a precondition has failed), then this
+ property can be set to true. The default value is false.
+
+ \code
+ TestCase {
+ when: false
+ optional: true
+ function test_not_run() {
+ verify(false)
+ }
+ }
+ \endcode
+
+ \sa when, completed
+ */
+ property bool optional: false
+
+ /*!
+ \qmlproperty bool TestCase::windowShown
+
+ This property will be set to true after the QML viewing window has
+ been displayed. Normally test cases run as soon as the test application
+ is loaded and before a window is displayed. If the test case involves
+ visual types and behaviors, then it may need to be delayed until
+ after the window is shown.
+
+ \code
+ Button {
+ id: button
+ onClicked: text = "Clicked"
+ TestCase {
+ name: "ClickTest"
+ when: windowShown
+ function test_click() {
+ button.clicked();
+ compare(button.text, "Clicked");
+ }
+ }
+ }
+ \endcode
+ */
+ property bool windowShown: QTestRootObject.windowShown
+
+ // Internal private state. Identifiers prefixed with qtest are reserved.
+ /*! \internal */
+ property bool qtest_prevWhen: true
+ /*! \internal */
+ property int qtest_testId: -1
+ /*! \internal */
+ property bool qtest_componentCompleted : false
+ /*! \internal */
+ property var qtest_testCaseResult
+ /*! \internal */
+ property var qtest_results: qtest_results_normal
+ /*! \internal */
+ TestResult { id: qtest_results_normal }
+ /*! \internal */
+ property var qtest_events: qtest_events_normal
+ TestEvent { id: qtest_events_normal }
+ /*! \internal */
+ property var qtest_temporaryObjects: []
+
+ /*!
+ \qmlmethod TestCase::fail(message = "")
+
+ Fails the current test case, with the optional \a message.
+ Similar to \c{QFAIL(message)} in C++.
+ */
+ function fail(msg) {
+ if (msg === undefined)
+ msg = "";
+ qtest_results.fail(msg, util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ /*! \internal */
+ function qtest_fail(msg, frame) {
+ if (msg === undefined)
+ msg = "";
+ qtest_results.fail(msg, util.callerFile(frame), util.callerLine(frame))
+ throw new Error("QtQuickTest::fail")
+ }
+
+ /*!
+ \qmlmethod TestCase::verify(condition, message = "")
+
+ Fails the current test case if \a condition is false, and
+ displays the optional \a message. Similar to \c{QVERIFY(condition)}
+ or \c{QVERIFY2(condition, message)} in C++.
+ */
+ function verify(cond, msg) {
+ if (arguments.length > 2)
+ qtest_fail("More than two arguments given to verify(). Did you mean tryVerify() or tryCompare()?", 1)
+
+ if (msg === undefined)
+ msg = "";
+ if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine()))
+ throw new Error("QtQuickTest::fail")
+ }
+
+ /*!
+ \since 5.8
+ \qmlmethod TestCase::tryVerify(function, timeout = 5000, message = "")
+
+ Fails the current test case if \a function does not evaluate to
+ \c true before the specified \a timeout (in milliseconds) has elapsed.
+ The function is evaluated multiple times until the timeout is
+ reached. An optional \a message is displayed upon failure.
+
+ This function is intended for testing applications where a condition
+ changes based on asynchronous events. Use verify() for testing
+ synchronous condition changes, and tryCompare() for testing
+ asynchronous property changes.
+
+ For example, in the code below, it's not possible to use tryCompare(),
+ because the \c currentItem property might be \c null for a short period
+ of time:
+
+ \code
+ tryCompare(listView.currentItem, "text", "Hello");
+ \endcode
+
+ Instead, we can use tryVerify() to first check that \c currentItem
+ isn't \c null, and then use a regular compare afterwards:
+
+ \code
+ tryVerify(function(){ return listView.currentItem })
+ compare(listView.currentItem.text, "Hello")
+ \endcode
+
+ \sa verify(), compare(), tryCompare(), SignalSpy::wait()
+ */
+ function tryVerify(expressionFunction, timeout, msg) {
+ if (!expressionFunction || !(expressionFunction instanceof Function)) {
+ qtest_results.fail("First argument must be a function", util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (timeout && typeof(timeout) !== "number") {
+ qtest_results.fail("timeout argument must be a number", util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (msg && typeof(msg) !== "string") {
+ qtest_results.fail("message argument must be a string", util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (!timeout)
+ timeout = 5000
+
+ if (msg === undefined)
+ msg = "function returned false"
+
+ if (!expressionFunction())
+ wait(0)
+
+ var i = 0
+ while (i < timeout && !expressionFunction()) {
+ wait(50)
+ i += 50
+ }
+
+ if (!qtest_results.verify(expressionFunction(), msg, util.callerFile(), util.callerLine()))
+ throw new Error("QtQuickTest::fail")
+ }
+
+ /*!
+ \since 5.13
+ \qmlmethod bool TestCase::isPolishScheduled(object item)
+
+ Returns \c true if \l {QQuickItem::}{updatePolish()} has not been called
+ on \a item since the last call to \l {QQuickItem::}{polish()},
+ otherwise returns \c false.
+
+ When assigning values to properties in QML, any layouting the item
+ must do as a result of the assignment might not take effect immediately,
+ but can instead be postponed until the item is polished. For these cases,
+ you can use this function to ensure that the item has been polished
+ before the execution of the test continues. For example:
+
+ \code
+ verify(isPolishScheduled(item))
+ verify(waitForItemPolished(item))
+ \endcode
+
+ Without the call to \c isPolishScheduled() above, the
+ call to \c waitForItemPolished() might see that no polish
+ was scheduled and therefore pass instantly, assuming that
+ the item had already been polished. This function
+ makes it obvious why an item wasn't polished and allows tests to
+ fail early under such circumstances.
+
+ \sa waitForItemPolished(), QQuickItem::polish(), QQuickItem::updatePolish()
+ */
+ function isPolishScheduled(item) {
+ if (!item || typeof item !== "object") {
+ qtest_results.fail("Argument must be a valid Item; actual type is " + typeof item,
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ return qtest_results.isPolishScheduled(item)
+ }
+
+ /*!
+ \since 5.13
+ \qmlmethod bool waitForItemPolished(object item, int timeout = 5000)
+
+ Waits for \a timeout milliseconds or until
+ \l {QQuickItem::}{updatePolish()} has been called on \a item.
+
+ Returns \c true if \c updatePolish() was called on \a item within
+ \a timeout milliseconds, otherwise returns \c false.
+
+ \sa isPolishScheduled(), QQuickItem::polish(), QQuickItem::updatePolish()
+ */
+ function waitForItemPolished(item, timeout) {
+ if (!item || typeof item !== "object") {
+ qtest_results.fail("First argument must be a valid Item; actual type is " + typeof item,
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (timeout !== undefined && typeof(timeout) != "number") {
+ qtest_results.fail("Second argument must be a number; actual type is " + typeof timeout,
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (!timeout)
+ timeout = 5000
+
+ return qtest_results.waitForItemPolished(item, timeout)
+ }
+
+ /*!
+ \since 5.9
+ \qmlmethod object TestCase::createTemporaryQmlObject(string qml, object parent, string filePath)
+
+ This function dynamically creates a QML object from the given \a qml
+ string with the specified \a parent. The returned object will be
+ destroyed (if it was not already) after \l cleanup() has finished
+ executing, meaning that objects created with this function are
+ guaranteed to be destroyed after each test, regardless of whether or
+ not the tests fail.
+
+ If there was an error while creating the object, \c null will be
+ returned.
+
+ If \a filePath is specified, it will be used for error reporting for
+ the created object.
+
+ This function calls
+ \l {QtQml::Qt::createQmlObject()}{Qt.createQmlObject()} internally.
+
+ \sa {Managing Dynamically Created Test Objects}
+ */
+ function createTemporaryQmlObject(qml, parent, filePath) {
+ if (typeof qml !== "string") {
+ qtest_results.fail("First argument must be a string of QML; actual type is " + typeof qml,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ if (!parent || typeof parent !== "object") {
+ qtest_results.fail("Second argument must be a valid parent object; actual type is " + typeof parent,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ if (filePath !== undefined && typeof filePath !== "string") {
+ qtest_results.fail("Third argument must be a file path string; actual type is " + typeof filePath,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ var object = Qt.createQmlObject(qml, parent, filePath);
+ qtest_temporaryObjects.push(object);
+ return object;
+ }
+
+ /*!
+ \since 5.9
+ \qmlmethod object TestCase::createTemporaryObject(Component component, object parent, object properties)
+
+ This function dynamically creates a QML object from the given
+ \a component with the specified optional \a parent and \a properties.
+ The returned object will be destroyed (if it was not already) after
+ \l cleanup() has finished executing, meaning that objects created with
+ this function are guaranteed to be destroyed after each test,
+ regardless of whether or not the tests fail.
+
+ If there was an error while creating the object, \c null will be
+ returned.
+
+ This function calls
+ \l {QtQml::Component::createObject()}{component.createObject()}
+ internally.
+
+ \sa {Managing Dynamically Created Test Objects}
+ */
+ function createTemporaryObject(component, parent, properties) {
+ if (typeof component !== "object") {
+ qtest_results.fail("First argument must be a Component; actual type is " + typeof component,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ if (properties && typeof properties !== "object") {
+ qtest_results.fail("Third argument must be an object; actual type is " + typeof properties,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ var object = component.createObject(parent, properties ? properties : ({}));
+ qtest_temporaryObjects.push(object);
+ return object;
+ }
+
+ /*!
+ \internal
+
+ Destroys all temporary objects that still exist.
+ */
+ function qtest_destroyTemporaryObjects() {
+ for (var i = 0; i < qtest_temporaryObjects.length; ++i) {
+ var temporaryObject = qtest_temporaryObjects[i];
+ // ### the typeof check can be removed when QTBUG-57749 is fixed
+ if (temporaryObject && typeof temporaryObject.destroy === "function")
+ temporaryObject.destroy();
+ }
+ qtest_temporaryObjects = [];
+ }
+
+ /*! \internal */
+ // Determine what is o.
+ // Discussions and reference: http://philrathe.com/articles/equiv
+ // Test suites: http://philrathe.com/tests/equiv
+ // Author: Philippe Rathé <prathe@gmail.com>
+ function qtest_typeof(o) {
+ if (typeof o === "undefined") {
+ return "undefined";
+
+ // consider: typeof null === object
+ } else if (o === null) {
+ return "null";
+
+ } else if (o.constructor === String) {
+ return "string";
+
+ } else if (o.constructor === Boolean) {
+ return "boolean";
+
+ } else if (o.constructor === Number) {
+
+ if (isNaN(o)) {
+ return "nan";
+ } else {
+ return "number";
+ }
+ // consider: typeof [] === object
+ } else if (o instanceof Array) {
+ return "array";
+
+ // consider: typeof new Date() === object
+ } else if (o instanceof Date) {
+ return "date";
+
+ // consider: /./ instanceof Object;
+ // /./ instanceof RegExp;
+ // typeof /./ === "function"; // => false in IE and Opera,
+ // true in FF and Safari
+ } else if (o instanceof RegExp) {
+ return "regexp";
+
+ } else if (typeof o === "object") {
+ if ("mapFromItem" in o && "mapToItem" in o) {
+ return "declarativeitem"; // @todo improve detection of declarative items
+ } else if ("x" in o && "y" in o && "z" in o) {
+ return "vector3d"; // Qt 3D vector
+ }
+ return "object";
+ } else if (o instanceof Function) {
+ return "function";
+ } else {
+ return undefined;
+ }
+ }
+
+ /*! \internal */
+ // Test for equality
+ // Large parts contain sources from QUnit or http://philrathe.com
+ // Discussions and reference: http://philrathe.com/articles/equiv
+ // Test suites: http://philrathe.com/tests/equiv
+ // Author: Philippe Rathé <prathe@gmail.com>
+ function qtest_compareInternal(act, exp) {
+ var success = false;
+ if (act === exp) {
+ success = true; // catch the most you can
+ } else if (act === null || exp === null || typeof act === "undefined" || typeof exp === "undefined") {
+ success = false; // don't lose time with error prone cases
+ } else {
+ var typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act)
+ if (typeExp !== typeAct) {
+ // allow object vs string comparison (e.g. for colors)
+ // else break on different types
+ if ((typeExp === "string" && (typeAct === "object") || typeAct == "declarativeitem")
+ || ((typeExp === "object" || typeExp == "declarativeitem") && typeAct === "string")) {
+ success = (act == exp)
+ }
+ } else if (typeExp === "string" || typeExp === "boolean" ||
+ typeExp === "null" || typeExp === "undefined") {
+ if (exp instanceof act.constructor || act instanceof exp.constructor) {
+ // to catch short annotaion VS 'new' annotation of act declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ success = (act == exp)
+ } else {
+ success = (act === exp)
+ }
+ } else if (typeExp === "nan") {
+ success = isNaN(act);
+ } else if (typeExp === "number") {
+ // Use act fuzzy compare if the two values are floats
+ if (Math.abs(act - exp) <= 0.00001) {
+ success = true
+ }
+ } else if (typeExp === "array") {
+ success = qtest_compareInternalArrays(act, exp)
+ } else if (typeExp === "object") {
+ success = qtest_compareInternalObjects(act, exp)
+ } else if (typeExp === "declarativeitem") {
+ success = qtest_compareInternalObjects(act, exp) // @todo improve comparison of declarative items
+ } else if (typeExp === "vector3d") {
+ success = (Math.abs(act.x - exp.x) <= 0.00001 &&
+ Math.abs(act.y - exp.y) <= 0.00001 &&
+ Math.abs(act.z - exp.z) <= 0.00001)
+ } else if (typeExp === "date") {
+ success = (act.valueOf() === exp.valueOf())
+ } else if (typeExp === "regexp") {
+ success = (act.source === exp.source && // the regex itself
+ act.global === exp.global && // and its modifers (gmi) ...
+ act.ignoreCase === exp.ignoreCase &&
+ act.multiline === exp.multiline)
+ }
+ }
+ return success
+ }
+
+ /*! \internal */
+ function qtest_compareInternalObjects(act, exp) {
+ var i;
+ var eq = true; // unless we can proove it
+ var aProperties = [], bProperties = []; // collection of strings
+
+ // comparing constructors is more strict than using instanceof
+ if (act.constructor !== exp.constructor) {
+ return false;
+ }
+
+ for (i in act) { // be strict: don't ensures hasOwnProperty and go deep
+ aProperties.push(i); // collect act's properties
+ if (!qtest_compareInternal(act[i], exp[i])) {
+ eq = false;
+ break;
+ }
+ }
+
+ for (i in exp) {
+ bProperties.push(i); // collect exp's properties
+ }
+
+ if (aProperties.length == 0 && bProperties.length == 0) { // at least a special case for QUrl
+ return eq && (JSON.stringify(act) == JSON.stringify(exp));
+ }
+
+ // Ensures identical properties name
+ return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
+
+ }
+
+ /*! \internal */
+ function qtest_compareInternalArrays(actual, expected) {
+ if (actual.length != expected.length) {
+ return false
+ }
+
+ for (var i = 0, len = actual.length; i < len; i++) {
+ if (!qtest_compareInternal(actual[i], expected[i])) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ /*!
+ \qmlmethod TestCase::compare(actual, expected, message = "")
+
+ Fails the current test case if \a actual is not the same as
+ \a expected, and displays the optional \a message. Similar
+ to \c{QCOMPARE(actual, expected)} in C++.
+
+ \sa tryCompare(), fuzzyCompare
+ */
+ function compare(actual, expected, msg) {
+ var act = qtest_results.stringify(actual)
+ var exp = qtest_results.stringify(expected)
+
+ var success = qtest_compareInternal(actual, expected)
+ if (msg === undefined) {
+ if (success)
+ msg = "COMPARE()"
+ else
+ msg = "Compared values are not the same"
+ }
+ if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) {
+ throw new Error("QtQuickTest::fail")
+ }
+ }
+
+ /*!
+ \qmlmethod TestCase::fuzzyCompare(actual, expected, delta, message = "")
+
+ Fails the current test case if the difference betwen \a actual and \a expected
+ is greater than \a delta, and displays the optional \a message. Similar
+ to \c{qFuzzyCompare(actual, expected)} in C++ but with a required \a delta value.
+
+ This function can also be used for color comparisons if both the \a actual and
+ \a expected values can be converted into color values. If any of the differences
+ for RGBA channel values are greater than \a delta, the test fails.
+
+ \sa tryCompare(), compare()
+ */
+ function fuzzyCompare(actual, expected, delta, msg) {
+ if (delta === undefined)
+ qtest_fail("A delta value is required for fuzzyCompare", 2)
+
+ var success = qtest_results.fuzzyCompare(actual, expected, delta)
+ if (msg === undefined) {
+ if (success)
+ msg = "FUZZYCOMPARE()"
+ else
+ msg = "Compared values are not the same with delta(" + delta + ")"
+ }
+
+ if (!qtest_results.compare(success, msg, actual, expected, util.callerFile(), util.callerLine())) {
+ throw new Error("QtQuickTest::fail")
+ }
+ }
+
+ /*!
+ \qmlmethod object TestCase::grabImage(item)
+
+ Returns a snapshot image object of the given \a item.
+
+ The returned image object has the following properties:
+ \list
+ \li width Returns the width of the underlying image (since 5.10)
+ \li height Returns the height of the underlying image (since 5.10)
+ \li size Returns the size of the underlying image (since 5.10)
+ \endlist
+
+ Additionally, the returned image object has the following methods:
+ \list
+ \li \c {red(x, y)} Returns the red channel value of the pixel at \e x, \e y position
+ \li \c {green(x, y)} Returns the green channel value of the pixel at \e x, \e y position
+ \li \c {blue(x, y)} Returns the blue channel value of the pixel at \e x, \e y position
+ \li \c {alpha(x, y)} Returns the alpha channel value of the pixel at \e x, \e y position
+ \li \c {pixel(x, y)} Returns the color value of the pixel at \e x, \e y position
+ \li \c {equals(image)} Returns \c true if this image is identical to \e image -
+ see \l QImage::operator== (since 5.6)
+
+ For example:
+
+ \code
+ var image = grabImage(rect);
+ compare(image.red(10, 10), 255);
+ compare(image.pixel(20, 20), Qt.rgba(255, 0, 0, 255));
+
+ rect.width += 10;
+ var newImage = grabImage(rect);
+ verify(!newImage.equals(image));
+ \endcode
+
+ \li \c {save(path)} Saves the image to the given \e path. If the image cannot
+ be saved, an exception will be thrown. (since 5.10)
+
+ This can be useful to perform postmortem analysis on failing tests, for
+ example:
+
+ \code
+ var image = grabImage(rect);
+ try {
+ compare(image.width, 100);
+ } catch (ex) {
+ image.save("debug.png");
+ throw ex;
+ }
+ \endcode
+
+ \endlist
+ */
+ function grabImage(item) {
+ return qtest_results.grabImage(item);
+ }
+
+ /*!
+ \since 5.4
+ \qmlmethod QtObject TestCase::findChild(parent, objectName)
+
+ Returns the first child of \a parent with \a objectName, or \c null if
+ no such item exists. Both visual and non-visual children are searched
+ recursively, with visual children being searched first.
+
+ \code
+ compare(findChild(item, "childObject"), expectedChildObject);
+ \endcode
+ */
+ function findChild(parent, objectName) {
+ // First, search the visual item hierarchy.
+ var child = qtest_findVisualChild(parent, objectName);
+ if (child)
+ return child;
+
+ // If it's not a visual child, it might be a QObject child.
+ return qtest_results.findChild(parent, objectName);
+ }
+
+ /*! \internal */
+ function qtest_findVisualChild(parent, objectName) {
+ if (!parent || parent.children === undefined)
+ return null;
+
+ for (var i = 0; i < parent.children.length; ++i) {
+ // Is this direct child of ours the child we're after?
+ var child = parent.children[i];
+ if (child.objectName === objectName)
+ return child;
+ }
+
+ for (i = 0; i < parent.children.length; ++i) {
+ // Try the direct child's children.
+ child = qtest_findVisualChild(parent.children[i], objectName);
+ if (child)
+ return child;
+ }
+ return null;
+ }
+
+ /*!
+ \qmlmethod TestCase::tryCompare(obj, property, expected, timeout = 5000, message = "")
+
+ Fails the current test case if the specified \a property on \a obj
+ is not the same as \a expected, and displays the optional \a message.
+ The test will be retried multiple times until the
+ \a timeout (in milliseconds) is reached.
+
+ This function is intended for testing applications where a property
+ changes value based on asynchronous events. Use compare() for testing
+ synchronous property changes.
+
+ \code
+ tryCompare(img, "status", BorderImage.Ready)
+ compare(img.width, 120)
+ compare(img.height, 120)
+ compare(img.horizontalTileMode, BorderImage.Stretch)
+ compare(img.verticalTileMode, BorderImage.Stretch)
+ \endcode
+
+ SignalSpy::wait() provides an alternative method to wait for a
+ signal to be emitted.
+
+ \sa compare(), SignalSpy::wait()
+ */
+ function tryCompare(obj, prop, value, timeout, msg) {
+ if (arguments.length == 1 || (typeof(prop) != "string" && typeof(prop) != "number")) {
+ qtest_results.fail("A property name as string or index is required for tryCompare",
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+ if (arguments.length == 2) {
+ qtest_results.fail("A value is required for tryCompare",
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+ if (timeout !== undefined && typeof(timeout) != "number") {
+ qtest_results.fail("timeout should be a number",
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+ if (!timeout)
+ timeout = 5000
+ if (msg === undefined)
+ msg = "property " + prop
+ if (!qtest_compareInternal(obj[prop], value))
+ wait(0)
+ var i = 0
+ while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
+ wait(50)
+ i += 50
+ }
+ var actual = obj[prop]
+ var act = qtest_results.stringify(actual)
+ var exp = qtest_results.stringify(value)
+ var success = qtest_compareInternal(actual, value)
+ if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine()))
+ throw new Error("QtQuickTest::fail")
+ }
+
+ /*!
+ \qmlmethod TestCase::skip(message = "")
+
+ Skips the current test case and prints the optional \a message.
+ If this is a data-driven test, then only the current row is skipped.
+ Similar to \c{QSKIP(message)} in C++.
+ */
+ function skip(msg) {
+ if (msg === undefined)
+ msg = ""
+ qtest_results.skip(msg, util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::skip")
+ }
+
+ /*!
+ \qmlmethod TestCase::expectFail(tag, message)
+
+ In a data-driven test, marks the row associated with \a tag as
+ expected to fail. When the fail occurs, display the \a message,
+ abort the test, and mark the test as passing. Similar to
+ \c{QEXPECT_FAIL(tag, message, Abort)} in C++.
+
+ If the test is not data-driven, then \a tag must be set to
+ an empty string.
+
+ \sa expectFailContinue()
+ */
+ function expectFail(tag, msg) {
+ if (tag === undefined) {
+ warn("tag argument missing from expectFail()")
+ tag = ""
+ }
+ if (msg === undefined) {
+ warn("message argument missing from expectFail()")
+ msg = ""
+ }
+ if (!qtest_results.expectFail(tag, msg, util.callerFile(), util.callerLine()))
+ throw new Error("QtQuickTest::expectFail")
+ }
+
+ /*!
+ \qmlmethod TestCase::expectFailContinue(tag, message)
+
+ In a data-driven test, marks the row associated with \a tag as
+ expected to fail. When the fail occurs, display the \a message,
+ and then continue the test. Similar to
+ \c{QEXPECT_FAIL(tag, message, Continue)} in C++.
+
+ If the test is not data-driven, then \a tag must be set to
+ an empty string.
+
+ \sa expectFail()
+ */
+ function expectFailContinue(tag, msg) {
+ if (tag === undefined) {
+ warn("tag argument missing from expectFailContinue()")
+ tag = ""
+ }
+ if (msg === undefined) {
+ warn("message argument missing from expectFailContinue()")
+ msg = ""
+ }
+ if (!qtest_results.expectFailContinue(tag, msg, util.callerFile(), util.callerLine()))
+ throw new Error("QtQuickTest::expectFail")
+ }
+
+ /*!
+ \qmlmethod TestCase::warn(message)
+
+ Prints \a message as a warning message. Similar to
+ \c{QWARN(message)} in C++.
+
+ \sa ignoreWarning()
+ */
+ function warn(msg) {
+ if (msg === undefined)
+ msg = ""
+ qtest_results.warn(msg, util.callerFile(), util.callerLine());
+ }
+
+ /*!
+ \qmlmethod TestCase::ignoreWarning(message)
+
+ Marks \a message as an ignored warning message. When it occurs,
+ the warning will not be printed and the test passes. If the message
+ does not occur, then the test will fail. Similar to
+ \c{QTest::ignoreMessage(QtWarningMsg, message)} in C++.
+
+ Since Qt 5.12, \a message can be either a string, or a regular
+ expression providing a pattern of messages to ignore.
+
+ For example, the following snippet will ignore a string warning message:
+ \qml
+ ignoreWarning("Something sort of bad happened")
+ \endqml
+
+ And the following snippet will ignore a regular expression matching a
+ number of possible warning messages:
+ \qml
+ ignoreWarning(new RegExp("[0-9]+ bad things happened"))
+ \endqml
+
+ \note Despite being a JavaScript RegExp object, it will not be
+ interpreted as such; instead, the pattern will be passed to
+ \l QRegularExpression.
+
+ \sa warn()
+ */
+ function ignoreWarning(msg) {
+ if (msg === undefined)
+ msg = ""
+ qtest_results.ignoreWarning(msg)
+ }
+
+ /*!
+ \qmlmethod TestCase::wait(ms)
+
+ Waits for \a ms milliseconds while processing Qt events.
+
+ \sa sleep(), waitForRendering()
+ */
+ function wait(ms) {
+ qtest_results.wait(ms)
+ }
+
+ /*!
+ \qmlmethod TestCase::waitForRendering(item, timeout = 5000)
+
+ Waits for \a timeout milliseconds or until the \a item is rendered by the renderer.
+ Returns true if \c item is rendered in \a timeout milliseconds, otherwise returns false.
+ The default \a timeout value is 5000.
+
+ \sa sleep(), wait()
+ */
+ function waitForRendering(item, timeout) {
+ if (timeout === undefined)
+ timeout = 5000
+ if (!qtest_verifyItem(item, "waitForRendering"))
+ return
+ return qtest_results.waitForRendering(item, timeout)
+ }
+
+ /*!
+ \qmlmethod TestCase::sleep(ms)
+
+ Sleeps for \a ms milliseconds without processing Qt events.
+
+ \sa wait(), waitForRendering()
+ */
+ function sleep(ms) {
+ qtest_results.sleep(ms)
+ }
+
+ /*!
+ \qmlmethod TestCase::keyPress(key, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates pressing a \a key with optional \a modifiers on the currently
+ focused item. If \a delay is larger than 0, the test will wait for
+ \a delay milliseconds.
+
+ The event will be sent to the TestCase window or, in case of multiple windows,
+ to the current active window. See \l QGuiApplication::focusWindow() for more details.
+
+ \b{Note:} At some point you should release the key using keyRelease().
+
+ \sa keyRelease(), keyClick()
+ */
+ function keyPress(key, modifiers, delay) {
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (typeof(key) == "string" && key.length == 1) {
+ if (!qtest_events.keyPressChar(key, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ } else {
+ if (!qtest_events.keyPress(key, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+ }
+
+ /*!
+ \qmlmethod TestCase::keyRelease(key, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates releasing a \a key with optional \a modifiers on the currently
+ focused item. If \a delay is larger than 0, the test will wait for
+ \a delay milliseconds.
+
+ The event will be sent to the TestCase window or, in case of multiple windows,
+ to the current active window. See \l QGuiApplication::focusWindow() for more details.
+
+ \sa keyPress(), keyClick()
+ */
+ function keyRelease(key, modifiers, delay) {
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (typeof(key) == "string" && key.length == 1) {
+ if (!qtest_events.keyReleaseChar(key, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ } else {
+ if (!qtest_events.keyRelease(key, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+ }
+
+ /*!
+ \qmlmethod TestCase::keyClick(key, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates clicking of \a key with optional \a modifiers on the currently
+ focused item. If \a delay is larger than 0, the test will wait for
+ \a delay milliseconds.
+
+ The event will be sent to the TestCase window or, in case of multiple windows,
+ to the current active window. See \l QGuiApplication::focusWindow() for more details.
+
+ \sa keyPress(), keyRelease()
+ */
+ function keyClick(key, modifiers, delay) {
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (typeof(key) == "string" && key.length == 1) {
+ if (!qtest_events.keyClickChar(key, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ } else {
+ if (!qtest_events.keyClick(key, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+ }
+
+ /*!
+ \since 5.10
+ \qmlmethod TestCase::keySequence(keySequence)
+
+ Simulates typing of \a keySequence. The key sequence can be set
+ to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, or
+ it can be described with a string containing a sequence of up to four key
+ presses.
+
+ Each event shall be sent to the TestCase window or, in case of multiple windows,
+ to the current active window. See \l QGuiApplication::focusWindow() for more details.
+
+ \sa keyPress(), keyRelease(), {GNU Emacs Style Key Sequences},
+ {QtQuick::Shortcut::sequence}{Shortcut.sequence}
+ */
+ function keySequence(keySequence) {
+ if (!qtest_events.keySequence(keySequence))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TestCase::mousePress(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates pressing a mouse \a button with optional \a modifiers
+ on an \a item. The position is defined by \a x and \a y.
+ If \a x or \a y are not defined the position will be the center of \a item.
+ If \a delay is specified, the test will wait for the specified amount of
+ milliseconds before the press.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ \sa mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel()
+ */
+ function mousePress(item, x, y, button, modifiers, delay) {
+ if (!qtest_verifyItem(item, "mousePress"))
+ return
+
+ if (button === undefined)
+ button = Qt.LeftButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (x === undefined)
+ x = item.width / 2
+ if (y === undefined)
+ y = item.height / 2
+ if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TestCase::mouseRelease(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates releasing a mouse \a button with optional \a modifiers
+ on an \a item. The position of the release is defined by \a x and \a y.
+ If \a x or \a y are not defined the position will be the center of \a item.
+ If \a delay is specified, the test will wait for the specified amount of
+ milliseconds before releasing the button.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel()
+ */
+ function mouseRelease(item, x, y, button, modifiers, delay) {
+ if (!qtest_verifyItem(item, "mouseRelease"))
+ return
+
+ if (button === undefined)
+ button = Qt.LeftButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (x === undefined)
+ x = item.width / 2
+ if (y === undefined)
+ y = item.height / 2
+ if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TestCase::mouseDrag(item, x, y, dx, dy, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates dragging the mouse on an \a item with \a button pressed and optional \a modifiers
+ The initial drag position is defined by \a x and \a y,
+ and drag distance is defined by \a dx and \a dy. If \a delay is specified,
+ the test will wait for the specified amount of milliseconds before releasing the button.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseWheel()
+ */
+ function mouseDrag(item, x, y, dx, dy, button, modifiers, delay) {
+ if (!qtest_verifyItem(item, "mouseDrag"))
+ return
+
+ if (item.x === undefined || item.y === undefined)
+ return
+ if (button === undefined)
+ button = Qt.LeftButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ var moveDelay = Math.max(1, delay === -1 ? qtest_events.defaultMouseDelay : delay)
+
+ // Divide dx and dy to have intermediate mouseMove while dragging
+ // Fractions of dx/dy need be superior to the dragThreshold
+ // to make the drag works though
+ var intermediateDx = Math.round(dx/3)
+ if (Math.abs(intermediateDx) < (util.dragThreshold + 1))
+ intermediateDx = 0
+ var intermediateDy = Math.round(dy/3)
+ if (Math.abs(intermediateDy) < (util.dragThreshold + 1))
+ intermediateDy = 0
+
+ mousePress(item, x, y, button, modifiers, delay)
+
+ // Trigger dragging by dragging past the drag threshold, but making sure to only drag
+ // along a certain axis if a distance greater than zero was given for that axis.
+ var dragTriggerXDistance = dx > 0 ? (util.dragThreshold + 1) : 0
+ var dragTriggerYDistance = dy > 0 ? (util.dragThreshold + 1) : 0
+ mouseMove(item, x + dragTriggerXDistance, y + dragTriggerYDistance, moveDelay, button)
+ if (intermediateDx !== 0 || intermediateDy !== 0) {
+ mouseMove(item, x + intermediateDx, y + intermediateDy, moveDelay, button)
+ mouseMove(item, x + 2*intermediateDx, y + 2*intermediateDy, moveDelay, button)
+ }
+ mouseMove(item, x + dx, y + dy, moveDelay, button)
+ mouseRelease(item, x + dx, y + dy, button, modifiers, delay)
+ }
+
+ /*!
+ \qmlmethod TestCase::mouseClick(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates clicking a mouse \a button with optional \a modifiers
+ on an \a item. The position of the click is defined by \a x and \a y.
+ If \a x and \a y are not defined the position will be the center of \a item.
+ If \a delay is specified, the test will wait for the specified amount of
+ milliseconds before pressing and before releasing the button.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ \sa mousePress(), mouseRelease(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel()
+ */
+ function mouseClick(item, x, y, button, modifiers, delay) {
+ if (!qtest_verifyItem(item, "mouseClick"))
+ return
+
+ if (button === undefined)
+ button = Qt.LeftButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (x === undefined)
+ x = item.width / 2
+ if (y === undefined)
+ y = item.height / 2
+ if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TestCase::mouseDoubleClickSequence(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates the full sequence of events generated by double-clicking a mouse
+ \a button with optional \a modifiers on an \a item.
+
+ This method reproduces the sequence of mouse events generated when a user makes
+ a double click: Press-Release-Press-DoubleClick-Release.
+
+ The position of the click is defined by \a x and \a y.
+ If \a x and \a y are not defined the position will be the center of \a item.
+ If \a delay is specified, the test will wait for the specified amount of
+ milliseconds before pressing and before releasing the button.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ This QML method was introduced in Qt 5.5.
+
+ \sa mousePress(), mouseRelease(), mouseClick(), mouseMove(), mouseDrag(), mouseWheel()
+ */
+ function mouseDoubleClickSequence(item, x, y, button, modifiers, delay) {
+ if (!qtest_verifyItem(item, "mouseDoubleClickSequence"))
+ return
+
+ if (button === undefined)
+ button = Qt.LeftButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (delay == undefined)
+ delay = -1
+ if (x === undefined)
+ x = item.width / 2
+ if (y === undefined)
+ y = item.height / 2
+ if (!qtest_events.mouseDoubleClickSequence(item, x, y, button, modifiers, delay))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TestCase::mouseMove(item, x = item.width / 2, y = item.height / 2, delay = -1, buttons = Qt.NoButton)
+
+ Moves the mouse pointer to the position given by \a x and \a y within
+ \a item, while holding \a buttons if given. Since Qt 6.0, if \a x and
+ \a y are not defined, the position will be the center of \a item.
+
+ If a \a delay (in milliseconds) is given, the test will wait before
+ moving the mouse pointer.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ \sa mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseDrag(), mouseWheel()
+ */
+ function mouseMove(item, x, y, delay, buttons) {
+ if (!qtest_verifyItem(item, "mouseMove"))
+ return
+
+ if (delay == undefined)
+ delay = -1
+ if (buttons == undefined)
+ buttons = Qt.NoButton
+ if (x === undefined)
+ x = item.width / 2
+ if (y === undefined)
+ y = item.height / 2
+ if (!qtest_events.mouseMove(item, x, y, delay, buttons))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TestCase::mouseWheel(item, x, y, xDelta, yDelta, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
+
+ Simulates rotating the mouse wheel on an \a item with \a button pressed and optional \a modifiers.
+ The position of the wheel event is defined by \a x and \a y.
+ If \a delay is specified, the test will wait for the specified amount of milliseconds before releasing the button.
+
+ The position given by \a x and \a y is transformed from the co-ordinate
+ system of \a item into window co-ordinates and then delivered.
+ If \a item is obscured by another item, or a child of \a item occupies
+ that position, then the event will be delivered to the other item instead.
+
+ The \a xDelta and \a yDelta contain the wheel rotation distance in eighths of a degree. see \l QWheelEvent::angleDelta() for more details.
+
+ \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseDrag(), QWheelEvent::angleDelta()
+ */
+ function mouseWheel(item, x, y, xDelta, yDelta, buttons, modifiers, delay) {
+ if (!qtest_verifyItem(item, "mouseWheel"))
+ return
+
+ if (delay == undefined)
+ delay = -1
+ if (buttons == undefined)
+ buttons = Qt.NoButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifier
+ if (xDelta == undefined)
+ xDelta = 0
+ if (yDelta == undefined)
+ yDelta = 0
+ if (!qtest_events.mouseWheel(item, x, y, buttons, modifiers, xDelta, yDelta, delay))
+ qtest_fail("window not shown", 2)
+ }
+
+ /*!
+ \qmlmethod TouchEventSequence TestCase::touchEvent(object item)
+
+ \since 5.9
+
+ Begins a sequence of touch events through a simulated touchscreen (QPointingDevice).
+ Events are delivered to the window containing \a item.
+
+ The returned object is used to enumerate events to be delivered through a single
+ QTouchEvent. Touches are delivered to the window containing the TestCase unless
+ otherwise specified.
+
+ \code
+ Rectangle {
+ width: 640; height: 480
+
+ MultiPointTouchArea {
+ id: area
+ anchors.fill: parent
+
+ property bool touched: false
+
+ onPressed: touched = true
+ }
+
+ TestCase {
+ name: "ItemTests"
+ when: windowShown
+ id: test1
+
+ function test_touch() {
+ var touch = touchEvent(area);
+ touch.press(0, area, 10, 10);
+ touch.commit();
+ verify(area.touched);
+ }
+ }
+ }
+ \endcode
+
+ \sa TouchEventSequence::press(), TouchEventSequence::move(), TouchEventSequence::release(), TouchEventSequence::stationary(), TouchEventSequence::commit(), QInputDevice::DeviceType
+ */
+
+ function touchEvent(item) {
+ if (!qtest_verifyItem(item, "touchEvent"))
+ return
+
+ return {
+ _defaultItem: item,
+ _sequence: qtest_events.touchEvent(item),
+
+ press: function (id, target, x, y) {
+ if (!target)
+ target = this._defaultItem;
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::press", 1);
+ if (x === undefined)
+ x = target.width / 2;
+ if (y === undefined)
+ y = target.height / 2;
+ this._sequence.press(id, target, x, y);
+ return this;
+ },
+
+ move: function (id, target, x, y) {
+ if (!target)
+ target = this._defaultItem;
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::move", 1);
+ if (x === undefined)
+ x = target.width / 2;
+ if (y === undefined)
+ y = target.height / 2;
+ this._sequence.move(id, target, x, y);
+ return this;
+ },
+
+ stationary: function (id) {
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::stationary", 1);
+ this._sequence.stationary(id);
+ return this;
+ },
+
+ release: function (id, target, x, y) {
+ if (!target)
+ target = this._defaultItem;
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::release", 1);
+ if (x === undefined)
+ x = target.width / 2;
+ if (y === undefined)
+ y = target.height / 2;
+ this._sequence.release(id, target, x, y);
+ return this;
+ },
+
+ commit: function () {
+ this._sequence.commit();
+ return this;
+ }
+ };
+ }
+
+ // Functions that can be overridden in subclasses for init/cleanup duties.
+ /*!
+ \qmlmethod TestCase::initTestCase()
+
+ This function is called before any other test functions in the
+ \l TestCase type. The default implementation does nothing.
+ The application can provide its own implementation to perform
+ test case initialization.
+
+ \sa cleanupTestCase(), init()
+ */
+ function initTestCase() {}
+
+ /*!
+ \qmlmethod TestCase::cleanupTestCase()
+
+ This function is called after all other test functions in the
+ \l TestCase type have completed. The default implementation
+ does nothing. The application can provide its own implementation
+ to perform test case cleanup.
+
+ \sa initTestCase(), cleanup()
+ */
+ function cleanupTestCase() {}
+
+ /*!
+ \qmlmethod TestCase::init()
+
+ This function is called before each test function that is
+ executed in the \l TestCase type. The default implementation
+ does nothing. The application can provide its own implementation
+ to perform initialization before each test function.
+
+ \sa cleanup(), initTestCase()
+ */
+ function init() {}
+
+ /*!
+ \qmlmethod TestCase::cleanup()
+
+ This function is called after each test function that is
+ executed in the \l TestCase type. The default implementation
+ does nothing. The application can provide its own implementation
+ to perform cleanup after each test function.
+
+ \sa init(), cleanupTestCase()
+ */
+ function cleanup() {}
+
+ /*! \internal */
+ function qtest_verifyItem(item, method) {
+ try {
+ if (!(item instanceof Item) &&
+ !(item instanceof Window)) {
+ // it's a QObject, but not a type
+ qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 2);
+ return false;
+ }
+ } catch (e) { // it's not a QObject
+ qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 3);
+ return false;
+ }
+
+ return true;
+ }
+
+ /*! \internal */
+ function qtest_runInternal(prop, arg) {
+ try {
+ qtest_testCaseResult = testCase[prop](arg)
+ } catch (e) {
+ qtest_testCaseResult = []
+ if (e.message.indexOf("QtQuickTest::") != 0) {
+ // Test threw an unrecognized exception - fail.
+ qtest_results.fail("Uncaught exception: " + e.message,
+ e.fileName, e.lineNumber)
+ }
+ }
+ return !qtest_results.failed
+ }
+
+ /*! \internal */
+ function qtest_runFunction(prop, arg) {
+ qtest_runInternal("init")
+ if (!qtest_results.skipped) {
+ qtest_runInternal(prop, arg)
+ qtest_results.finishTestData()
+ qtest_runInternal("cleanup")
+ qtest_destroyTemporaryObjects()
+ qtest_results.finishTestDataCleanup()
+ // wait(0) will call processEvents() so objects marked for deletion
+ // in the test function will be deleted.
+ wait(0)
+ }
+ }
+
+ /*! \internal */
+ function qtest_runBenchmarkFunction(prop, arg) {
+ qtest_results.startMeasurement()
+ do {
+ qtest_results.beginDataRun()
+ do {
+ // Run the initialization function.
+ qtest_runInternal("init")
+ if (qtest_results.skipped)
+ break
+
+ // Execute the benchmark function.
+ if (prop.indexOf("benchmark_once_") != 0)
+ qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag)
+ else
+ qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
+ while (!qtest_results.isBenchmarkDone()) {
+ var success = qtest_runInternal(prop, arg)
+ qtest_results.finishTestData()
+ if (!success)
+ break
+ qtest_results.nextBenchmark()
+ }
+ qtest_results.stopBenchmark()
+
+ // Run the cleanup function.
+ qtest_runInternal("cleanup")
+ qtest_results.finishTestDataCleanup()
+ // wait(0) will call processEvents() so objects marked for deletion
+ // in the test function will be deleted.
+ wait(0)
+ } while (!qtest_results.measurementAccepted())
+ qtest_results.endDataRun()
+ } while (qtest_results.needsMoreMeasurements())
+ }
+
+ /*! \internal */
+ function qtest_run() {
+ if (TestLogger.log_start_test()) {
+ qtest_results.reset()
+ qtest_results.testCaseName = name
+ qtest_results.startLogging()
+ } else {
+ qtest_results.testCaseName = name
+ }
+ running = true
+
+ // Check the run list to see if this class is mentioned.
+ let checkNames = false
+ let testsToRun = {} // explicitly provided function names to run and their tags for data-driven tests
+
+ if (qtest_results.functionsToRun.length > 0) {
+ checkNames = true
+ var found = false
+
+ if (name.length > 0) {
+ for (var index in qtest_results.functionsToRun) {
+ let caseFuncName = qtest_results.functionsToRun[index]
+ if (caseFuncName.indexOf(name + "::") != 0)
+ continue
+
+ found = true
+ let funcName = caseFuncName.substring(name.length + 2)
+
+ if (!(funcName in testsToRun))
+ testsToRun[funcName] = []
+
+ let tagName = qtest_results.tagsToRun[index]
+ if (tagName.length > 0) // empty tags mean run all rows
+ testsToRun[funcName].push(tagName)
+ }
+ }
+ if (!found) {
+ completed = true
+ if (!TestLogger.log_complete_test(qtest_testId)) {
+ qtest_results.stopLogging()
+ Qt.quit()
+ }
+ qtest_results.testCaseName = ""
+ return
+ }
+ }
+
+ // Run the initTestCase function.
+ qtest_results.functionName = "initTestCase"
+ var runTests = true
+ if (!qtest_runInternal("initTestCase"))
+ runTests = false
+ qtest_results.finishTestData()
+ qtest_results.finishTestDataCleanup()
+ qtest_results.finishTestFunction()
+
+ // Run the test methods.
+ var testList = []
+ if (runTests) {
+ for (var prop in testCase) {
+ if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
+ continue
+ var tail = prop.lastIndexOf("_data");
+ if (tail != -1 && tail == (prop.length - 5))
+ continue
+ testList.push(prop)
+ }
+ testList.sort()
+ }
+
+ for (var index in testList) {
+ var prop = testList[index]
+
+ if (checkNames && !(prop in testsToRun))
+ continue
+
+ var datafunc = prop + "_data"
+ var isBenchmark = (prop.indexOf("benchmark_") == 0)
+ qtest_results.functionName = prop
+
+ if (!(datafunc in testCase))
+ datafunc = "init_data";
+
+ if (datafunc in testCase) {
+ if (qtest_runInternal(datafunc)) {
+ var table = qtest_testCaseResult
+ var haveData = false
+
+ let checkTags = (checkNames && testsToRun[prop].length > 0)
+
+ qtest_results.initTestTable()
+ for (var index in table) {
+ haveData = true
+ var row = table[index]
+ if (!row.tag)
+ row.tag = "row " + index // Must have something
+ if (checkTags) {
+ let tags = testsToRun[prop]
+ let tagIdx = tags.indexOf(row.tag)
+ if (tagIdx < 0)
+ continue
+ tags.splice(tagIdx, 1)
+ }
+ qtest_results.dataTag = row.tag
+ if (isBenchmark)
+ qtest_runBenchmarkFunction(prop, row)
+ else
+ qtest_runFunction(prop, row)
+ qtest_results.dataTag = ""
+ qtest_results.skipped = false
+ }
+ if (!haveData) {
+ if (datafunc === "init_data")
+ qtest_runFunction(prop, null, isBenchmark)
+ else
+ qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()"
+ , util.callerFile(), util.callerLine());
+ }
+ qtest_results.clearTestTable()
+ }
+ } else if (isBenchmark) {
+ qtest_runBenchmarkFunction(prop, null, isBenchmark)
+ } else {
+ qtest_runFunction(prop, null, isBenchmark)
+ }
+ qtest_results.finishTestFunction()
+ qtest_results.skipped = false
+
+ if (checkNames && testsToRun[prop].length <= 0)
+ delete testsToRun[prop]
+ }
+
+ // Run the cleanupTestCase function.
+ qtest_results.skipped = false
+ qtest_results.functionName = "cleanupTestCase"
+ qtest_runInternal("cleanupTestCase")
+
+ // Complain about missing functions that we were supposed to run.
+ if (checkNames) {
+ let missingTests = []
+ for (var func in testsToRun) {
+ let caseFuncName = name + '::' + func
+ let tags = testsToRun[func]
+ if (tags.length <= 0)
+ missingTests.push(caseFuncName)
+ else
+ for (var i in tags)
+ missingTests.push(caseFuncName + ':' + tags[i])
+ }
+ missingTests.sort()
+ if (missingTests.length > 0)
+ qtest_results.fail("Could not find test functions: " + missingTests, "", 0)
+ }
+
+ // Clean up and exit.
+ running = false
+ completed = true
+ qtest_results.finishTestData()
+ qtest_results.finishTestDataCleanup()
+ qtest_results.finishTestFunction()
+ qtest_results.functionName = ""
+
+ // Stop if there are no more tests to be run.
+ if (!TestLogger.log_complete_test(qtest_testId)) {
+ qtest_results.stopLogging()
+ Qt.quit()
+ }
+ qtest_results.testCaseName = ""
+ }
+
+ onWhenChanged: {
+ if (when != qtest_prevWhen) {
+ qtest_prevWhen = when
+ if (when && !completed && !running && qtest_componentCompleted)
+ qtest_run()
+ }
+ }
+
+ onOptionalChanged: {
+ if (!completed) {
+ if (optional)
+ TestLogger.log_optional_test(qtest_testId)
+ else
+ TestLogger.log_mandatory_test(qtest_testId)
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/aliasLookup.qml b/tests/auto/qml/qmlcppcodegen/data/aliasLookup.qml
new file mode 100644
index 0000000000..defea11fc1
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/aliasLookup.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ id: self
+ property real foo: 12.3
+ property alias a: self.foo
+ property string t: Math.round(self.a)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/anchorsFill.qml b/tests/auto/qml/qmlcppcodegen/data/anchorsFill.qml
new file mode 100644
index 0000000000..3d18a40434
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/anchorsFill.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Item {
+ width: 234
+ Item {
+ anchors.fill: parent
+ }
+
+ Item {
+ width: 47
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/array.qml b/tests/auto/qml/qmlcppcodegen/data/array.qml
new file mode 100644
index 0000000000..8ea957ea73
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/array.qml
@@ -0,0 +1,6 @@
+import QML
+
+QtObject {
+ property var values1: [1, 2, 3]
+ property var values2: []
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/asCast.qml b/tests/auto/qml/qmlcppcodegen/data/asCast.qml
new file mode 100644
index 0000000000..6f8e274827
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/asCast.qml
@@ -0,0 +1,50 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+pragma Strict
+import QtQuick
+
+Item {
+ QtObject { id: object }
+ Item { id: item }
+ Rectangle { id: rectangle }
+ Dummy { id: dummy }
+
+ property QtObject objectAsObject: object as QtObject
+ property QtObject objectAsItem: object as Item
+ property QtObject objectAsRectangle: object as Rectangle
+ property QtObject objectAsDummy: object as Dummy
+
+ property QtObject itemAsObject: item as QtObject
+ property QtObject itemAsItem: item as Item
+ property QtObject itemAsRectangle: item as Rectangle
+ property QtObject itemAsDummy: item as Dummy
+
+ property QtObject rectangleAsObject: rectangle as QtObject
+ property QtObject rectangleAsItem: rectangle as Item
+ property QtObject rectangleAsRectangle: rectangle as Rectangle
+ property QtObject rectangleAsDummy: rectangle as Dummy
+
+ property QtObject dummyAsObject: dummy as QtObject
+ property QtObject dummyAsItem: dummy as Item
+ property QtObject dummyAsRectangle: dummy as Rectangle
+ property QtObject dummyAsDummy: dummy as Dummy
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/attachedBaseEnum.qml b/tests/auto/qml/qmlcppcodegen/data/attachedBaseEnum.qml
new file mode 100644
index 0000000000..3f6a3fc307
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/attachedBaseEnum.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+MouseArea {
+ drag.axis: Drag.YAxis
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/bindToValueType.qml b/tests/auto/qml/qmlcppcodegen/data/bindToValueType.qml
new file mode 100644
index 0000000000..2ce90fc84d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/bindToValueType.qml
@@ -0,0 +1,9 @@
+pragma Strict
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.Button {
+ icon.color: "green"
+ property color a: icon.color
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
new file mode 100644
index 0000000000..a4211d59be
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
@@ -0,0 +1,87 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#include "birthdayparty.h"
+
+BirthdayParty::BirthdayParty(QObject *parent)
+ : QObject(parent), m_host(nullptr)
+{
+}
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *c)
+{
+ if (c != m_host) {
+ m_host = c;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return {this, &m_guests};
+}
+
+int BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(int index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::invite(const QString &name)
+{
+ auto *person = new Person(this);
+ person->setName(name);
+ m_guests.append(person);
+}
+
+BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
+{
+ return new BirthdayPartyAttached(object);
+}
+
+BirthdayPartyAttached::BirthdayPartyAttached(QObject *parent)
+ : QObject(parent), m_rsvp(QDateTime(QDate(1911, 3, 4), QTime()))
+{
+}
+
+QDateTime BirthdayPartyAttached::rsvp() const
+{
+ return m_rsvp.value();
+}
+
+void BirthdayPartyAttached::setRsvp(const QDateTime &rsvp)
+{
+ m_rsvp.setValue(rsvp);
+}
+
+QBindable<QDateTime> BirthdayPartyAttached::rsvpBindable()
+{
+ return QBindable<QDateTime>(&m_rsvp);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
new file mode 100644
index 0000000000..2310352d48
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
@@ -0,0 +1,117 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include <QObject>
+#include <QQmlListProperty>
+#include <QProperty>
+#include <QBindable>
+#include <QDateTime>
+#include "person.h"
+
+class BirthdayPartyAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QDateTime rsvp READ rsvp WRITE setRsvp BINDABLE rsvpBindable)
+ QML_ANONYMOUS
+public:
+ BirthdayPartyAttached(QObject *parent);
+
+ QDateTime rsvp() const;
+ void setRsvp(const QDateTime &rsvp);
+ QBindable<QDateTime> rsvpBindable();
+
+private:
+ QProperty<QDateTime> m_rsvp;
+};
+
+class BirthDayPartyExtended : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int eee READ eee WRITE setEee NOTIFY eeeChanged)
+public:
+
+ BirthDayPartyExtended(QObject *parent) : QObject(parent) {}
+
+ int eee() const { return m_eee; }
+ void setEee(int eee)
+ {
+ if (eee != m_eee) {
+ m_eee = eee;
+ emit eeeChanged();
+ }
+ }
+
+signals:
+ void eeeChanged();
+
+private:
+ int m_eee = 25;
+};
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
+ QML_ELEMENT
+ QML_ATTACHED(BirthdayPartyAttached)
+ QML_EXTENDED(BirthDayPartyExtended)
+public:
+ BirthdayParty(QObject *parent = nullptr);
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ int guestCount() const;
+ Person *guest(int) const;
+
+ Q_INVOKABLE void invite(const QString &name);
+ static BirthdayPartyAttached *qmlAttachedProperties(QObject *object);
+
+signals:
+ void hostChanged();
+
+private:
+ Person *m_host;
+ QList<Person *> m_guests;
+};
+
+class NastyBase : public QObject {
+public:
+ NastyBase(QObject *parent) : QObject(parent) {}
+};
+
+class Nasty : public NastyBase
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_ATTACHED(Nasty)
+
+public:
+ Nasty(QObject *parent = nullptr) : NastyBase(parent) {}
+ static Nasty *qmlAttachedProperties(QObject *object) { return new Nasty(object); }
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/callContextPropertyLookupResult.qml b/tests/auto/qml/qmlcppcodegen/data/callContextPropertyLookupResult.qml
new file mode 100644
index 0000000000..f371b56909
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/callContextPropertyLookupResult.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property Component c: Qt.createComponent("Dummy.qml")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/childobject.qml b/tests/auto/qml/qmlcppcodegen/data/childobject.qml
new file mode 100644
index 0000000000..3775ee16bc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/childobject.qml
@@ -0,0 +1,15 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ property ObjectWithMethod child: ObjectWithMethod {
+ objectName: "kraut"
+ }
+ objectName: child.objectName
+ property int doneThing: child.doThing()
+ property int usingFinal: child.fff
+
+ function setChildObjectName(name: string) {
+ child.objectName = name
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/colorAsVariant.qml b/tests/auto/qml/qmlcppcodegen/data/colorAsVariant.qml
new file mode 100644
index 0000000000..1162a2f5b3
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/colorAsVariant.qml
@@ -0,0 +1,7 @@
+import QtQuick
+import QtQuick.Templates as T
+
+T.Button {
+ property color a: "green"
+ icon.color: a
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/colorString.qml b/tests/auto/qml/qmlcppcodegen/data/colorString.qml
new file mode 100644
index 0000000000..3c0bff257a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/colorString.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import QtQuick
+
+QtObject {
+ id: self
+ property color c: "#aabbcc"
+ property color d: "#ccbbaa"
+ property color e: "#" + "112233"
+ Component.onCompleted: {
+ c = "#dddddd";
+ self.d = "#aaaaaa";
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/componentReturnType.qml b/tests/auto/qml/qmlcppcodegen/data/componentReturnType.qml
new file mode 100644
index 0000000000..69c237c97d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/componentReturnType.qml
@@ -0,0 +1,15 @@
+import QtQml.Models
+import QtQuick
+
+Item {
+ id: root
+ property int count: 0
+ Component {
+ id: delegate
+ Item {
+ Component.onCompleted: root.count++
+ }
+ }
+
+ Repeater { model: 10; delegate: delegate }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/compositeTypeMethod.qml b/tests/auto/qml/qmlcppcodegen/data/compositeTypeMethod.qml
new file mode 100644
index 0000000000..9bae4c575c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/compositeTypeMethod.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Item {
+ id: root
+ signal foo()
+
+ Timer {
+ interval: 10
+ running: true
+ onTriggered: root.foo()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/compositesingleton.qml b/tests/auto/qml/qmlcppcodegen/data/compositesingleton.qml
new file mode 100644
index 0000000000..42db13154e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/compositesingleton.qml
@@ -0,0 +1,8 @@
+import QtQml
+import QmlBench
+
+QtObject {
+ property real x: Globals.realProp
+ property real y: Globals.intProp
+ property bool smooth: Globals.boolProp
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/construct.qml b/tests/auto/qml/qmlcppcodegen/data/construct.qml
new file mode 100644
index 0000000000..19d3322992
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/construct.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+QtObject {
+ property var foo: new Error("bar")
+ property int aaa: 12
+
+ function ouch() {
+ aaa = 13;
+ throw new Error("ouch")
+ aaa = 25;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/contextParam.qml b/tests/auto/qml/qmlcppcodegen/data/contextParam.qml
new file mode 100644
index 0000000000..63c057ca79
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/contextParam.qml
@@ -0,0 +1,5 @@
+import QML
+
+QtObject {
+ property int foo: contextParam.foo
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions.qml b/tests/auto/qml/qmlcppcodegen/data/conversions.qml
new file mode 100644
index 0000000000..dc76404f2f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/conversions.qml
@@ -0,0 +1,130 @@
+import QML
+
+QtObject {
+ id: self
+ property var aNull: null
+ property var anInt: 5
+ property var aZero: 0
+ property var anArray: []
+ property var anObject: ({a: 12, b: 14, c: "somestring"})
+
+ property int cmpEqInt: {
+ if ({} == 12)
+ return 8;
+ if ([] == 12)
+ return 2;
+ if (null == 12)
+ return 3;
+ if (undefined == 12)
+ return 4;
+ if (12 == 12)
+ return 17;
+ return 5;
+ }
+
+ property bool nullIsNull: aNull == null
+ property bool intIsNull: anInt == null
+ property bool zeroIsNull: aZero == null
+ property bool arrayIsNull: anArray == null
+ property bool objectIsNull: anObject == null
+
+ property bool nullIsNotNull: aNull != null
+ property bool intIsNotNull: anInt != null
+ property bool zeroIsNotNull: aZero != null
+ property bool arrayIsNotNull: anArray != null
+ property bool objectIsNotNull: anObject != null
+
+ property bool boolEqualsBool: nullIsNull == zeroIsNull
+ property bool boolNotEqualsBool: nullIsNull != zeroIsNull
+
+ property var aInObject: "a" in anObject
+ property var lengthInArray: "length" in anArray
+ property var fooInNumber: "foo" in 12
+ property var numberInObject: 12 in anObject
+ property var numberInArray: 2 in [1, 12, 3]
+
+ property real varPlusVar: aInObject + lengthInArray
+ property real varMinusVar: lengthInArray - fooInNumber
+ property real varTimesVar: fooInNumber * numberInObject
+ property real varDivVar: numberInObject / numberInArray
+
+ property var stringPlusString: "12" + "20"
+ property var stringMinusString: "12" - "20"
+ property var stringTimesString: "12" * "20"
+ property var stringDivString: "12" / "20"
+
+ property string uglyString: "with\nnewline" + 'with"quot' + "with\\slashes";
+
+ property QtObject nullObject1: aInObject ? null : self
+ property QtObject nullObject2: {
+ if (lengthInArray)
+ return self
+ if (aInObject)
+ return null;
+ return undefined;
+ }
+
+ property var doneStuff
+ function doStuff(stuff) {
+ var a = {g: 0, h: 5};
+ var b = {i: 6, j: 7};
+ var c = {k: 9, l: 9};
+
+ a.g = 4;
+
+ doneStuff = a.g + b.i + c.l
+ return stuff[3] ? stuff[3] : stuff.z
+ }
+
+ property var passObjectLiteral: doStuff({"x": 13, "y": 17, "z": 11})
+ property var passArrayLiteral: doStuff([1, 2, 3, 4])
+
+ property bool neNull: {
+ var a = anArray[12]
+ if (a)
+ return true;
+ else
+ return false;
+ }
+
+ property bool eqNull: {
+ var a = anArray[12]
+ if (!a)
+ return true;
+ else
+ return false;
+ }
+
+ property string undefinedType: typeof undefined
+ property string booleanType: typeof true
+ property string numberType: typeof 12
+ property string stringType: typeof "bah"
+ property string objectType: typeof null
+ property string symbolType: typeof Symbol("baz")
+
+ property var modulos: [
+ -20 % -1,
+ 0 % 1,
+ 20 % 4.4,
+ -4.4 % true,
+ false % "11",
+ undefined % null,
+ {} % []
+ ]
+
+ property var unaryOps: {
+ var a = stringPlusString;
+ var b = stringPlusString;
+ var c = +stringPlusString;
+ var d = -stringPlusString;
+ var e = a++;
+ var f = b--;
+ return [ a, b, c, d, e, f ];
+ }
+
+ function retUndefined() {
+ var a = 5 + 12;
+ if (false)
+ return a;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
new file mode 100644
index 0000000000..e7bde6c3e6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
@@ -0,0 +1,93 @@
+import QtQuick
+import QtTest
+
+Item {
+ id: spy
+ visible: false
+
+ TestUtil {
+ id: util
+ }
+
+ property var target: null
+ property string signalName: ""
+ readonly property alias count: spy.qtest_count
+ readonly property alias valid:spy.qtest_valid
+ readonly property alias signalArguments:spy.qtest_signalArguments
+
+ function clear() {
+ qtest_count = 0
+ qtest_expectedCount = 0
+ qtest_signalArguments = []
+ }
+
+ function wait(timeout) {
+ if (timeout === undefined)
+ timeout = 5000
+ var expected = ++qtest_expectedCount
+ var i = 0
+ while (i < timeout && qtest_count < expected) {
+ qtest_results.wait(50)
+ i += 50
+ }
+ var success = (qtest_count >= expected)
+ if (!qtest_results.verify(success, "wait for signal " + signalName, util.callerFile(), util.callerLine()))
+ return false;
+ }
+
+ TestResult { id: qtest_results }
+
+ onTargetChanged: {
+ qtest_update()
+ }
+ onSignalNameChanged: {
+ qtest_update()
+ }
+
+ property var qtest_prevTarget: null
+ property string qtest_prevSignalName: ""
+ property int qtest_expectedCount: 0
+ property var qtest_signalArguments:[]
+ property int qtest_count: 0
+ property bool qtest_valid:false
+
+ function qtest_update() {
+ if (qtest_prevTarget != null) {
+ var prevHandlerName = qtest_signalHandlerName(qtest_prevSignalName)
+ var prevFunc = qtest_prevTarget[prevHandlerName]
+ if (prevFunc)
+ prevFunc.disconnect(spy.qtest_activated)
+ qtest_prevTarget = null
+ qtest_prevSignalName = ""
+ }
+ if (target != null && signalName != "") {
+ var func = target[signalName]
+ if (typeof func !== "function") {
+ func = target[qtest_signalHandlerName(signalName)]
+ }
+ if (func === undefined) {
+ spy.qtest_valid = false
+ console.log("Signal '" + signalName + "' not found")
+ } else {
+ qtest_prevTarget = target
+ qtest_prevSignalName = signalName
+ func.connect(spy.qtest_activated)
+ spy.qtest_valid = true
+ spy.qtest_signalArguments = []
+ }
+ } else {
+ spy.qtest_valid = false
+ }
+ }
+
+ function qtest_activated() {
+ ++qtest_count
+ spy.qtest_signalArguments[spy.qtest_signalArguments.length] = [1, 2, 3]
+ }
+
+ function qtest_signalHandlerName(sn) {
+ 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/cppbaseclass.h b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
new file mode 100644
index 0000000000..e518ea0299
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
@@ -0,0 +1,63 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#ifndef CPPBASECLASS_H
+#define CPPBASECLASS_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqml.h>
+
+class CppBaseClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int cppProp MEMBER cppProp BINDABLE cppPropBindable FINAL)
+ Q_PROPERTY(int cppProp2 MEMBER cppProp2 BINDABLE cppProp2Bindable FINAL)
+ QML_ELEMENT
+public:
+ QProperty<int> cppProp;
+ QBindable<int> cppPropBindable() { return QBindable<int>(&cppProp); }
+
+ QProperty<int> cppProp2;
+ QBindable<int> cppProp2Bindable() { return QBindable<int>(&cppProp2); }
+
+ Q_INVOKABLE void doCall(QObject *foo);
+};
+
+inline void CppBaseClass::doCall(QObject *foo)
+{
+ cppProp = foo ? 17 : 18;
+}
+
+class CppSingleton : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ explicit CppSingleton(QObject *parent = nullptr) : QObject(parent)
+ {
+ setObjectName(QStringLiteral("ItIsTheSingleton"));
+ }
+};
+
+#endif // CPPBASECLASS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/curlygrouped.qml b/tests/auto/qml/qmlcppcodegen/data/curlygrouped.qml
new file mode 100644
index 0000000000..0f0e8c03f2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/curlygrouped.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import QtQuick
+
+Item {
+ Item { id: foo }
+ anchors { left: foo.left }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/deadShoeSize.qml b/tests/auto/qml/qmlcppcodegen/data/deadShoeSize.qml
new file mode 100644
index 0000000000..4dddab900a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/deadShoeSize.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+Person {
+ shoeSize: pain
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dialog.qml b/tests/auto/qml/qmlcppcodegen/data/dialog.qml
new file mode 100644
index 0000000000..d33a082741
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dialog.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.Basic
+
+T.Dialog {
+ id: control
+ header: Label {
+ background: Rectangle {
+ width: parent.width + 1
+ height: parent.height - 1
+ }
+ }
+
+ property var n: 10
+ property var a: {
+ var x = 1;
+ for (var i=0; i<n; ++i)
+ x = x + x;
+ return x;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicscene.qml b/tests/auto/qml/qmlcppcodegen/data/dynamicscene.qml
new file mode 100644
index 0000000000..7699e7f2be
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dynamicscene.qml
@@ -0,0 +1,33 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQml
+
+QtObject {
+ id: self
+ onObjectNameChanged: {
+ try {
+ Qt.createQmlObject("1", self, 'CustomObject');
+ } catch(err) {
+ }
+ }
+}
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumInvalid.qml b/tests/auto/qml/qmlcppcodegen/data/enumInvalid.qml
new file mode 100644
index 0000000000..a14955d3e2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumInvalid.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ property bool c: Qt.red > 4
+ property bool d: Qt.red < 2
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumScope.qml b/tests/auto/qml/qmlcppcodegen/data/enumScope.qml
new file mode 100644
index 0000000000..d385346c87
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumScope.qml
@@ -0,0 +1,5 @@
+import QtQuick.Layouts
+
+GridLayout {
+ flow: GridLayout.TopToBottom
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumsInOtherObject.qml b/tests/auto/qml/qmlcppcodegen/data/enumsInOtherObject.qml
new file mode 100644
index 0000000000..5cf620ba68
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumsInOtherObject.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ property Enums app: Enums {
+ appState: 0
+ }
+
+ property string color: app.color
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumsUser.qml b/tests/auto/qml/qmlcppcodegen/data/enumsUser.qml
new file mode 100644
index 0000000000..13e26737b7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumsUser.qml
@@ -0,0 +1,8 @@
+pragma Strict
+import TestTypes
+
+QtObject {
+ function getEnum(): int {
+ return Test.AA + Test.BB + Test.CC
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/equalsUndefined.qml b/tests/auto/qml/qmlcppcodegen/data/equalsUndefined.qml
new file mode 100644
index 0000000000..86a2568b21
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/equalsUndefined.qml
@@ -0,0 +1,18 @@
+import QtQuick
+
+Item {
+ function wait(timeout) {
+ if (timeout === undefined)
+ timeout = 5000
+
+ var i = 0;
+ while (i < timeout) {
+ i += 50
+ }
+
+ return i
+ }
+
+ property var a: wait(10)
+ property var b: wait()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/excessiveParameters.qml b/tests/auto/qml/qmlcppcodegen/data/excessiveParameters.qml
new file mode 100644
index 0000000000..1b801d4d02
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/excessiveParameters.qml
@@ -0,0 +1,14 @@
+import QtQml
+
+QtObject {
+ id: root
+ signal testSignal(string a, int b, string c, bool d, bool e, real f, real g, bool h, int i, int j, string k, int l, string m, string n)
+ signal foo()
+ onTestSignal: foo()
+
+ property Timer timer: Timer {
+ interval: 10
+ running: true
+ onTriggered: root.testSignal("a", 1, "b", true, true, 0.1, 0.1, true, 1, 1, "a", 1, "a", "a")
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/extendedTypes.qml b/tests/auto/qml/qmlcppcodegen/data/extendedTypes.qml
new file mode 100644
index 0000000000..a7012e1b29
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/extendedTypes.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ property int a: Qt.LeftButton + 5
+ property size b: Qt.size(10, 20)
+ property int c: b.width + b.height
+ property string d: b.toString();
+ property int e: Locale.ImperialUKSystem
+
+ Component.onCompleted: console.log(a, b, c)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml
new file mode 100644
index 0000000000..d1af90c2e6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml
@@ -0,0 +1,15 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ property string attachedForNonObject: objectName.Component.objectName
+ property string attachedForNasty: Nasty.objectName
+
+ property Nasty nasty: Nasty {
+ objectName: Component.objectName
+ }
+
+ onFooBar: console.log("nope")
+
+ function doesReturnValue() { return 5; }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
new file mode 100644
index 0000000000..febad45ca6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ function doneAccepted() {
+ dialog.done(FileDialog.Accepted)
+ }
+
+ function doneRejected() {
+ dialog.done(FileDialog.Rejected)
+ }
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/funcWithParams.qml b/tests/auto/qml/qmlcppcodegen/data/funcWithParams.qml
new file mode 100644
index 0000000000..b0c9780463
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/funcWithParams.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function foo(a: int) : int { return a + 10 }
+ property int bar: foo(10 + 10)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/globals.qml b/tests/auto/qml/qmlcppcodegen/data/globals.qml
new file mode 100644
index 0000000000..a17894cd17
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/globals.qml
@@ -0,0 +1,24 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ property QtObject application: Qt.application
+
+ property Timer t: Timer {
+ interval: 1
+ running: true
+ onTriggered: Qt.exit(0)
+ }
+
+ property Connections c: Connections {
+ target: root.application
+ function onAboutToQuit() { console.log("End"); }
+ }
+
+ Component.onCompleted: console.log("Start", 2, Qt.application.arguments[1]);
+
+ property url somewhere: Qt.resolvedUrl("/somewhere/else.qml")
+ property url plain: "/not/here.qml"
+ property string somewhereString: somewhere
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
new file mode 100644
index 0000000000..2090926872
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
@@ -0,0 +1,15 @@
+import QtQuick
+
+Item {
+ id: root
+ y: z
+ onXChanged: {
+ root.y = 48
+ ttt.font.pointSize = 22
+ }
+ z: 12
+
+ Text {
+ id: ttt
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/immediateQuit.qml b/tests/auto/qml/qmlcppcodegen/data/immediateQuit.qml
new file mode 100644
index 0000000000..1da9d1201a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/immediateQuit.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+
+QtObject {
+ Component.onCompleted: {
+ console.log("End: " + Qt.application.arguments[1]);
+ Qt.quit()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/imports/Module/NotCompiled.qml b/tests/auto/qml/qmlcppcodegen/data/imports/Module/NotCompiled.qml
new file mode 100644
index 0000000000..6f672b4af9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/imports/Module/NotCompiled.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ objectName: "not compiled"
+ property int foo: 12
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/imports/Module/qmldir b/tests/auto/qml/qmlcppcodegen/data/imports/Module/qmldir
new file mode 100644
index 0000000000..a1e0ebc5cd
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/imports/Module/qmldir
@@ -0,0 +1,2 @@
+module Module
+NotCompiled 1.0 NotCompiled.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/Globals.qml b/tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/Globals.qml
new file mode 100644
index 0000000000..6531a1e7ec
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/Globals.qml
@@ -0,0 +1,10 @@
+pragma Singleton
+import QtQuick 2.0
+
+QtObject {
+ readonly property string stringProp: "stringValue"
+ readonly property int intProp: 10
+ readonly property real realProp: 4.5
+ readonly property color colorProp: "green"
+ readonly property bool boolProp: true
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/qmldir b/tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/qmldir
new file mode 100644
index 0000000000..66bddd1ca1
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/imports/QmlBench/qmldir
@@ -0,0 +1,2 @@
+module QmlBench
+singleton Globals 1.0 Globals.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/importsFromImportPath.qml b/tests/auto/qml/qmlcppcodegen/data/importsFromImportPath.qml
new file mode 100644
index 0000000000..18a6f281f5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/importsFromImportPath.qml
@@ -0,0 +1,5 @@
+import Module
+
+NotCompiled {
+ property int bar: foo + 16
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/intEnumCompare.qml b/tests/auto/qml/qmlcppcodegen/data/intEnumCompare.qml
new file mode 100644
index 0000000000..e494d0d3e3
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/intEnumCompare.qml
@@ -0,0 +1,9 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property bool a: Component.Asynchronous == 1
+ property bool b: Component.Asynchronous != 1
+ property bool c: Qt.BottomEdge > 4
+ property bool d: Qt.BottomEdge < 2
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/intOverflow.qml b/tests/auto/qml/qmlcppcodegen/data/intOverflow.qml
new file mode 100644
index 0000000000..cbb5e2d4a7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/intOverflow.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property real a: 1024 * 1024 * 1024 * 1024 + 5
+ property int b: 1024 * 1024 * 1024 * 1024 + 5
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/interactive.qml b/tests/auto/qml/qmlcppcodegen/data/interactive.qml
new file mode 100644
index 0000000000..5367ca103f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/interactive.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+pragma Strict
+import QtQuick 2.9
+import QtQuick.Shapes 1.0
+
+Rectangle {
+ id: root
+
+ Row {
+ Slider {
+ id: widthSlider
+ }
+
+ property ShapePath linePath: ShapePath {
+ id: lineShapePath
+ strokeWidth: widthSlider.value
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/interceptor.qml b/tests/auto/qml/qmlcppcodegen/data/interceptor.qml
new file mode 100644
index 0000000000..5ee973933f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/interceptor.qml
@@ -0,0 +1,53 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick
+import TestTypes
+
+Item {
+ Behavior on width { PropertyAnimation {} }
+
+ CppBaseClass {
+ id: withBehavior
+ Behavior on cppProp { PropertyAnimation {} }
+ Component.onCompleted: cppProp = 200
+ }
+ height: withBehavior.cppProp
+
+ CppBaseClass {
+ id: withoutBehavior
+ Component.onCompleted: cppProp = 100
+ }
+ x: withoutBehavior.cppProp
+
+ Component.onCompleted: {
+ width = 200
+ y = 100
+ }
+
+ CppBaseClass {
+ id: qPropertyBinder
+ cppProp: withoutBehavior.cppProp + withBehavior.cppProp
+ }
+
+ property int qProperty1: qPropertyBinder.cppProp
+ property int qProperty2: withoutBehavior.cppProp + withBehavior.cppProp
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsMathObject.qml b/tests/auto/qml/qmlcppcodegen/data/jsMathObject.qml
new file mode 100644
index 0000000000..795e38b652
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsMathObject.qml
@@ -0,0 +1,64 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+pragma Strict
+import QtQml
+
+QtObject {
+ property double a: 0
+ property double b: 0
+
+ property double abs: Math.abs(a)
+ property double acos: Math.acos(a)
+ property double acosh: Math.acosh(a)
+ property double asin: Math.asin(a)
+ property double asinh: Math.asinh(a)
+ property double atan: Math.atan(a)
+ property double atanh: Math.atanh(a)
+// property double atan2: Math.atan2(a)
+ property double cbrt: Math.cbrt(a)
+ property double ceil: Math.ceil(a)
+ property double clz32: Math.clz32(a)
+ property double cos: Math.cos(a)
+ property double cosh: Math.cosh(a)
+ property double exp: Math.exp(a)
+// property double expm1: Math.expm1(a)
+ property double floor: Math.floor(a)
+ property double fround: Math.fround(a)
+// property double hypot: Math.hypot(a)
+ property double imul: Math.imul(a, b)
+ property double log: Math.log(a)
+ property double log10: Math.log10(a)
+ property double log1p: Math.log1p(a)
+ property double log2: Math.log2(a)
+ property double max: Math.max(a, b)
+ property double min: Math.min(a, b)
+// property double pow: Math.pow(a, b)
+ property double random: Math.random()
+ property double round: Math.round(a)
+ property double sign: Math.sign(a)
+ property double sin: Math.sin(a)
+ property double sinh: Math.sinh(a)
+ property double sqrt: Math.sqrt(a)
+ property double tan: Math.tan(a)
+ property double tanh: Math.tanh(a)
+ property double trunc: Math.trunc(a)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsimport.qml b/tests/auto/qml/qmlcppcodegen/data/jsimport.qml
new file mode 100644
index 0000000000..9c40878e60
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsimport.qml
@@ -0,0 +1,6 @@
+import QtQml 2.0
+import "script.js" as Script
+
+QtObject {
+ property int value: Script.getter()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsmoduleimport.qml b/tests/auto/qml/qmlcppcodegen/data/jsmoduleimport.qml
new file mode 100644
index 0000000000..d9a4fc29fb
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsmoduleimport.qml
@@ -0,0 +1,7 @@
+import QtQml 2.0
+import "script.mjs" as Script
+
+QtObject {
+ property bool ok: Script.ok()
+ property var okFunc: Script.ok
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/layouts.qml b/tests/auto/qml/qmlcppcodegen/data/layouts.qml
new file mode 100644
index 0000000000..d3fe4efbd7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/layouts.qml
@@ -0,0 +1,131 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick
+import QtQuick.Layouts
+
+Rectangle {
+ id: appWindow
+
+ visible: false
+ objectName: "Basic layouts"
+ property int margin: 11
+
+ Component.onCompleted: {
+ width = mainLayout.implicitWidth + 2 * margin
+ height = mainLayout.implicitHeight + 2 * margin
+ }
+
+ width: mainLayout.Layout.minimumWidth + 2 * margin
+ height: mainLayout.Layout.minimumHeight + 2 * margin
+
+ ColumnLayout {
+ id: mainLayout
+ anchors.fill: parent
+ anchors.margins: appWindow.margin
+ Rectangle {
+ id: rowBox
+ objectName: "Row layout"
+ Layout.fillWidth: true
+ Layout.minimumWidth: rowLayout.Layout.minimumWidth + 30
+
+ RowLayout {
+ id: rowLayout
+ anchors.fill: parent
+ Item {
+ objectName: "This wants to grow horizontally"
+ Layout.fillWidth: true
+ }
+ Item {
+ property string text: "Button"
+ }
+ }
+ }
+
+ Rectangle {
+ id: gridBox
+ objectName: "Grid layout"
+ Layout.fillWidth: true
+ Layout.minimumWidth: gridLayout.Layout.minimumWidth + 30
+
+ GridLayout {
+ id: gridLayout
+ rows: 3
+ flow: GridLayout.TopToBottom
+ anchors.fill: parent
+
+ Item { property string text: "Line 1" }
+ Item { property string text: "Line 2" }
+ Item { property string text: "Line 3" }
+
+ Item { }
+ Item { }
+ Item { }
+
+ Item {
+ property string text: "This widget spans over three rows in the GridLayout.\n"
+ + "All items in the GridLayout are implicitly positioned from top to bottom."
+ property int wrapMode: Text.WordWrap
+ Layout.rowSpan: 3
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.minimumHeight: implicitHeight
+ Layout.minimumWidth: 100 // guesstimate, should be size of largest word
+ }
+ }
+ }
+ Item {
+ id: t3
+ property string text: "This fills the whole cell"
+ Layout.minimumHeight: 30
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ Rectangle {
+ id: stackBox
+ objectName: "Stack layout"
+ implicitWidth: 200
+ implicitHeight: 60
+ Layout.minimumHeight: 60
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ StackLayout {
+ id: stackLayout
+ anchors.fill: parent
+
+ function advance() { currentIndex = (currentIndex + 1) % count }
+
+ Repeater {
+ id: stackRepeater
+ model: 5
+ Rectangle {
+ required property int index
+ color: Qt.hsla((0.5 + index) / stackRepeater.count, 0.3, 0.7, 1)
+ MouseArea {
+ anchors.centerIn: parent
+ onClicked: stackLayout.advance()
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/library.js b/tests/auto/qml/qmlcppcodegen/data/library.js
new file mode 100644
index 0000000000..51fb41dc23
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/library.js
@@ -0,0 +1,4 @@
+
+function getter() {
+ return 42;
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listIndices.qml b/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
new file mode 100644
index 0000000000..b5fda4ef0d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
@@ -0,0 +1,14 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property list<QtObject> items
+ property int numItems: items.length
+
+ Component.onCompleted: {
+ items.length = 3
+ for (var i = 0; i < 3; ++i)
+ items[i] = self
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listlength.qml b/tests/auto/qml/qmlcppcodegen/data/listlength.qml
new file mode 100644
index 0000000000..2a5832aa03
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listlength.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ property int l: children.length
+
+ Item {}
+ Item {}
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/math.qml b/tests/auto/qml/qmlcppcodegen/data/math.qml
new file mode 100644
index 0000000000..cc6cd3741a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/math.qml
@@ -0,0 +1,6 @@
+import QML
+
+QtObject {
+ property int a: Math.max(5, 7, 9, -111)
+ property var b: 50 / 22
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/methods.qml b/tests/auto/qml/qmlcppcodegen/data/methods.qml
new file mode 100644
index 0000000000..3abd14c9c1
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/methods.qml
@@ -0,0 +1,56 @@
+import QtQuick
+import TestTypes
+
+BirthdayParty {
+ id: party
+ host: Person {
+ name: "Bob Jones"
+ shoeSize: 12
+ }
+ guests: [
+ Person { name: "Leo Hodges" },
+ Person { name: "Jack Smith" },
+ Person { name: "Anne Brown" }
+ ]
+
+ property var dresses: [0, 0, 0, 0]
+
+ property var foo: party
+ property var bar: console
+
+ property var numeric
+ property var stringly
+
+ Component.onCompleted: {
+ invite("William Green")
+ foo.invite("The Foo")
+ bar.log("The Bar")
+ numeric = 1;
+ stringly = "name";
+ }
+
+ function storeElement() {
+ ++host.shoeSize
+ party.dresses[2] = [1, 2, 3]
+ }
+
+ function stuff(sn) {
+ if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase())
+ return sn
+ return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1)
+ }
+
+ function retrieveVar() {
+ return guests[numeric][stringly];
+ }
+
+ function retrieveString() : string {
+ return guests[numeric][stringly];
+ }
+
+ property var n1: stuff("onGurk")
+ property var n2: stuff("semmeln")
+ property var n3: stuff(12)
+
+ property int enumValue: Item.TopRight
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/modulePrefix.qml b/tests/auto/qml/qmlcppcodegen/data/modulePrefix.qml
new file mode 100644
index 0000000000..eb12882dd7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/modulePrefix.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+import TestTypes as T
+
+QtObject {
+ id: self
+ property date foo: self.T.BirthdayParty.rsvp
+ property date bar: T.BirthdayParty.rsvp
+ property string baz: T.CppSingleton.objectName
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/noQQmlData.qml b/tests/auto/qml/qmlcppcodegen/data/noQQmlData.qml
new file mode 100644
index 0000000000..47e1d34183
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/noQQmlData.qml
@@ -0,0 +1,10 @@
+import QtQml
+import TestTypes
+
+BirthdayParty {
+ id: party
+ property string inDaHouse: " in da house!"
+ property string n: host.name + inDaHouse
+ function burn() { host = mrBurns.createObject(); }
+ property Component mrBurns: Person { name: "Mr Burns" }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nonNotifyable.qml b/tests/auto/qml/qmlcppcodegen/data/nonNotifyable.qml
new file mode 100644
index 0000000000..13102ea638
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/nonNotifyable.qml
@@ -0,0 +1,14 @@
+import TestTypes
+import QtQml
+
+QtObject {
+ id: parent
+ property date dayz: BirthdayParty.rsvp
+ property QtObject o: QtObject {}
+
+ property date oParty: o.BirthdayParty.rsvp
+ Component.onCompleted: {
+ BirthdayParty.rsvp = new Date(2121, 0, 12);
+ o.BirthdayParty.rsvp = new Date(2111, 11, 11);
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/noscope.qml b/tests/auto/qml/qmlcppcodegen/data/noscope.qml
new file mode 100644
index 0000000000..298c00175c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/noscope.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Canvas {
+ onPaint: {
+ // Does not actually compile to C++ because getContext() is not properly specified
+ // However, it shouldn't crash
+ var ctx = getContext("2d");
+ ctx.clearRect(0, 0, width, height);
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullAccess.qml b/tests/auto/qml/qmlcppcodegen/data/nullAccess.qml
new file mode 100644
index 0000000000..ba2b59feb1
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/nullAccess.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ width: ListView.view.width+40
+ height: ListView.view.height
+ Component.onCompleted: ListView.view.height = 10
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
new file mode 100644
index 0000000000..6af7965628
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
@@ -0,0 +1,77 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#ifndef OBJECTWITHMETOD_H
+#define OBJECTWITHMETOD_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqml.h>
+
+// Make objectName available. It doesn't exist on the builtin QtObject type
+struct QObjectForeignForObjectName {
+ Q_GADGET
+ QML_FOREIGN(QObject)
+ QML_ANONYMOUS
+};
+
+class ObjectWithMethod : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int fff MEMBER theThing BINDABLE theThingBindable FINAL)
+
+public:
+ ObjectWithMethod(QObject *parent = nullptr) : QObject(parent) { theThing = 5; }
+
+ Q_INVOKABLE int doThing() const { return theThing; }
+ QProperty<int> theThing;
+ QBindable<int> theThingBindable() { return QBindable<int>(&theThing); }
+};
+
+class OverriddenObjectName : public ObjectWithMethod
+{
+ Q_OBJECT
+ Q_PROPERTY(QString objectName READ objectName WRITE setObjectName BINDABLE objectNameBindable)
+
+ // This shouldn't work
+ Q_PROPERTY(int fff READ fff BINDABLE nothingBindable)
+
+public:
+ OverriddenObjectName(QObject *parent = nullptr) : ObjectWithMethod(parent)
+ {
+ m_objectName = u"borschtsch"_qs;
+ nothing = 77;
+ }
+
+ QString objectName() const { return m_objectName.value(); }
+ void setObjectName(const QString &objectName) { m_objectName.setValue(objectName); }
+ QBindable<QString> objectNameBindable() { return QBindable<QString>(&m_objectName); }
+ Q_INVOKABLE QString doThing() const { return u"7"_qs; }
+
+ int fff() const { return nothing.value(); }
+ QBindable<int> nothingBindable() { return QBindable<int>(&nothing); }
+private:
+ QProperty<int> nothing;
+ QProperty<QString> m_objectName;
+};
+
+#endif // OBJECTWITHMETHOD_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/outOfBounds.qml b/tests/auto/qml/qmlcppcodegen/data/outOfBounds.qml
new file mode 100644
index 0000000000..14933a0792
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/outOfBounds.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property var oob: children[12]
+ Component.onCompleted: console.log("oob", children[11])
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/overriddenMember.qml b/tests/auto/qml/qmlcppcodegen/data/overriddenMember.qml
new file mode 100644
index 0000000000..296c852e1e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/overriddenMember.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+BaseMember {
+ property int ppp: 16
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ownProperty.qml b/tests/auto/qml/qmlcppcodegen/data/ownProperty.qml
new file mode 100644
index 0000000000..70e850f4eb
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ownProperty.qml
@@ -0,0 +1,12 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject bar: QtObject {
+ id: inner
+ property string a: objectName
+ objectName: "foo"
+ }
+
+ objectName: inner.a
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/page.qml b/tests/auto/qml/qmlcppcodegen/data/page.qml
new file mode 100644
index 0000000000..ee79fba4c0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/page.qml
@@ -0,0 +1,41 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+
+T.Page {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ background: Rectangle {
+ color: control.palette.window
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/parentProp.qml b/tests/auto/qml/qmlcppcodegen/data/parentProp.qml
new file mode 100644
index 0000000000..fdfd96ba73
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/parentProp.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Item {
+ implicitWidth: 12
+ property int a: 5
+ property int c: child.b + 2
+ property int i: child.i * 2
+
+ Item {
+ id: child
+ property int b: parent.a + 4
+ property int i: parent.implicitWidth - 1
+ }
+
+ Item {
+ id: sibling
+ implicitWidth: 29
+ }
+
+ Item {
+ id: evil
+ property string a: "599"
+ property string implicitWidth: "444"
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/person.cpp b/tests/auto/qml/qmlcppcodegen/data/person.cpp
new file mode 100644
index 0000000000..7989d3715b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/person.cpp
@@ -0,0 +1,59 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#include "person.h"
+
+Person::Person(QObject *parent)
+ : QObject(parent), m_name(u"Bart"_qs), m_shoeSize(0)
+{
+}
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &n)
+{
+ if (n != m_name) {
+ m_name = n;
+ emit nameChanged();
+ }
+}
+
+void Person::resetName()
+{
+ setName(u"Bart"_qs);
+}
+
+int Person::shoeSize() const
+{
+ return m_shoeSize;
+}
+
+void Person::setShoeSize(int s)
+{
+ if (s != m_shoeSize) {
+ m_shoeSize = s;
+ emit shoeSizeChanged();
+ }
+}
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/person.h b/tests/auto/qml/qmlcppcodegen/data/person.h
new file mode 100644
index 0000000000..5d208396c8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/person.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QObject>
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlengine.h>
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged RESET resetName FINAL)
+ Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
+ Q_PROPERTY(int pain READ pain CONSTANT FINAL)
+ QML_ELEMENT
+public:
+ Person(QObject *parent = nullptr);
+ ~Person() { setShoeSize(-25); }
+
+ int pain() const {
+ qmlEngine(this)->throwError(QStringLiteral("ouch"));
+ return 92;
+ }
+
+ QString name() const;
+ void setName(const QString &);
+ void resetName();
+
+ int shoeSize() const;
+ void setShoeSize(int);
+
+signals:
+ void nameChanged();
+ void shoeSizeChanged();
+
+private:
+ QString m_name;
+ int m_shoeSize;
+};
+
+#endif // PERSON_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/pressAndHoldButton.qml b/tests/auto/qml/qmlcppcodegen/data/pressAndHoldButton.qml
new file mode 100644
index 0000000000..9750bb3a1d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/pressAndHoldButton.qml
@@ -0,0 +1,38 @@
+import QtQuick
+
+Image {
+ id: container
+
+ property int repeatDelay: 300
+ property int repeatDuration: 75
+ property bool pressed: false
+
+ signal clicked
+
+ scale: pressed ? 0.9 : 1
+
+ function press() {
+ autoRepeatClicks.start();
+ }
+
+ function release() {
+ autoRepeatClicks.stop()
+ container.pressed = false
+ }
+
+ ParallelAnimation on pressed {
+ id: autoRepeatClicks
+ running: false
+
+ PropertyAction { target: container; property: "pressed"; value: true }
+ ScriptAction { script: container.clicked() }
+ PauseAnimation { duration: container.repeatDelay }
+
+ ParallelAnimation {
+ loops: Animation.Infinite
+ ScriptAction { script: container.clicked() }
+ PauseAnimation { duration: container.repeatDuration }
+ }
+ }
+}
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/propertyChanges.qml b/tests/auto/qml/qmlcppcodegen/data/propertyChanges.qml
new file mode 100644
index 0000000000..d0cc08f003
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/propertyChanges.qml
@@ -0,0 +1,9 @@
+pragma Strict
+import QtQuick
+
+Item {
+ id: outer
+ PropertyChanges {
+ outer.x: 15 + 12
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/registerelimination.qml b/tests/auto/qml/qmlcppcodegen/data/registerelimination.qml
new file mode 100644
index 0000000000..2ea98fe4ff
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/registerelimination.qml
@@ -0,0 +1,44 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+import QtQuick
+
+Rectangle {
+ id: page
+ height: 480;
+
+ property int input: 10
+ property int output: ball.y
+
+ onInputChanged: ball.y = input
+
+ Rectangle {
+ id: ball
+
+ onYChanged: {
+ if (y <= 0) {
+ y = page.height - 21;
+ } else if (y >= page.height - 20) {
+ y = 0;
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/scopeVsObject.qml b/tests/auto/qml/qmlcppcodegen/data/scopeVsObject.qml
new file mode 100644
index 0000000000..631f8e2991
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/scopeVsObject.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ objectName: "foo"
+ Component.onCompleted: objectName = objectName + "bar"
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/script.js b/tests/auto/qml/qmlcppcodegen/data/script.js
new file mode 100644
index 0000000000..fa55f9069e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/script.js
@@ -0,0 +1,6 @@
+
+.import "library.js" as Library
+
+function getter() {
+ return Library.getter()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/script.mjs b/tests/auto/qml/qmlcppcodegen/data/script.mjs
new file mode 100644
index 0000000000..459c336125
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/script.mjs
@@ -0,0 +1,4 @@
+
+export function ok() {
+ return true
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
new file mode 100644
index 0000000000..336edade29
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ id: slider
+ height: 26
+ width: 320
+
+ property real min: 0
+ property real max: 1
+ property real value: min + (max - min) * dragHandler.value
+ property real init: min+(max-min)/2
+ property string name: "Slider"
+ property color color: "#0066cc"
+ property real minLabelWidth: 44
+
+ DragHandler {
+ id: dragHandler
+ target: handle
+ xAxis.minimum: Math.round(-handle.width / 2 + 3)
+ xAxis.maximum: Math.round(foo.width - handle.width/2 - 3)
+ property real value: (handle.x - xAxis.minimum) / (xAxis.maximum - xAxis.minimum)
+ }
+
+ Component.onCompleted: ()=> setValue(init)
+ function setValue(v) {
+ if (min < max)
+ handle.x = Math.round( v / (max - min) *
+ (dragHandler.xAxis.maximum - dragHandler.xAxis.minimum)
+ + dragHandler.xAxis.minimum);
+ }
+ Rectangle {
+ id:sliderName
+ anchors.left: parent.left
+ anchors.leftMargin: 16
+ height: childrenRect.height
+ width: Math.max(slider.minLabelWidth, childrenRect.width)
+ anchors.verticalCenter: parent.verticalCenter
+ Item {
+ objectName: slider.name + ":"
+ }
+ }
+
+ Rectangle{
+ id: foo
+ width: parent.width - 8 - sliderName.width
+ color: "#eee"
+ height: 7
+ radius: 3
+ antialiasing: true
+ border.color: Qt.darker(color, 1.2)
+ anchors.left: sliderName.right
+ anchors.right: parent.right
+ anchors.leftMargin: 10
+ anchors.rightMargin: 24
+ anchors.verticalCenter: parent.verticalCenter
+
+ Rectangle {
+ height: parent.height
+ anchors.left: parent.left
+ anchors.right: handle.horizontalCenter
+ color: slider.color
+ radius: 3
+ border.width: 1
+ border.color: Qt.darker(color, 1.3)
+ opacity: 0.8
+ }
+ Image {
+ id: handle
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shifts.qml b/tests/auto/qml/qmlcppcodegen/data/shifts.qml
new file mode 100644
index 0000000000..009e010b2d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shifts.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ property int a: (155 >> 2) << (2 << 2);
+ property int b: (a + 1) >> 1
+ property int c: (b - 2) << 2
+ property int d: (a + 3) << (b * 3)
+ property int e: (d - 4) >> (c / 2)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/signal.qml b/tests/auto/qml/qmlcppcodegen/data/signal.qml
new file mode 100644
index 0000000000..a40020dff5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/signal.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ property int ff: 4
+ onObjectNameChanged: ff = 12
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/signalHandler.qml b/tests/auto/qml/qmlcppcodegen/data/signalHandler.qml
new file mode 100644
index 0000000000..621c218d02
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/signalHandler.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ signal foo
+ signal bar(baz: string)
+
+ onFoo: ()=> console.log("foo")
+ onBar: (baz)=> console.log(baz)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/specificParent.qml b/tests/auto/qml/qmlcppcodegen/data/specificParent.qml
new file mode 100644
index 0000000000..6d5e6cdc16
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/specificParent.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Rectangle {
+ Item {
+ id: child
+ property real a: parent.radius
+ }
+
+ property real a: child.a
+ radius: 77
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/stringLength.qml b/tests/auto/qml/qmlcppcodegen/data/stringLength.qml
new file mode 100644
index 0000000000..df9bbb7f2d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/stringLength.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ objectName: "astringb"
+ property int stringLength: objectName.length
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/testlogger.js b/tests/auto/qml/qmlcppcodegen/data/testlogger.js
new file mode 100644
index 0000000000..77dded4e7b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/testlogger.js
@@ -0,0 +1,76 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+.pragma library
+
+var testResults = null;
+
+function log_init_results()
+{
+ if (!testResults) {
+ testResults = {
+ reportedStart: false,
+ nextId: 0,
+ testCases: []
+ }
+ }
+}
+
+function log_register_test(name)
+{
+ log_init_results()
+ var testId = testResults.nextId++
+ testResults.testCases.push(testId)
+ return testId
+}
+
+function log_optional_test(testId)
+{
+ log_init_results()
+ var index = testResults.testCases.indexOf(testId)
+ if (index >= 0)
+ testResults.testCases.splice(index, 1)
+}
+
+function log_mandatory_test(testId)
+{
+ log_init_results()
+ var index = testResults.testCases.indexOf(testId)
+ if (index == -1)
+ testResults.testCases.push(testId)
+}
+
+function log_start_test()
+{
+ log_init_results()
+ if (testResults.reportedStart)
+ return false
+ testResults.reportedStart = true
+ return true
+}
+
+function log_complete_test(testId)
+{
+ var index = testResults.testCases.indexOf(testId)
+ if (index >= 0)
+ testResults.testCases.splice(index, 1)
+ return testResults.testCases.length > 0
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/text.qml b/tests/auto/qml/qmlcppcodegen/data/text.qml
new file mode 100644
index 0000000000..5a30c7e3f4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/text.qml
@@ -0,0 +1,20 @@
+import TestTypes
+import QtQml
+
+QtObject {
+ id: parent
+ property int a: 16
+ property date dayz: BirthdayParty.rsvp
+ property QtObject o: QtObject {
+ property date width: parent.dayz
+ }
+
+ property date oParty: o.BirthdayParty.rsvp
+
+ property BirthdayParty party: BirthdayParty {
+ eee: parent.a + 5
+ property int fff: eee + 12
+ }
+
+ property int ggg: party.eee + a
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml b/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml
new file mode 100644
index 0000000000..fae040a1a5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+Person {
+ name: shoeSize === 11 ? undefined : "Marge"
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/unknownParameter.qml b/tests/auto/qml/qmlcppcodegen/data/unknownParameter.qml
new file mode 100644
index 0000000000..3b750c2a66
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/unknownParameter.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+CppBaseClass {
+ Component.onCompleted: doCall(null);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/unusedAttached.qml b/tests/auto/qml/qmlcppcodegen/data/unusedAttached.qml
new file mode 100644
index 0000000000..5bae1ef8c8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/unusedAttached.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ KeyNavigation.priority: KeyNavigation.BeforeItem
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/urlString.qml b/tests/auto/qml/qmlcppcodegen/data/urlString.qml
new file mode 100644
index 0000000000..511c54532c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/urlString.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property url c: "http://aabbcc.com"
+ property url d: "http://ccbbaa.com"
+ property url e: "http:" + "//a112233.de"
+ Component.onCompleted: {
+ c = "http://dddddd.com";
+ self.d = "http://aaaaaa.com";
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeProperty.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeProperty.qml
new file mode 100644
index 0000000000..1d1b74d9bc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeProperty.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQuick
+
+Text {
+ property string foo: font.family
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/voidfunction.qml b/tests/auto/qml/qmlcppcodegen/data/voidfunction.qml
new file mode 100644
index 0000000000..74313f900f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/voidfunction.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function doesNotReturnValue() { objectName = "barbar" }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
new file mode 100644
index 0000000000..11c70f2c72
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -0,0 +1,1623 @@
+/******************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt JavaScript to C++ compiler.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#include <data/birthdayparty.h>
+#include <data/cppbaseclass.h>
+#include <data/objectwithmethod.h>
+
+#include <QtTest>
+#include <QtQml>
+#include <QtGui/qcolor.h>
+
+#if QT_CONFIG(process)
+#include <QtCore/qprocess.h>
+#endif
+
+Q_IMPORT_QML_PLUGIN(TestTypesPlugin)
+
+class tst_QmlCppCodegen : public QObject
+{
+ Q_OBJECT
+private slots:
+ void simpleBinding();
+ 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 runInterpreted();
+ void methods();
+ void math();
+ void unknownParameter();
+ 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 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 bindingExpression();
+ void voidFunction();
+ void overriddenProperty();
+ void listLength();
+ void parentProperty();
+ void registerElimination();
+ void asCast();
+ void noQQmlData();
+ void scopeObjectDestruction();
+ void colorAsVariant();
+ void bindToValueType();
+ void undefinedResets();
+ void innerObjectNonShadowable();
+ void ownPropertiesNonShadowable();
+ void modulePrefix();
+ void colorString();
+ void urlString();
+ void callContextPropertyLookupResult();
+ void deadShoeSize();
+ void listIndices();
+ void jsMathObject();
+ void intEnumCompare();
+};
+
+void tst_QmlCppCodegen::simpleBinding()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/Test.qml"_qs));
+ 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);
+ }
+}
+
+void tst_QmlCppCodegen::anchorsFill()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/anchorsFill.qml"_qs));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
+
+ QCOMPARE(object->property("width").toInt(), 234);
+ QCOMPARE(object->children().length(), 2);
+
+ QObject *child = object->children().front();
+ QVERIFY(child);
+ QCOMPARE(child->property("width").toInt(), 234);
+
+ QObject *newParent = object->children().back();
+ QVERIFY(newParent);
+ QCOMPARE(newParent->property("width").toInt(), 47);
+
+ child->setProperty("parent", QVariant::fromValue(newParent));
+ QCOMPARE(child->property("width").toInt(), 47);
+}
+
+void tst_QmlCppCodegen::signalHandler()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/signal.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->objectName(), QString());
+ QCOMPARE(object->property("ff").toInt(), 4);
+
+ object->setObjectName(u"foo"_qs);
+ QCOMPARE(object->property("ff").toInt(), 12);
+}
+
+void tst_QmlCppCodegen::idAccess()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/idAccess.qml"_qs));
+ 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);
+
+ // The binding was broken by setting the property
+ object->setProperty("z", 14);
+ QCOMPARE(object->property("y").toInt(), 48);
+
+ QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_qs);
+ QFont f = qvariant_cast<QFont>(ttt->property("font"));
+ QCOMPARE(f.pointSize(), 22);
+}
+
+static QByteArray arg1()
+{
+ const QStringList args = QCoreApplication::instance()->arguments();
+ return args.length() > 1 ? args[1].toUtf8() : QByteArray("undefined");
+}
+
+void tst_QmlCppCodegen::globals()
+{
+ QQmlEngine engine;
+ int exitCode = -1;
+ QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; });
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/globals.qml"_qs));
+ 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);
+
+ QObject *application = qvariant_cast<QObject *>(object->property("application"));
+ QVERIFY(application);
+ QCOMPARE(QString::fromUtf8(application->metaObject()->className()),
+ u"QQuickApplication"_qs);
+
+ 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"_qs);
+
+ const QVariant somewhereString = object->property("somewhereString");
+ QCOMPARE(somewhereString.userType(), QMetaType::QString);
+ QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_qs);
+
+ const QVariant plain = object->property("plain");
+ QCOMPARE(plain.userType(), QMetaType::QUrl);
+ QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_qs);
+}
+
+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:/TestTypes/immediateQuit.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ const QByteArray message = QByteArray("End: ") + arg1();
+ QTest::ignoreMessage(QtDebugMsg, message.constData());
+
+ QSignalSpy quitSpy(&engine, &QQmlEngine::quit);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(quitSpy.count(), 1);
+}
+
+void tst_QmlCppCodegen::enums()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/Enums.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/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"_qs);
+
+ QTRY_COMPARE(object->property("appState").toInt(&ok), 1);
+ QVERIFY(ok);
+ QCOMPARE(object->property("color").toString(), u"green"_qs);
+
+ const auto func = qmlAttachedPropertiesFunction(
+ object.data(), QMetaType::fromName("QQuickLayout*").metaObject());
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/enumsInOtherObject.qml:4:25: "
+ "QML Enums: Layout must be attached to Item elements");
+ QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
+
+ const QVariant prop = attached->property("alignment");
+ QVERIFY(prop.isValid());
+ QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter);
+ }
+ {
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/enumsInOtherObject.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("color").toString(), u"blue"_qs);
+ QTRY_COMPARE(object->property("color").toString(), u"green"_qs);
+ }
+}
+
+void tst_QmlCppCodegen::funcWithParams()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/funcWithParams.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("bar").toInt(), 30);
+}
+
+void tst_QmlCppCodegen::intOverflow()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/intOverflow.qml"_qs));
+ 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);
+}
+
+void tst_QmlCppCodegen::stringLength()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/stringLength.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("stringLength").toInt(), 8);
+}
+
+void tst_QmlCppCodegen::scopeVsObject()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/scopeVsObject.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("objectName").toString(), u"foobar"_qs);
+}
+
+void tst_QmlCppCodegen::compositeTypeMethod()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/compositeTypeMethod.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QSignalSpy spy(object.data(), SIGNAL(foo()));
+ QTRY_VERIFY(spy.count() > 0);
+}
+
+void tst_QmlCppCodegen::excessiveParameters()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/excessiveParameters.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QSignalSpy spy(object.data(), SIGNAL(foo()));
+ QTRY_VERIFY(spy.count() > 0);
+}
+
+void tst_QmlCppCodegen::jsImport()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/jsimport.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("value").toInt(), 42);
+}
+
+void tst_QmlCppCodegen::jsmoduleImport()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/jsmoduleimport.qml"_qs));
+ 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::methods()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/methods.qml"_qs));
+ QVERIFY(component.isReady());
+
+ QTest::ignoreMessage(QtDebugMsg, "The Bar");
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_qs));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj);
+ BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data()));
+
+ 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"_qs)
+ foundGreen = true;
+ if (party->guest(ii)->name() == u"The Foo"_qs)
+ foundFoo = true;
+ }
+
+ QVERIFY(foundGreen);
+ QVERIFY(foundFoo);
+
+ QCOMPARE(obj->property("n1").toString(), u"onGurk"_qs);
+ QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_qs);
+ 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"_qs);
+ }
+
+ {
+ QString ret;
+ obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret));
+ QCOMPARE(ret, u"Jack Smith"_qs);
+ }
+
+ 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::math()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/math.qml"_qs));
+ 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);
+}
+
+void tst_QmlCppCodegen::unknownParameter()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/unknownParameter.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("cppProp").toInt(), 18);
+}
+
+void tst_QmlCppCodegen::array()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/array.qml"_qs));
+ 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"_qs).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"_qs).toInt(), 0);
+}
+
+void tst_QmlCppCodegen::equalsUndefined()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/equalsUndefined.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("a").toInt(), 50);
+ QCOMPARE(object->property("b").toInt(), 5000);
+}
+
+void tst_QmlCppCodegen::conversions()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/conversions.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/conversions.qml:42: TypeError: Type error");
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QVERIFY(object->property("nullIsNull").toBool());
+ QVERIFY(!object->property("intIsNull").toBool());
+ QVERIFY(!object->property("zeroIsNull").toBool());
+ QVERIFY(!object->property("arrayIsNull").toBool());
+ QVERIFY(!object->property("objectIsNull").toBool());
+ QVERIFY(!object->property("nullIsNotNull").toBool());
+ QVERIFY(object->property("intIsNotNull").toBool());
+ QVERIFY(object->property("zeroIsNotNull").toBool());
+ QVERIFY(object->property("arrayIsNotNull").toBool());
+ QVERIFY(object->property("objectIsNotNull").toBool());
+ QVERIFY(!object->property("neNull").toBool());
+ QVERIFY(object->property("eqNull").toBool());
+
+ QVERIFY(!object->property("boolEqualsBool").toBool());
+ QVERIFY(object->property("boolNotEqualsBool").toBool());
+
+ QCOMPARE(object->property("cmpEqInt").toInt(), 17);
+
+ QCOMPARE(object->property("undefinedType").toString(), u"undefined"_qs);
+ QCOMPARE(object->property("booleanType").toString(), u"boolean"_qs);
+ QCOMPARE(object->property("numberType").toString(), u"number"_qs);
+ QCOMPARE(object->property("stringType").toString(), u"string"_qs);
+ QCOMPARE(object->property("objectType").toString(), u"object"_qs);
+ QCOMPARE(object->property("symbolType").toString(), u"symbol"_qs);
+
+ QJSManagedValue obj = engine.toManagedValue(object->property("anObject"));
+ QCOMPARE(obj.property(u"a"_qs).toInt(), 12);
+ QCOMPARE(obj.property(u"b"_qs).toInt(), 14);
+ QCOMPARE(obj.property(u"c"_qs).toString(), u"somestring"_qs);
+
+ QVERIFY(object->property("aInObject").toBool());
+ QVERIFY(object->property("lengthInArray").toBool());
+ QVERIFY(!object->property("fooInNumber").isValid());
+ QVERIFY(!object->property("numberInObject").toBool());
+ QVERIFY(object->property("numberInArray").toBool());
+
+ QCOMPARE(object->property("varPlusVar").toDouble(), 2.0);
+ QVERIFY(qIsNaN(object->property("varMinusVar").toDouble()));
+ QVERIFY(qIsNaN(object->property("varTimesVar").toDouble()));
+ QCOMPARE(object->property("varDivVar").toDouble(), 0.0);
+
+ const QVariant stringPlusString = object->property("stringPlusString");
+ QCOMPARE(stringPlusString.typeId(), QMetaType::QString);
+ QCOMPARE(stringPlusString.toString(), u"1220"_qs);
+
+ const QVariant stringMinusString = object->property("stringMinusString");
+ QCOMPARE(stringMinusString.typeId(), QMetaType::Double);
+ QCOMPARE(stringMinusString.toDouble(), -8.0);
+
+ const QVariant stringTimesString = object->property("stringTimesString");
+ QCOMPARE(stringTimesString.typeId(), QMetaType::Double);
+ QCOMPARE(stringTimesString.toDouble(), 240.0);
+
+ const QVariant stringDivString = object->property("stringDivString");
+ QCOMPARE(stringDivString.typeId(), QMetaType::Double);
+ QCOMPARE(stringDivString.toDouble(), 0.6);
+
+ QCOMPARE(object->property("uglyString").toString(),
+ u"with\nnewlinewith\"quotwith\\slashes"_qs);
+
+ QCOMPARE(qvariant_cast<QObject *>(object->property("nullObject1")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(object->property("nullObject2")), object.data());
+
+ QCOMPARE(object->property("passObjectLiteral").toInt(), 11);
+ QCOMPARE(object->property("passArrayLiteral").toInt(), 4);
+
+ QCOMPARE(object->property("doneStuff").toInt(), 19);
+
+ QVariantList modulos = object->property("modulos").toList();
+ QCOMPARE(modulos.length(), 7);
+
+ QCOMPARE(modulos[0].userType(), QMetaType::Double);
+ QCOMPARE(modulos[0].toDouble(), 0.0);
+
+ QCOMPARE(modulos[1].userType(), QMetaType::Int);
+ QCOMPARE(modulos[1].toInt(), 0);
+
+ QCOMPARE(modulos[2].userType(), QMetaType::Double);
+ QCOMPARE(modulos[2].toDouble(), 2.4);
+
+ QCOMPARE(modulos[3].userType(), QMetaType::Double);
+ QCOMPARE(modulos[3].toDouble(), -0.4);
+
+ QCOMPARE(modulos[4].userType(), QMetaType::Double);
+ QCOMPARE(modulos[4].toDouble(), 0.0);
+
+ QCOMPARE(modulos[5].userType(), QMetaType::Double);
+ QVERIFY(qIsNaN(modulos[5].toDouble()));
+
+ QCOMPARE(modulos[6].userType(), QMetaType::Double);
+ QVERIFY(qIsNaN(modulos[6].toDouble()));
+
+ QVariantList unaryOps = object->property("unaryOps").toList();
+ QCOMPARE(unaryOps.length(), 6);
+
+ QCOMPARE(unaryOps[0].userType(), QMetaType::Double);
+ QCOMPARE(unaryOps[0].toDouble(), 1221);
+
+ QCOMPARE(unaryOps[1].userType(), QMetaType::Double);
+ QCOMPARE(unaryOps[1].toDouble(), 1219);
+
+ QCOMPARE(unaryOps[2].userType(), QMetaType::Double);
+ QCOMPARE(unaryOps[2].toDouble(), 1220);
+
+ QCOMPARE(unaryOps[3].userType(), QMetaType::Double);
+ QCOMPARE(unaryOps[3].toDouble(), -1220);
+
+ QCOMPARE(unaryOps[4].userType(), QMetaType::Double);
+ QCOMPARE(unaryOps[4].toDouble(), 1220);
+
+ QCOMPARE(unaryOps[5].userType(), QMetaType::Double);
+ QCOMPARE(unaryOps[5].toDouble(), 1220);
+
+ QVariant undef;
+ QVERIFY(object->metaObject()->invokeMethod(object.data(), "retUndefined",
+ Q_RETURN_ARG(QVariant, undef)));
+ QVERIFY(!undef.isValid());
+}
+
+void tst_QmlCppCodegen::interestingFiles_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addRow("conversions2") << u"conversions2.qml"_qs;
+ QTest::addRow("TestCase") << u"TestCase.qml"_qs;
+ QTest::addRow("layouts") << u"layouts.qml"_qs;
+ QTest::addRow("interactive") << u"interactive.qml"_qs;
+ QTest::addRow("Panel") << u"Panel.qml"_qs;
+ QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_qs;
+ QTest::addRow("Root") << u"ProgressBar/Root.qml"_qs;
+ QTest::addRow("noscope") << u"noscope.qml"_qs;
+ QTest::addRow("dynamicscene") << u"dynamicscene.qml"_qs;
+ QTest::addRow("curlygrouped") << u"curlygrouped.qml"_qs;
+}
+
+void tst_QmlCppCodegen::interestingFiles()
+{
+ QFETCH(QString, file);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/%1"_qs.arg(file)));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+}
+
+void tst_QmlCppCodegen::extendedTypes()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/extendedTypes.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.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)"_qs);
+
+ QCOMPARE(object->property("e").toInt(), 2);
+}
+
+void tst_QmlCppCodegen::construct()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/construct.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ 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"_qs);
+
+ QCOMPARE(object->property("aaa").toInt(), 12);
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/construct.qml:9: Error: ouch");
+ object->metaObject()->invokeMethod(object.data(), "ouch");
+ QCOMPARE(object->property("aaa").toInt(), 13);
+}
+
+void tst_QmlCppCodegen::contextParam()
+{
+ // The compiler cannot resolve context parameters.
+ // Make sure the binding is interpreted.
+
+ QQmlEngine engine;
+
+ QVariantMap m;
+ m.insert(u"foo"_qs, 10);
+ engine.rootContext()->setContextProperty(u"contextParam"_qs, m);
+
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/contextParam.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("foo").toInt(), 10);
+}
+
+void tst_QmlCppCodegen::attachedType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/text.qml"_qs));
+ 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);
+}
+
+void tst_QmlCppCodegen::componentReturnType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/componentReturnType.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QCOMPARE(object->property("count").toInt(), 10);
+ QCOMPARE(QQmlListReference(object.data(), "children", &engine).count(), 11);
+}
+
+void tst_QmlCppCodegen::onAssignment()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/pressAndHoldButton.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QCOMPARE(object->property("pressed").toBool(), false);
+ QCOMPARE(object->property("scale").toDouble(), 1.0);
+
+ object->metaObject()->invokeMethod(object.data(), "press");
+ QTRY_COMPARE(object->property("pressed").toBool(), true);
+ QCOMPARE(object->property("scale").toDouble(), 0.9);
+
+ object->metaObject()->invokeMethod(object.data(), "release");
+ QCOMPARE(object->property("pressed").toBool(), false);
+ QCOMPARE(object->property("scale").toDouble(), 1.0);
+}
+
+namespace QmlCacheGeneratedCode {
+namespace _0x5f_TestTypes_failures_qml {
+extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];
+}
+}
+
+void tst_QmlCppCodegen::failures()
+{
+ const auto &aotFailure
+ = QmlCacheGeneratedCode::_0x5f_TestTypes_failures_qml::aotBuiltFunctions[0];
+ QVERIFY(aotFailure.argumentTypes.isEmpty());
+ QVERIFY(!aotFailure.functionPtr);
+ QCOMPARE(aotFailure.index, 0);
+}
+
+void tst_QmlCppCodegen::enumScope()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/enumScope.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QCOMPARE(object->property("flow").toInt(), 1);
+}
+
+void tst_QmlCppCodegen::unusedAttached()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/unusedAttached.qml"_qs));
+ 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::attachedBaseEnum()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/attachedBaseEnum.qml"_qs));
+ 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::nullAccess()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/nullAccess.qml"_qs));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/TestTypes/nullAccess.qml:4:5: TypeError: "
+ "Cannot read property 'width' of null");
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/TestTypes/nullAccess.qml:5:5: TypeError: "
+ "Cannot read property 'height' of null");
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/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::interceptor()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/interceptor.qml"_qs));
+ 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);
+
+ 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::nonNotifyable()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/nonNotifyable.qml"_qs));
+ 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::importsFromImportPath()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/importsFromImportPath.qml"_qs));
+
+ // 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:/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_qs);
+}
+
+void tst_QmlCppCodegen::aliasLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/aliasLookup.qml"_qs));
+ 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"_qs);
+}
+
+void tst_QmlCppCodegen::outOfBoundsArray()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/outOfBounds.qml"_qs));
+ 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());
+}
+
+void tst_QmlCppCodegen::compositeSingleton()
+{
+ QQmlEngine engine;
+ engine.addImportPath(u":/TestTypes/imports/"_qs);
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/compositesingleton.qml"_qs));
+ 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);
+}
+
+void tst_QmlCppCodegen::lotsOfRegisters()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/page.qml"_qs));
+ 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.length()], (i * 17) % 512));
+ compare();
+ }
+}
+
+void tst_QmlCppCodegen::inPlaceDecrement()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/dialog.qml"_qs));
+ 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"));
+
+ QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
+ QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
+
+ 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);
+
+ QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
+ QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
+
+ QCOMPARE(object->property("a").toInt(), 1024);
+}
+
+void tst_QmlCppCodegen::shifts()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/shifts.qml"_qs));
+ 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);
+}
+
+void tst_QmlCppCodegen::valueTypeProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/valueTypeProperty.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QFont font = qvariant_cast<QFont>(object->property("font"));
+ QCOMPARE(object->property("foo").toString(), font.family());
+ font.setFamily(u"Bar"_qs);
+ object->setProperty("font", QVariant::fromValue(font));
+ QCOMPARE(object->property("foo").toString(), u"Bar"_qs);
+}
+
+void tst_QmlCppCodegen::propertyOfParent()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/RootWithoutId.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QObject *child = qmlContext(object.data())->objectForName(u"item"_qs);
+
+ bool expected = false;
+
+ for (int i = 0; i < 3; ++i) {
+ const QVariant foo = object->property("foo");
+ QCOMPARE(foo.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(foo.toBool(), expected);
+
+ const QVariant bar = object->property("bar");
+ QCOMPARE(bar.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(bar.toBool(), expected);
+
+ const QVariant visible = child->property("visible");
+ QCOMPARE(visible.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(visible.toBool(), expected);
+
+ expected = !expected;
+ object->setProperty("foo", expected);
+ }
+}
+
+void tst_QmlCppCodegen::accessModelMethodFromOutSide()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/AccessModelMethodsFromOutside.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtDebugMsg, "3");
+ QTest::ignoreMessage(QtDebugMsg, "Apple");
+ QScopedPointer<QObject> object(component.create());
+
+ QCOMPARE(object->property("cost1").toDouble(), 3);
+ QCOMPARE(object->property("name1").toString(), u"Orange"_qs);
+ QCOMPARE(object->property("cost2").toDouble(), 1.95);
+ QCOMPARE(object->property("name2").toString(), u"Banana"_qs);
+}
+
+void tst_QmlCppCodegen::functionArguments()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/Dummy.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ const QMetaObject *metaObject = object->metaObject();
+
+ int result;
+ int a = 1;
+ bool b = false;
+ QObject *c = nullptr;
+ double d = -1.2;
+ int e = 3;
+
+ const QByteArray className = QByteArray(metaObject->className()) + '*';
+ metaObject->invokeMethod(
+ object.data(), "someFunction", Q_RETURN_ARG(int, result),
+ Q_ARG(int, a), Q_ARG(bool, b), QGenericArgument(className, &c),
+ Q_ARG(double, d), Q_ARG(int, e));
+ QCOMPARE(result, 42);
+
+ QString astr = u"foo"_qs;
+ QString bstr = u"bar"_qs;
+ QString concatenated;
+ metaObject->invokeMethod(
+ object.data(), "concat", Q_RETURN_ARG(QString, concatenated),
+ Q_ARG(QString, astr), Q_ARG(QString, bstr));
+ QCOMPARE(concatenated, u"foobar"_qs);
+}
+
+void tst_QmlCppCodegen::bindingExpression()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/BindingExpression.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QObject *child = qmlContext(object.data())->objectForName(u"child"_qs);
+
+ 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"_qs : u"light"_qs);
+ 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;
+ }
+
+ width = 200 * i;
+ y = 10 + i;
+ object->setProperty("width", width);
+ object->setProperty("y", y);
+ }
+}
+
+void tst_QmlCppCodegen::voidFunction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/voidfunction.qml"_qs));
+ 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"_qs);
+}
+
+void tst_QmlCppCodegen::overriddenProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/childobject.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->objectName(), u"kraut"_qs);
+ QCOMPARE(object->property("doneThing").toInt(), 5);
+ QCOMPARE(object->property("usingFinal").toInt(), 5);
+
+ auto checkAssignment = [&]() {
+ const QString newName = u"worscht"_qs;
+ QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName));
+ QCOMPARE(object->objectName(), newName);
+ };
+ checkAssignment();
+
+ ObjectWithMethod *benign = new ObjectWithMethod(object.data());
+ benign->theThing = 10;
+ benign->setObjectName(u"cabbage"_qs);
+ object->setProperty("child", QVariant::fromValue(benign));
+ QCOMPARE(object->objectName(), u"cabbage"_qs);
+ 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"_qs);
+
+ checkAssignment();
+ QCOMPARE(object->property("doneThing").toInt(), 7);
+ QCOMPARE(object->property("usingFinal").toInt(), 5);
+}
+
+void tst_QmlCppCodegen::listLength()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/listlength.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("l").toInt(), 2);
+}
+
+void tst_QmlCppCodegen::parentProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/parentProp.qml"_qs));
+ 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);
+
+ QObject *child = qmlContext(object.data())->objectForName(u"child"_qs);
+ QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_qs);
+ QObject *evil = qmlContext(object.data())->objectForName(u"evil"_qs);
+
+ 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:/TestTypes/specificParent.qml"_qs));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->property("a").toReal(), 77.0);
+ }
+}
+
+void tst_QmlCppCodegen::registerElimination()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/registerelimination.qml"_qs));
+ 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::asCast()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/asCast.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QQmlContext *context = qmlContext(root.data());
+ const QObject *object = context->objectForName(u"object"_qs);
+ const QObject *item = context->objectForName(u"item"_qs);
+ const QObject *rectangle = context->objectForName(u"rectangle"_qs);
+ const QObject *dummy = context->objectForName(u"dummy"_qs);
+
+ 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);
+}
+
+void tst_QmlCppCodegen::noQQmlData()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/noQQmlData.qml"_qs));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/noQQmlData.qml:7: TypeError: "
+ "Cannot read property 'name' of null");
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data());
+ QVERIFY(party != nullptr);
+
+ QCOMPARE(party->host(), nullptr);
+ QCOMPARE(party->property("n").toString(), QString());
+
+ Person *host1 = new Person(party);
+ party->setHost(host1);
+ QCOMPARE(party->property("n").toString(), u"Bart in da house!"_qs);
+ host1->setName(u"Marge"_qs);
+ QCOMPARE(party->property("n").toString(), u"Marge in da house!"_qs);
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/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!"_qs);
+ host2->setName(u"Homer"_qs);
+ QCOMPARE(party->property("n").toString(), u"Homer in da house!"_qs);
+
+ QMetaObject::invokeMethod(party, "burn");
+ engine.collectGarbage();
+
+ // Does not crash
+ party->setProperty("inDaHouse", u" burns!"_qs);
+
+ // 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"_qs));
+}
+
+void tst_QmlCppCodegen::scopeObjectDestruction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/fileDialog.qml"_qs));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ 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");
+}
+
+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);
+}
+
+void tst_QmlCppCodegen::colorAsVariant()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/colorAsVariant.qml"_qs));
+ checkColorProperties(&component);
+}
+
+void tst_QmlCppCodegen::bindToValueType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/bindToValueType.qml"_qs));
+ checkColorProperties(&component);
+}
+
+void tst_QmlCppCodegen::undefinedResets()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/undefinedResets.qml"_qs));
+
+ 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"_qs);
+
+ person->setShoeSize(11);
+
+ QCOMPARE(person->shoeSize(), 11);
+ QCOMPARE(person->name(), u"Bart"_qs);
+
+ person->setShoeSize(10);
+ QCOMPARE(person->shoeSize(), 10);
+ QCOMPARE(person->name(), u"Marge"_qs);
+}
+
+void tst_QmlCppCodegen::innerObjectNonShadowable()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/ownProperty.qml"_qs));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->objectName(), u"foo"_qs);
+}
+
+void tst_QmlCppCodegen::ownPropertiesNonShadowable()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/overriddenMember.qml"_qs));
+
+ 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::modulePrefix()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/modulePrefix.qml"_qs));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ 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"));
+}
+
+void tst_QmlCppCodegen::colorString()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/colorString.qml"_qs));
+
+ 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::urlString()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/TestTypes/urlString.qml"_qs));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_qs));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_qs));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_qs));
+}
+
+
+void tst_QmlCppCodegen::callContextPropertyLookupResult()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/callContextPropertyLookupResult.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr);
+}
+
+void tst_QmlCppCodegen::deadShoeSize()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/deadShoeSize.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/deadShoeSize.qml:5: Error: ouch");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("shoeSize").toInt(), 0);
+}
+
+void tst_QmlCppCodegen::listIndices()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/listIndices.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ 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);
+}
+
+void tst_QmlCppCodegen::jsMathObject()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/jsMathObject.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ const double inputs[] = {
+ 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,
+ std::copysign(0.0, -1), 0.0,
+ 0.5, 0.77, 1, 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(),
+ };
+
+ QJSManagedValue math(engine.globalObject().property(QStringLiteral("Math")), &engine);
+
+ const QMetaObject *metaObject = o->metaObject();
+
+ QString name;
+ for (double a : inputs) {
+ for (double b : inputs) {
+ o->setProperty("a", a);
+ o->setProperty("b", b);
+ for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) {
+ const QMetaProperty prop = metaObject->property(i);
+ name = QString::fromUtf8(prop.name());
+
+ if (!math.hasProperty(name))
+ continue;
+
+ const double result = prop.read(o.data()).toDouble();
+ if (name == QStringLiteral("random")) {
+ QVERIFY(result >= 0.0 && result < 1.0);
+ continue;
+ }
+
+ QJSManagedValue jsMethod(math.property(name), &engine);
+ const double expected = jsMethod.call(QJSValueList {a, b}).toNumber();
+ QCOMPARE(result, expected);
+ }
+ }
+ }
+
+ if (QTest::currentTestFailed())
+ qDebug() << name << "failed.";
+}
+
+void tst_QmlCppCodegen::intEnumCompare()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/intEnumCompare.qml"_qs));
+ 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:/TestTypes/enumInvalid.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("c").toBool(), false);
+ QCOMPARE(o->property("d").toBool(), false);
+ }
+}
+
+void tst_QmlCppCodegen::runInterpreted()
+{
+ if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
+ QSKIP("Already running in interpreted mode");
+
+#if QT_CONFIG(process)
+ QProcess process;
+ process.setProgram(QCoreApplication::applicationFilePath());
+ process.setEnvironment(QProcess::systemEnvironment()
+ + QStringList(u"QV4_FORCE_INTERPRETER=1"_qs));
+ process.start();
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ QCOMPARE(process.exitCode(), 0);
+#else
+ QSKIP("Test needs QProcess");
+#endif
+}
+
+QTEST_MAIN(tst_QmlCppCodegen)
+
+#include "tst_qmlcppcodegen.moc"
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index 29fe609469..3648a10f95 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -36,6 +36,8 @@
#include <QSaveFile>
#include <QScopedPointer>
#include <QScopeGuard>
+#include <QLibraryInfo>
+#include <QLoggingCategory>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmljsparser_p.h>
@@ -47,7 +49,9 @@
#include <algorithm>
-using namespace QQmlJS;
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcAotCompiler);
+QT_END_NAMESPACE
static bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments)
{
@@ -106,12 +110,21 @@ int main(int argc, char **argv)
QCommandLineOption directCallsOption(QStringLiteral("direct-calls"), QCoreApplication::translate("main", "This option is ignored."));
directCallsOption.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(directCallsOption);
- QCommandLineOption includesOption(QStringLiteral("i"), QCoreApplication::translate("main", "This option is ignored."), QCoreApplication::translate("main", "ignored file"));
- includesOption.setFlags(QCommandLineOption::HiddenFromHelp);
- parser.addOption(includesOption);
- QCommandLineOption importPathOption(QStringLiteral("I"), QCoreApplication::translate("main", "This option is ignored."), QCoreApplication::translate("main", "ignored path"));
- importPathOption.setFlags(QCommandLineOption::HiddenFromHelp);
+ QCommandLineOption importsOption(
+ QStringLiteral("i"),
+ QCoreApplication::translate("main", "Import extra qmltypes"),
+ QCoreApplication::translate("main", "qmltypes file"));
+ parser.addOption(importsOption);
+ QCommandLineOption importPathOption(
+ QStringLiteral("I"),
+ QCoreApplication::translate("main", "Look for QML modules in specified directory"),
+ QCoreApplication::translate("main", "import directory"));
parser.addOption(importPathOption);
+ QCommandLineOption onlyBytecode(
+ QStringLiteral("only-bytecode"),
+ QCoreApplication::translate(
+ "main", "Generate only byte code for bindings and functions, no C++ code"));
+ parser.addOption(onlyBytecode);
QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
parser.addOption(outputFileOption);
@@ -188,35 +201,34 @@ int main(int argc, char **argv)
QString inputFileUrl = inputFile;
QQmlJSSaveFunction saveFunction;
- if (target == GenerateCpp) {
- QQmlJSResourceFileMapper fileMapper(parser.values(resourceOption));
- QString inputResourcePath = parser.value(resourcePathOption);
-
- // If the user didn't specify the resource path corresponding to the file on disk being
- // compiled, try to determine it from the resource file, if one was supplied.
- if (inputResourcePath.isEmpty()) {
- const QStringList resourcePaths = fileMapper.resourcePaths(
- QQmlJSResourceFileMapper::localFileFilter(inputFile));
- if (resourcePaths.isEmpty()) {
- fprintf(stderr, "No resource path for file: %s\n", qPrintable(inputFile));
- return EXIT_FAILURE;
- }
-
- if (resourcePaths.size() != 1) {
- fprintf(stderr, "Multiple resource paths for file %s. "
- "Use the --%s option to disambiguate:\n",
- qPrintable(inputFile),
- qPrintable(resourcePathOption.names().first()));
- for (const QString &resourcePath: resourcePaths)
- fprintf(stderr, "\t%s\n", qPrintable(resourcePath));
- return EXIT_FAILURE;
- }
+ QQmlJSResourceFileMapper fileMapper(parser.values(resourceOption));
+ QString inputResourcePath = parser.value(resourcePathOption);
+
+ // If the user didn't specify the resource path corresponding to the file on disk being
+ // compiled, try to determine it from the resource file, if one was supplied.
+ if (inputResourcePath.isEmpty()) {
+ const QStringList resourcePaths = fileMapper.resourcePaths(
+ QQmlJSResourceFileMapper::localFileFilter(inputFile));
+ if (target == GenerateCpp && resourcePaths.isEmpty()) {
+ fprintf(stderr, "No resource path for file: %s\n", qPrintable(inputFile));
+ return EXIT_FAILURE;
+ }
+ if (resourcePaths.size() == 1) {
inputResourcePath = resourcePaths.first();
+ } else if (target == GenerateCpp) {
+ fprintf(stderr, "Multiple resource paths for file %s. "
+ "Use the --%s option to disambiguate:\n",
+ qPrintable(inputFile),
+ qPrintable(resourcePathOption.names().first()));
+ for (const QString &resourcePath: resourcePaths)
+ fprintf(stderr, "\t%s\n", qPrintable(resourcePath));
+ return EXIT_FAILURE;
}
+ }
+ if (target == GenerateCpp) {
inputFileUrl = QStringLiteral("qrc://") + inputResourcePath;
-
saveFunction = [inputResourcePath, outputFileName](
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions,
@@ -240,9 +252,47 @@ int main(int argc, char **argv)
if (inputFile.endsWith(QLatin1String(".qml"))) {
QQmlJSCompileError error;
- if (!qCompileQmlFile(inputFile, saveFunction, nullptr, &error)) {
- error.augment(QLatin1String("Error compiling qml file: ")).print();
- return EXIT_FAILURE;
+ if (target != GenerateCpp || inputResourcePath.isEmpty() || parser.isSet(onlyBytecode)) {
+ if (!qCompileQmlFile(inputFile, saveFunction, nullptr, &error,
+ /* storeSourceLocation */ false)) {
+ error.augment(QStringLiteral("Error compiling qml file: ")).print();
+ return EXIT_FAILURE;
+ }
+ } else {
+ QStringList importPaths;
+ if (parser.isSet(importPathOption))
+ importPaths = parser.values(importPathOption);
+
+ importPaths.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
+
+ QQmlJSImporter importer(
+ importPaths, parser.isSet(resourceOption) ? &fileMapper : nullptr);
+ QQmlJSLogger logger;
+
+ // Always trigger the qFatal() on "pragma Strict" violations.
+ logger.setCategoryError(Log_Compiler, true);
+
+ // By default, we're completely silent,
+ // as the lcAotCompiler category default is QtFatalMsg
+ if (lcAotCompiler().isDebugEnabled())
+ logger.setCategoryLevel(Log_Compiler, QtDebugMsg);
+ else if (lcAotCompiler().isInfoEnabled())
+ logger.setCategoryLevel(Log_Compiler, QtInfoMsg);
+ else if (lcAotCompiler().isWarningEnabled())
+ logger.setCategoryLevel(Log_Compiler, QtWarningMsg);
+ else if (lcAotCompiler().isCriticalEnabled())
+ logger.setCategoryLevel(Log_Compiler, QtCriticalMsg);
+ else
+ logger.setSilent(true);
+
+ QQmlJSAotCompiler cppCodeGen(
+ &importer, u':' + inputResourcePath, parser.values(importsOption), &logger);
+
+ if (!qCompileQmlFile(inputFile, saveFunction, &cppCodeGen, &error,
+ /* storeSourceLocation */ true)) {
+ error.augment(QStringLiteral("Error compiling qml file: ")).print();
+ return EXIT_FAILURE;
+ }
}
} else if (inputFile.endsWith(QLatin1String(".js")) || inputFile.endsWith(QLatin1String(".mjs"))) {
QQmlJSCompileError error;