aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qmlcppcodegen
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qmlcppcodegen')
-rw-r--r--tests/auto/qml/qmlcppcodegen/CMakeLists.txt18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Action.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/B.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt279
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Dummy2.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js3
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Panel.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Planner.qml50
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Variable.qml22
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ambiguous.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/asCast.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml40
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.h8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml45
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/childobject.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml39
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml54
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml32
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversions2.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dateConversions.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h29
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h41
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumConversion.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumProblems.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumProperty.h71
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumproblems.h68
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/extra/extra.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/failures.qml60
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml23
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/fileDialog.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/flagEnum.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h38
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h42
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/idAccess.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml39
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/intToEnum.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/interactive.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/internalConversion.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/invisible.h36
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/iteration.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml28
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml26
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listConversion.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listToString.qml25
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listprovider.h24
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/math.qml1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml59
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/methods.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/multiforeign.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullComparison.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml37
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml42
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h53
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml77
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/person.cpp20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/person.h44
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/resettable.h39
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/resettable.qml23
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h56
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml31
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml35
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/state.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/theme.cpp2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/theme.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/thisObject.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/timelinetheme.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/trigraphs.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp47
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/urlString.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml43
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantMap.qml27
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantReturn.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantreturn.h63
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/voidConversion.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h61
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/withlength.h28
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h31
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/writeback.qml43
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp5745
151 files changed, 6959 insertions, 1981 deletions
diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
index 8644e00cde..715ad6162a 100644
--- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlcppcodegen LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
add_subdirectory(data)
qt_internal_add_test(tst_qmlcppcodegen
@@ -8,9 +14,13 @@ qt_internal_add_test(tst_qmlcppcodegen
tst_qmlcppcodegen.cpp
LIBRARIES
Qt::QmlPrivate
- Qt::Gui
+ Qt::GuiPrivate
codegen_test_module
codegen_test_moduleplugin
+ codegen_test_hidden
+ codegen_test_hiddenplugin
+ codegen_test_stringbuilder
+ codegen_test_stringbuilderplugin
)
qt_internal_add_test(tst_qmlcppcodegen_interpreted
@@ -18,9 +28,13 @@ qt_internal_add_test(tst_qmlcppcodegen_interpreted
tst_qmlcppcodegen.cpp
LIBRARIES
Qt::QmlPrivate
- Qt::Gui
+ Qt::GuiPrivate
codegen_test_module
codegen_test_moduleplugin
+ codegen_test_hidden
+ codegen_test_hiddenplugin
+ codegen_test_stringbuilder
+ codegen_test_stringbuilderplugin
DEFINES
QT_TEST_FORCE_INTERPRETER
)
diff --git a/tests/auto/qml/qmlcppcodegen/data/Action.qml b/tests/auto/qml/qmlcppcodegen/data/Action.qml
new file mode 100644
index 0000000000..99b86fb31c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Action.qml
@@ -0,0 +1,7 @@
+import QtQuick
+import QtQuick.Controls as QQC2
+import QtQuick.Templates as T
+
+QQC2.Action {
+ property bool visible: true
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/B.qml b/tests/auto/qml/qmlcppcodegen/data/B.qml
new file mode 100644
index 0000000000..97e895ecad
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/B.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property rect r
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml b/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml
new file mode 100644
index 0000000000..2615778f6a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml
@@ -0,0 +1,9 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property int satisfaction: Satisfaction.NONE
+ property QtObject output
+
+ function inputsKnown(mark: int) : bool { return true }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 0737d77005..7f2e6ad967 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -4,41 +4,61 @@
set(cpp_sources
ambiguous.h
birthdayparty.cpp birthdayparty.h
+ convertQJSPrimitiveValueToIntegral.h
cppbaseclass.h
druggeljug.h
+ dummyobjekt.h
dynamicmeta.h
enumproblems.h
enumProperty.h
+ getOptionalLookup.h
gadgetwithenum.h
invisible.h
+ listprovider.h
multiforeign.h
objectwithmethod.h
person.cpp person.h
+ resettable.h
+ sequenceToIterable.h
sequencetypeexample.cpp sequencetypeexample.h
state.h
theme.cpp theme.h
timelinetheme.cpp timelinetheme.h
+ variantMapLookup.h
+ variantreturn.h
+ weathermoduleurl.h
wrapwithvariant.h
withlength.h
)
set(qml_files
AccessModelMethodsFromOutside.qml
+ Action.qml
ArraySequenceLengthInterop.qml
+ B.qml
BadType.qml
+ BaseConstraint.qml
BaseMember.qml
BindingExpression.qml
+ CxxTypeFromDir.qml
+ CxxTypeFromImplicit.qml
Cycle1.qml
Cycle2.qml
Cycle3.qml
- CxxTypeFromDir.qml
- CxxTypeFromImplicit.qml
+ CppMethodListReturnType.qml
Dummy.qml
+ Dummy2.qml
+ EditConstraint.qml
Enums.qml
Foozle.qml
+ GetOptionalLookupOnQJSValueNonStrict.qml
+ GetOptionalLookupShadowed.qml
Loopy.qml
+ NotificationItem.qml
+ NotificationsUtils.js
OkType.qml
Panel.qml
+ Planner.qml
ProgressBar/Keyframe.qml
ProgressBar/KeyframeGroup.qml
ProgressBar/ProgressBar.ui.qml
@@ -46,98 +66,145 @@ set(qml_files
ProgressBar/Timeline.qml
ProgressBar/TimelineAnimation.qml
RootWithoutId.qml
+ Satisfaction.qml
SelectionRectangle.qml
+ ShadowedObjectName.qml
+ ShadowedObjectNameDerived.qml
+ StoreMetaEnum.qml
Test.qml
TestCase.qml
+ Variable.qml
WindowDerived.qml
aliasLookup.qml
ambiguous1/Ambiguous.qml
ambiguous2/Ambiguous.qml
+ ambiguousAs.qml
ambiguousSignals.qml
anchorsFill.qml
argumentConversion.qml
array.qml
+ arrayCtor.qml
asCast.qml
attachedBaseEnum.qml
badSequence.qml
+ basicBlocksWithBackJump.qml
+ basicBlocksWithBackJump_infinite.qml
+ basicDTZ.qml
bindToValueType.qml
blockComments.qml
+ boolCoercions.qml
+ boolPointerMerge.qml
boundComponents.qml
callContextPropertyLookupResult.qml
callWithSpread.qml
childobject.qml
colorAsVariant.qml
colorString.qml
- consoleObject.qml
+ compareOriginals.qml
+ comparisonTypes.qml
componentReturnType.qml
compositeTypeMethod.qml
compositesingleton.qml
+ consoleObject.qml
+ consoleTrace.qml
construct.qml
contextParam.qml
conversionDecrement.qml
+ conversionInDeadCode.qml
conversions.qml
conversions2.qml
+ convertPrimitiveToVar.qml
+ convertQJSPrimitiveValueToIntegral.qml
+ convertToOriginalReadAcumulatorForUnaryOperators.qml
curlygrouped.qml
cycleHead.qml
+ dateConstruction.qml
dateConversions.qml
deadShoeSize.qml
deadStoreLoop.qml
dialog.qml
+ dialogButtonBox.qml
dynamicscene.qml
+ enforceSignature.qml
enumConversion.qml
+ enumFromBadSingleton.qml
enumInvalid.qml
enumLookup.qml
+ enumMarkedAsFlag.qml
enumProblems.qml
enumScope.qml
enumsInOtherObject.qml
enumsUser.qml
equalityQObjects.qml
+ equalityQUrl.qml
+ equalityTestsWithNullOrUndefined.qml
equalityVarAndNonStorable.qml
equalsUndefined.qml
+ exceptionFromInner.qml
excessiveParameters.qml
extendedTypes.qml
+ extra/extra.qml
failures.qml
fallbacklookups.qml
+ fallbackresettable.qml
fileDialog.qml
+ flagEnum.qml
fromBoolValue.qml
- functionLookup.qml
funcWithParams.qml
+ functionLookup.qml
functionReturningVoid.qml
functionTakingVar.qml
+ getOptionalLookup.qml
globals.qml
- hidden/Main.qml
- hidden/Style.qml
idAccess.qml
+ ignoredFunctionReturn.qml
immediateQuit.qml
imports/QmlBench/Globals.qml
importsFromImportPath.qml
+ indirectlyShadowable.qml
infinities.qml
infinitiesToInt.qml
- invisibleBase.qml
- invisibleTypes.qml
- invisibleListElementType.qml
intEnumCompare.qml
intOverflow.qml
+ intToEnum.qml
interactive.qml
interceptor.qml
+ internalConversion.qml
+ invisibleBase.qml
+ invisibleListElementType.qml
+ invisibleTypes.qml
isnan.qml
+ iteration.qml
javaScriptArgument.qml
+ jsArrayMethods.qml
+ jsArrayMethodsUntyped.qml
+ jsArrayMethodsWithParams.qml
+ jsArrayMethodsWithParamsUntyped.qml
jsMathObject.qml
jsimport.qml
jsmoduleimport.qml
layouts.qml
- library.js
letAndConst.qml
+ library.js
listAsArgument.qml
+ listConversion.qml
listIndices.qml
+ listOfInvisible.qml
listPropertyAsModel.qml
+ listToString.qml
listlength.qml
math.qml
+ mathMinMax.qml
mathOperations.qml
+ mathStaticProperties.qml
+ mergedObjectRead.qml
+ mergedObjectWrite.qml
+ methodOnListLookup.qml
methods.qml
modulePrefix.qml
moveRegVoid.qml
multiforeign.qml
+ multipleCtors.qml
namespaceWithEnum.qml
noBindingLoop.qml
noQQmlData.qml
@@ -146,9 +213,14 @@ set(qml_files
notEqualsInt.qml
notNotString.qml
nullAccess.qml
+ nullAccessInsideSignalHandler.qml
nullComparison.qml
+ nullishCoalescing.qml
numbersInJsPrimitive.qml
objectInVar.qml
+ objectLookupOnListElement.qml
+ objectWithStringListMethod.qml
+ optionalComparison.qml
outOfBounds.qml
overriddenMember.qml
ownProperty.qml
@@ -157,48 +229,76 @@ set(qml_files
popContextAfterRet.qml
prefixedMetaType.qml
pressAndHoldButton.qml
- registerelimination.qml
+ qtbug113150.qml
+ reduceWithNullThis.qml
+ readEnumFromInstance.qml
+ readonlyListProperty.qml
registerPropagation.qml
+ registerelimination.qml
+ renameAdjust.qml
+ resettable.qml
+ returnAfterReject.qml
revisions.qml
+ scopeIdLookup.qml
scopeVsObject.qml
+ scopedEnum.qml
script.js
script.mjs
+ sequenceToIterable.qml
+ setLookupConversion.qml
+ setLookupOriginalScope.qml
+ shadowedAsCasts.qml
+ shadowedMethod.qml
+ shadowedPrimitiveCmpEqNull.qml
shared/Slider.qml
shifts.qml
signal.qml
signalHandler.qml
signalIndexMismatch.qml
+ signalsWithLists.qml
signatureIgnored.qml
specificParent.qml
storeElementSideEffects.qml
stringArg.qml
stringLength.qml
stringToByteArray.qml
+ structuredValueType.qml
testlogger.js
text.qml
themerbad.qml
themergood.qml
+ thisObject.qml
throwObjectName.qml
toString.qml
+ topLevelComponent.qml
translation.qml
+ trigraphs.qml
trivialSignalHandler.qml
typePropagationLoop.qml
typePropertyClash.qml
typedArray.qml
undefinedResets.qml
+ undefinedToDouble.qml
unknownAttached.qml
unknownParameter.qml
unstoredUndefined.qml
unusedAttached.qml
urlString.qml
usingCxxTypesFromFileImports.qml
+ valueTypeCast.qml
valueTypeCopy.qml
+ valueTypeDefault.qml
valueTypeLists.qml
valueTypeProperty.qml
valueTypeReference.qml
+ variantMap.qml
+ variantMapLookup.qml
+ variantReturn.qml
variantlist.qml
versionmismatch.qml
+ voidConversion.qml
voidfunction.qml
+ writeback.qml
dummy_imports.qml
)
@@ -214,21 +314,88 @@ set_source_files_properties("shared/Slider.qml"
set_source_files_properties("hidden/Style.qml"
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
+qt_policy(SET QTP0001 NEW)
+
+# Add the module for the hidden files before setting QTP0004, so that we don't add a qmldir in
+# "hidden". That would defeat the purpose.
+
+qt_add_library(codegen_test_hidden STATIC)
+qt_autogen_tools_initial_setup(codegen_test_hidden)
+
+set_target_properties(codegen_test_hidden PROPERTIES
+ # We really want qmlcachegen here, even if qmlsc is available
+ QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+target_compile_definitions(codegen_test_hidden PUBLIC
+ -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache"
+)
+
+qt_policy(SET QTP0004 OLD)
+qt6_add_qml_module(codegen_test_hidden
+ URI HiddenTestTypes
+ QML_FILES
+ hidden/Main.qml
+ hidden/Style.qml
+ OUTPUT_DIRECTORY HiddenTestTypes
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
+)
+
+add_dependencies(codegen_test_hidden Qt::Quick)
+
+qt_autogen_tools_initial_setup(codegen_test_hiddenplugin)
+
+qt_policy(SET QTP0004 NEW)
+
+qt_add_library(codegen_test_stringbuilder STATIC)
+qt_autogen_tools_initial_setup(codegen_test_stringbuilder)
+
+set_target_properties(codegen_test_stringbuilder PROPERTIES
+ # We really want qmlcachegen here, even if qmlsc is available
+ QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+target_compile_definitions(codegen_test_stringbuilder PRIVATE
+ -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache"
+ QT_USE_QSTRINGBUILDER
+)
+
+qt6_add_qml_module(codegen_test_stringbuilder
+ URI StringBuilderTestTypes
+ SOURCES
+ writableVariantMap.h
+ QML_FILES
+ writeVariantMap.qml
+ OUTPUT_DIRECTORY stringbuilderTestTypes
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
+)
+
+qt_autogen_tools_initial_setup(codegen_test_stringbuilderplugin)
+
qt_add_library(codegen_test_module STATIC)
qt_autogen_tools_initial_setup(codegen_test_module)
set_target_properties(codegen_test_module PROPERTIES
# We really want qmlcachegen here, even if qmlsc is available
QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+
+
+target_compile_definitions(codegen_test_module PUBLIC
+ -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache"
)
qt6_add_qml_module(codegen_test_module
VERSION 1.5
URI TestTypes
IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/"
- AUTO_RESOURCE_PREFIX
DEPENDENCIES
QtQuick
+ QtQuick.Controls
QtQuick.Templates
QtQuick.Shapes
SOURCES
@@ -238,8 +405,96 @@ qt6_add_qml_module(codegen_test_module
RESOURCES
${resource_files}
OUTPUT_DIRECTORY TestTypes # Make sure tst_qmlcachegen doesn't see our output
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
)
+if(${CMAKE_VERSION} GREATER_EQUAL "3.19.0")
+ qt_target_qml_sources(codegen_test_module
+ QML_FILES extra2/extra.qml
+ )
+else()
+ target_compile_definitions(codegen_test_module PUBLIC
+ -DVERY_OLD_CMAKE=1
+ )
+endif()
+
add_dependencies(codegen_test_module Qt::Quick Qt::QuickTemplates2 Qt::QuickShapesPrivate)
qt_autogen_tools_initial_setup(codegen_test_moduleplugin)
+
+
+qt_add_library(codegen_test_module_verify STATIC)
+qt_autogen_tools_initial_setup(codegen_test_module_verify)
+
+set_target_properties(codegen_test_module_verify PROPERTIES
+ # We really want qmlcachegen here, even if qmlsc is available
+ QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+
+qt6_add_qml_module(codegen_test_module_verify
+ VERSION 1.5
+ URI TestTypes
+ IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/"
+ DEPENDENCIES
+ QtQuick
+ QtQuick.Controls
+ QtQuick.Templates
+ QtQuick.Shapes
+ SOURCES
+ ${cpp_sources}
+ QML_FILES
+ ${qml_files}
+ RESOURCES
+ ${resource_files}
+ OUTPUT_DIRECTORY verify/TestTypes # Make sure tst_qmlcachegen doesn't see our output
+ TARGET_PATH verify/TestTypes # Different target path to avoid resource file name clashes
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
+)
+
+add_dependencies(codegen_test_module_verify Qt::Quick Qt::QuickTemplates2 Qt::QuickShapesPrivate)
+
+qt_autogen_tools_initial_setup(codegen_test_module_verifyplugin)
+
+
+qt_internal_add_test(tst_qmlcppcodegen_verify
+ SOURCES
+ tst_qmlcppcodegen_verify.cpp
+)
+
+add_dependencies(tst_qmlcppcodegen_verify codegen_test_module codegen_test_module_verify)
+
+set(a_files "")
+set(b_files "")
+
+foreach(qml_file IN LISTS qml_files)
+ string(REGEX REPLACE "\\.(js|mjs|qml)$" "_\\1" compiled_file ${qml_file})
+ string(REGEX REPLACE "[$#?]+" "_" compiled_file ${compiled_file})
+
+ list(APPEND
+ a_files
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/codegen_test_module_${compiled_file}.cpp")
+
+ list(APPEND
+ b_files
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/codegen_test_module_verify_${compiled_file}.cpp")
+endforeach()
+
+qt_add_resources(tst_qmlcppcodegen_verify "a"
+ PREFIX
+ "/a"
+ FILES
+ ${a_files}
+ BASE
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/"
+)
+
+qt_add_resources(tst_qmlcppcodegen_verify "b"
+ PREFIX
+ "/b"
+ FILES
+ ${b_files}
+ BASE
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/"
+)
diff --git a/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml
new file mode 100644
index 0000000000..9c3ce4e877
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml
@@ -0,0 +1,12 @@
+pragma Strict
+
+import QtQuick
+import TestTypes
+
+Item {
+ ListProvider {
+ id: listProvider
+ }
+
+ property var list: listProvider.intList()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml b/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml
new file mode 100644
index 0000000000..a3bbef1888
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+
+import QtQml
+
+QtObject {
+ property int value
+ property Dummy2 child
+ property int dummyEnum
+
+ signal triggered()
+ signal signalWithArg(int one, bool two)
+ property real onValue
+ property real offValue
+
+ function someFunction(a: int, b: bool, c: Dummy2, d: real, e: int) : int { return 42 }
+ property string strProp
+ function concat(a: string, b: string) : string { return a + b }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml b/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml
new file mode 100644
index 0000000000..ce278fbdf9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property Variable myOutput
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml
new file mode 100644
index 0000000000..5a89e996b4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ property Action action: Action { }
+ property bool b: action?.visible
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml
new file mode 100644
index 0000000000..eacefc3017
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml
@@ -0,0 +1,19 @@
+pragma Strict
+
+import QtQml
+import QtQuick
+
+QtObject {
+ id: root
+
+ component Base : QtObject {
+ property int i: 1
+ }
+
+ component Derived : Base {
+ property string i: "a"
+ }
+
+ property Base base: Derived { }
+ property var res: root.base?.i
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml b/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml
new file mode 100644
index 0000000000..fba4df6453
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes as MobileShell
+
+QtObject {
+ id: notificationItem
+ objectName: MobileShell.NotificationsUtils.determineNotificationHeadingText(notificationItem)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js b/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js
new file mode 100644
index 0000000000..079270e1b9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js
@@ -0,0 +1,3 @@
+function determineNotificationHeadingText(notificationItem) {
+ return "heading";
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Panel.qml b/tests/auto/qml/qmlcppcodegen/data/Panel.qml
index 84e926b8d2..7f589d23e8 100644
--- a/tests/auto/qml/qmlcppcodegen/data/Panel.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/Panel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qmlcppcodegen/data/Planner.qml b/tests/auto/qml/qmlcppcodegen/data/Planner.qml
new file mode 100644
index 0000000000..ddb2fff053
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Planner.qml
@@ -0,0 +1,50 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: planner
+ property Variable last: Variable { value: 10 }
+
+ function newMark() : int {
+ return 5;
+ }
+
+ function addPropagate(i: int) : bool {
+ return false;
+ }
+
+ function typeErasedRemoveOne(v: QtObject) { removeOne(v as Variable) }
+
+ // Work with various shadowable members and return values.
+ function removeOne(v: Variable) {
+ let vDeterminedBy = v.determinedBy;
+ for (let i = 0, length = v.length(); i < length; ++i) {
+ let next = v.constraint(i) as BaseConstraint;
+ if (next.satisfaction === Satisfaction.NONE)
+ objectName += "n"
+ else if (next !== vDeterminedBy)
+ objectName += "d"
+ else
+ objectName += "x"
+ }
+ }
+
+ function typeErasedRun(c: QtObject) { run(c as BaseConstraint) }
+
+ function run(initial: BaseConstraint) {
+ let mark = planner.newMark();
+ let c = initial;
+
+ let output = c.output as Variable;
+ if (output.mark !== mark && c.inputsKnown(mark)) {
+ output.mark = mark;
+ }
+ }
+
+ function verify(i: int) {
+ if (last.value !== i)
+ console.error("failed", last.value, i);
+ else
+ console.log("success")
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
index 21a3832366..a44af04a50 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Window 2.3
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
index 707f0d9be9..43ff1b62b1 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
index b97e8956bf..75ad2245f2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
@@ -2,5 +2,5 @@ import QtQuick
NumberAnimation {
property bool pingPong
- signal finished()
+ signal finishedEvil()
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml b/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml
new file mode 100644
index 0000000000..74968c65ea
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ enum Value {
+ NONE = 0,
+ FORWARD = 1,
+ BACKWARD = 2
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml
new file mode 100644
index 0000000000..f079f4a94e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property int objectName: 12
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml
new file mode 100644
index 0000000000..f988cd6bc9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+ShadowedObjectName {
+ property double objectName: 17.4
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml b/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml
new file mode 100644
index 0000000000..eb3da15a3a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+QtObject {
+ enum Foo {
+ Bar,
+ Baz
+ }
+
+ property var eF: StoreMetaEnum.Foo
+ property int bar: eF.Bar
+ property int baz: eF.Baz
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Variable.qml b/tests/auto/qml/qmlcppcodegen/data/Variable.qml
new file mode 100644
index 0000000000..f5af97757f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Variable.qml
@@ -0,0 +1,22 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: variable
+ property int value: 0
+ property int mark: 0
+ property BaseConstraint determinedBy: null
+ property list<BaseConstraint> constraints: [
+ BaseConstraint {
+ satisfaction: variable.value == 0 ? Satisfaction.NONE : Satisfaction.FORWARD
+ }
+ ]
+
+ function length(): int {
+ return constraints.length
+ }
+
+ function constraint(i: int) : BaseConstraint {
+ return constraints[i];
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ambiguous.h b/tests/auto/qml/qmlcppcodegen/data/ambiguous.h
index 5ea20dbcd1..8f964d593c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ambiguous.h
+++ b/tests/auto/qml/qmlcppcodegen/data/ambiguous.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef AMBIGUOUS_H
#define AMBIGUOUS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml b/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml
new file mode 100644
index 0000000000..2b7cbd593d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property bool useSelf: true
+ property QtObject other: {
+ var a;
+ if (useSelf)
+ a = self
+ else
+ a = 15
+ return a as QtObject
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml b/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml
new file mode 100644
index 0000000000..9886a14cb1
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QML
+
+QtObject {
+ property list<int> defaultCtor: new Array()
+ property list<int> oneArgCtor: new Array(5)
+ property list<int> multiArgCtor: new Array(2, 3, 3, 4)
+ property list<bool> arrayTrue: new Array(true)
+ property list<bool> arrayFalse: new Array(false)
+ property list<real> arrayNegative: new Array(-14)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/asCast.qml b/tests/auto/qml/qmlcppcodegen/data/asCast.qml
index 1befc08d0a..cb8155ca6c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/asCast.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/asCast.qml
@@ -28,4 +28,14 @@ Item {
property QtObject dummyAsItem: dummy as Item
property QtObject dummyAsRectangle: dummy as Rectangle
property QtObject dummyAsDummy: dummy as Dummy
+
+ property QtObject nullAsObject: null as QtObject
+ property QtObject nullAsItem: null as Item
+ property QtObject nullAsRectangle: null as Rectangle
+ property QtObject nullAsDummy: null as Dummy
+
+ property QtObject undefinedAsObject: undefined as QtObject
+ property QtObject undefinedAsItem: undefined as Item
+ property QtObject undefinedAsRectangle: undefined as Rectangle
+ property QtObject undefinedAsDummy: undefined as Dummy
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml
new file mode 100644
index 0000000000..5b254fe494
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml
@@ -0,0 +1,33 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function t1() {
+ let i = 0
+ let foo = false
+ if (true) {
+ for (i = 0; i < 42 ; ++i) {}
+ } else {
+ console.log(foo)
+ }
+ }
+
+ function t2() {
+ let foo = false
+ if (false) {
+ while(Math.random() < 0.5) {}
+ } else {
+ console.log(foo)
+ }
+ }
+
+ function t3() {
+ let foo = false
+ if (Math.random() < 0.5) {
+ console.log(foo)
+ } else {
+ while(Math.random() < 0.5) {}
+ console.log(foo)
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml
new file mode 100644
index 0000000000..997ff68736
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function infinite() {
+ let foo = false
+ if (true) {
+ while (true) {}
+ } else {
+ console.log(foo)
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml b/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml
new file mode 100644
index 0000000000..bc9506a533
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml
@@ -0,0 +1,40 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: win
+ property real width: 640
+ property real height: 480
+ property string title: "none"
+
+ function t1(): void {
+ const w = win.width
+ const h = win.height
+
+ if (w > 0 && h > 0) {
+ const wByH = h / 3.0 * 4.0
+ }
+ }
+
+ function t2(): void {
+ let i = 42
+ win.title = "Foo " + i
+
+ for (let j = 0; j < 10; j++) {
+ win.title = "Bar " + j
+ }
+
+ for (let k = 0; k < i; k++) {
+ win.title = "Baz " + k
+ }
+ }
+
+ function t3(): void {
+ let v1 = 1
+ let v2 = 2
+
+ if (true) {
+ v1 = v2
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
index 048459f03a..10a2c90b38 100644
--- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "birthdayparty.h"
@@ -36,6 +36,23 @@ Person *BirthdayParty::guest(int index) const
return m_guests.at(index);
}
+QStringList BirthdayParty::guestNames() const
+{
+ QStringList names;
+ for (Person *guest: m_guests)
+ names.append(guest->name());
+ return names;
+}
+
+QVariantList BirthdayParty::stuffs() const
+{
+ return QVariantList({
+ QVariant::fromValue(objectName()),
+ QVariant::fromValue(m_guests.size()),
+ QVariant::fromValue(m_host)
+ });
+}
+
void BirthdayParty::invite(const QString &name)
{
auto *person = new Person(this);
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
index a6871c39b1..8dd640c67f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef BIRTHDAYPARTY_H
#define BIRTHDAYPARTY_H
@@ -52,7 +52,6 @@ private:
};
struct Foozle {
- Q_GADGET
int foo = 1;
};
@@ -61,6 +60,8 @@ class BirthdayParty : public QObject
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
+ Q_PROPERTY(QStringList guestNames READ guestNames FINAL)
+ Q_PROPERTY(QVariantList stuffs READ stuffs FINAL)
QML_ELEMENT
QML_ATTACHED(BirthdayPartyAttached)
QML_EXTENDED(BirthDayPartyExtended)
@@ -74,6 +75,9 @@ public:
int guestCount() const;
Person *guest(int) const;
+ QStringList guestNames() const;
+ QVariantList stuffs() const;
+
Q_INVOKABLE void invite(const QString &name);
static BirthdayPartyAttached *qmlAttachedProperties(QObject *object);
diff --git a/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml b/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml
new file mode 100644
index 0000000000..6b8ebb3f38
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml
@@ -0,0 +1,45 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property rect a
+ property bool t1: a
+
+ property int c: 1
+ property bool t2: c
+
+ property url e: "qrc:/ab/c.txt"
+ property bool t3: e
+
+ property string f: "a"
+ property bool t4: f
+
+ property var j: 1
+ property bool t5: j
+
+ id: k
+ property bool t6: k
+
+ property date l
+ property bool t7: l
+
+ property url m
+ property bool t8: m
+
+
+
+ property int b: 0
+ property bool f1: b
+
+ property QtObject d: null
+ property bool f2: d
+
+ property string g
+ property bool f3: g
+
+ property var h: undefined
+ property bool f4: h
+
+ property var i: null
+ property bool f5: i
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml b/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml
new file mode 100644
index 0000000000..b2d4d7c5d0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import TestTypes
+import QtQuick
+
+Loader {
+ id: self
+ source: "BaseMember.qml"
+ property int ppp: -99
+
+ onItemChanged: {
+ var base = item as BaseMember;
+ if (base)
+ base.ppp = ppp
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/childobject.qml b/tests/auto/qml/qmlcppcodegen/data/childobject.qml
index 3775ee16bc..76ad8fbbb2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/childobject.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/childobject.qml
@@ -4,6 +4,19 @@ import TestTypes
QtObject {
property ObjectWithMethod child: ObjectWithMethod {
objectName: "kraut"
+
+ function doString() { overloaded("string"); }
+ function doNumber() { overloaded(5.2); }
+ function doArray() { overloaded({a: 2, b: 3, c: 3}); }
+
+ function doString2() { overloaded2("string"); }
+ function doNumber2() { overloaded2(5.2); }
+
+ // Artificially pass an extra argument to avoid choosing the "string" overload.
+ // Unfortunately this is still order-dependent on the metaobject level.
+ function doArray2() { overloaded2({a: 2, b: 3, c: 3}, 1); }
+
+ function doFoo() { foo(this); }
}
objectName: child.objectName
property int doneThing: child.doThing()
diff --git a/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml b/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml
new file mode 100644
index 0000000000..3d40ffee62
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml
@@ -0,0 +1,39 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ component Variable: QtObject {
+ property int value: 4
+ }
+
+ property Variable first: Variable {}
+ property Variable last: Variable {
+ id: last
+ }
+
+ property int compareOriginals: {
+ var matches = 0;
+ for (var i = 0; i < 6; i++) {
+ first.value = i; // do a shadowed assignment
+ if (last.value != i)
+ ++matches
+ }
+ return matches;
+ }
+
+ property bool optionalThis: {
+ var a
+ if (2 == 2)
+ a = this
+ else
+ a = undefined
+
+ var b
+ if (2 == 2)
+ b = this
+ else
+ b = undefined
+
+ return a === b
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml b/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml
new file mode 100644
index 0000000000..423cd55ed4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml
@@ -0,0 +1,54 @@
+import QtQml
+
+QtObject {
+ component Variable: QtObject {
+ property int value: 4
+ }
+
+ component VariableShadow: Variable {
+ property string value: "1"
+ }
+
+ property Variable last: VariableShadow {}
+
+ function find(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value == i)
+ ++found
+ }
+ return found;
+ }
+
+ function findStrict(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value === i)
+ ++found
+ }
+ return found;
+ }
+
+ function findNot(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value != i)
+ ++found
+ }
+ return found;
+ }
+
+ function findNotStrict(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value !== i)
+ ++found
+ }
+ return found;
+ }
+
+ property int found: find(3)
+ property int foundStrict: findStrict(10)
+ property int foundNot: findNot(3)
+ property int foundNotStrict: findNotStrict(10)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml
new file mode 100644
index 0000000000..a80af89ddd
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ function a() { b() }
+ function b() { c() }
+ function c() { console.trace() }
+ Component.onCompleted: a()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml b/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml
new file mode 100644
index 0000000000..b2e7b40c00
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml
@@ -0,0 +1,32 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ // This does not look like dead code, but each access to 'result' generates a
+ // DeadTemoralZoneCheck instruction that we ignore when compiling to C++
+ // after checking statically that 'result' is alive throughout the function.
+ // Therefore, this function is a torture test for the dead code elimination.
+ function calc(a: int, b: int) : int {
+ let result = a;
+ if (b < 0) {
+ if (b < -1)
+ result -= b;
+ if (b < -2)
+ result /= b;
+ } else {
+ if (b > 1)
+ result *= b;
+ if (b > 2)
+ result += b;
+ }
+ return result;
+ }
+
+ property int a: calc(10, -3);
+ property int b: calc(10, -2);
+ property int c: calc(10, -1);
+ property int d: calc(10, 0);
+ property int e: calc(10, 1);
+ property int f: calc(10, 2);
+ property int g: calc(10, 3);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
index c3a9414ae2..d0d1fc9b8f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
@@ -94,6 +94,8 @@ Item {
}
function qtest_signalHandlerName(sn) {
+ // Warning: to not test for signal handlers like this in actual code.
+ // Use the helper methods in QQmlSignalNames instead.
if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase())
return sn
return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1)
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml b/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml
new file mode 100644
index 0000000000..f7c2cc4058
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml
@@ -0,0 +1,18 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: foo
+
+ property int offsetValue
+
+ function send(data : variant) {
+ }
+
+ Component.onCompleted: () => {
+ let deltaOffset = 42
+ deltaOffset -= 1
+ foo.offsetValue = deltaOffset
+ foo.send({offset: deltaOffset})
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h
new file mode 100644
index 0000000000..459dd62374
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H
+#define CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class Moo485 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(int uid READ uid CONSTANT FINAL)
+
+public:
+ explicit Moo485(QObject *parent = nullptr) : QObject(parent) { }
+ int uid() const { return 4711; }
+};
+
+class Foo485 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(quint16 uid MEMBER m_uid FINAL)
+
+public:
+ explicit Foo485(QObject *parent = nullptr) : QObject(parent) { }
+ quint16 m_uid = 0;
+};
+
+#endif // CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml
new file mode 100644
index 0000000000..6a15d6f775
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml
@@ -0,0 +1,13 @@
+import QtQuick
+import TestTypes
+
+
+Item {
+ id: root
+
+ property Moo485 moo
+
+ readonly property Foo485 foo: Foo485 {
+ uid: root.moo.uid ?? 0xFFFF
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml b/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml
new file mode 100644
index 0000000000..6eb14bba57
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property int i: 0
+ property Planner planner: null
+
+ function satisfy(mark: int) {
+ planner.addPropagate(mark);
+ i = +mark;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
index eecc3d968e..812415ae24 100644
--- a/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
+++ b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CPPBASECLASS_H
#define CPPBASECLASS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml b/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml
new file mode 100644
index 0000000000..fc8a34da71
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml
@@ -0,0 +1,20 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property date now: new Date()
+ property date now2: new Date(now)
+ property date fromString: new Date("1995-12-17T03:24:00")
+ property date fromNumber: new Date(777)
+ property date fromPrimitive: new Date(objectName.length === 0 ? 57 : "1997-02-13T13:04:12")
+ property date from2: new Date(1996, 1)
+ property date from3: new Date(1996, 2, 3)
+ property date from4: new Date(1996, 3, 4, 5)
+ property date from5: new Date(1996, 4, 5, 6, 7)
+ property date from6: new Date(1996, 5, 6, 7, 8, 9)
+ property date from7: new Date(1996, 6, 7, 8, 9, 10, 11)
+ property date from8: new Date(1996, 7, 8, 9, 10, 11, 12, 13)
+
+ property date withUnderflow: new Date(-4, -5, -6, -7, -8, -9, -10)
+ property date invalid: new Date("foo", "bar")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml b/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml
index 38a34f7487..5c0a426466 100644
--- a/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml
@@ -9,12 +9,17 @@ QtObject {
property string dateString: date
property string timeString: time
+ property real dateNumber: date
+ property real timeNumber: time
+
function shuffle() {
Druggeljug.myDate = date;
Druggeljug.myTime = time;
dateString = Druggeljug.myDate;
timeString = Druggeljug.myTime;
+ dateNumber = Druggeljug.myDate;
+ timeNumber = Druggeljug.myTime;
}
function fool() {
@@ -22,4 +27,9 @@ QtObject {
Druggeljug.myTime = Druggeljug.myDate;
Druggeljug.myDate = tmp;
}
+
+ function invalidate() {
+ date = new Date("foo", "bar");
+ time = new Date("bar", "foo");
+ }
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml b/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml
new file mode 100644
index 0000000000..b30e0124c8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml
@@ -0,0 +1,8 @@
+pragma Strict
+import QtQuick.Controls.Basic
+
+ApplicationWindow {
+ footer: DialogButtonBox {
+ standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h b/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h
new file mode 100644
index 0000000000..ace319f91f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef DUMMYOBJEKT_H
+#define DUMMYOBJEKT_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+#if QT_DEPRECATED_SINCE(6, 4)
+class DummyObjekt : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ enum Test {
+ TestA = 1,
+ TestB
+ };
+ Q_ENUM(Test)
+
+ // Deliberately not default constructible
+ explicit DummyObjekt(QObject *parent) : QObject(parent) {}
+};
+#endif
+
+#endif // DUMMYOBJEKT_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
index 64c2850bda..d8358b6a9c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
+++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
@@ -1,38 +1,48 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DYNAMICMETA_H
#define DYNAMICMETA_H
#include <private/qobject_p.h>
+#include <private/qmetaobjectbuilder_p.h>
#include <QtQmlIntegration/qqmlintegration.h>
-struct FreeDeleter {
- void operator()(QMetaObject *meta) { free(meta); }
-};
-
template<typename T>
class MetaObjectData : public QDynamicMetaObjectData
{
Q_DISABLE_COPY_MOVE(MetaObjectData)
public:
- MetaObjectData() = default;
- ~MetaObjectData() = default;
+ MetaObjectData()
+ {
+ QMetaObjectBuilder builder;
+ builder.setSuperClass(&T::staticMetaObject);
+ builder.setFlags(builder.flags() | DynamicMetaObject);
+ metaObject = builder.toMetaObject();
+ };
+
+ ~MetaObjectData() {
+ free(metaObject);
+ };
QMetaObject *toDynamicMetaObject(QObject *) override
{
- return const_cast<QMetaObject *>(&T::staticMetaObject);
+ return metaObject;
}
int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override
{
return o->qt_metacall(call, idx, argv);
}
+
+ QMetaObject *metaObject = nullptr;
};
class DynamicMeta : public QObject
{
Q_OBJECT
Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL)
+ Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL)
+ Q_PROPERTY(qreal shadowable READ shadowable CONSTANT)
QML_ELEMENT
public:
@@ -54,11 +64,26 @@ public:
Q_INVOKABLE int bar(int baz) { return baz + 12; }
+ qreal value() const { return m_value; }
+ qreal shadowable() const { return 25; }
+
+public slots:
+ void resetValue() { setValue(0); }
+ void setValue(qreal value)
+ {
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit valueChanged();
+ }
+
Q_SIGNALS:
void fooChanged();
+ void valueChanged();
private:
int m_foo = 0;
+ qreal m_value = 0;
};
class DynamicMetaSingleton : public DynamicMeta
diff --git a/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
new file mode 100644
index 0000000000..571a000199
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ id: mainItem
+
+ function arg(item: Binding) : QtObject { return item }
+ function ret(item: QtObject) : Binding { return item }
+
+ property QtObject a: arg(mainItem);
+ property QtObject b: ret(mainItem);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml b/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml
index fee2c50e15..61ddd2162d 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml
@@ -1,7 +1,12 @@
+pragma Strict
import TestTypes
MyType {
id: root
+
+ property alias status: root.a
+
property int test: myEnumType.type
property bool test_1: myEnumType.type
+ objectName: root.status + "m"
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml b/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml
new file mode 100644
index 0000000000..3176fde315
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml
@@ -0,0 +1,6 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ objectName: "Dummy: " + DummyObjekt.TestA
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml b/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml
new file mode 100644
index 0000000000..2ef37cbdf0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml
@@ -0,0 +1,6 @@
+import QML
+import TestTypes
+
+QtObject {
+ property int flagValue: ControlFlags.Both
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml b/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml
index f9a4eb144b..6a57b0e64a 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml
@@ -11,4 +11,9 @@ QtObject {
readonly property FooThing fighter: root.f.get(Foo.Fighter)
readonly property FooThing bar: root.f.get(Foo.Component)
}
+
+ property int a: FooFactory.B
+ property int b: f.t8
+ property int c: FooFactory.D
+ property int d: f.t16
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumProperty.h b/tests/auto/qml/qmlcppcodegen/data/enumProperty.h
index 8c13e860a3..8d6405a059 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumProperty.h
+++ b/tests/auto/qml/qmlcppcodegen/data/enumProperty.h
@@ -15,20 +15,87 @@ public:
Tri = 0x04,
};
Q_ENUM(MyEnum)
- Q_PROPERTY(MyEnum type READ type)
+ Q_PROPERTY(MyEnum type READ type CONSTANT)
MyEnum type() const { return MyEnum::Tri; }
};
class MyType : public QObject
{
Q_OBJECT
- Q_PROPERTY(MyEnumType myEnumType READ myEnumType)
+ Q_PROPERTY(MyEnumType myEnumType READ myEnumType CONSTANT)
+ Q_PROPERTY(A a READ a WRITE setA NOTIFY aChanged FINAL)
QML_ELEMENT
public:
+ enum A { B, C, D };
+ Q_ENUM(A)
+
MyEnumType myEnumType() const { return m_type; }
+ A a() const { return m_a; }
+ void setA(A newA)
+ {
+ if (m_a == newA)
+ return;
+ m_a = newA;
+ emit aChanged();
+ }
+
+ Q_INVOKABLE int method(quint16, const QString &) { return 24; }
+ Q_INVOKABLE int method(quint16, MyType::A a) { return int(a); }
+
+Q_SIGNALS:
+ void aChanged();
+
private:
MyEnumType m_type;
+ A m_a = B;
+};
+
+class CommunicationPermission
+{
+ Q_GADGET
+public:
+ enum CommunicationMode : quint8 {
+ Access = 0x01,
+ Advertise = 0x02,
+ Default = Access | Advertise,
+ };
+ Q_DECLARE_FLAGS(CommunicationModes, CommunicationMode)
+ Q_FLAG(CommunicationModes)
+
+ void setCommunicationModes(CommunicationModes modes) { m_modes = modes; }
+ CommunicationModes communicationModes() const { return m_modes; }
+
+private:
+ CommunicationModes m_modes;
+};
+
+struct QQmlCommunicationPermission : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(CommunicationPermission)
+ QML_EXTENDED_NAMESPACE(CommunicationPermission)
+ Q_PROPERTY(CommunicationPermission::CommunicationModes communicationModes READ communicationModes WRITE setCommunicationmodes NOTIFY communicationModesChanged)
+
+public:
+ CommunicationPermission::CommunicationModes communicationModes() const
+ {
+ return m_permission.communicationModes();
+ }
+
+ void setCommunicationmodes(const CommunicationPermission::CommunicationModes &newCommunicationModes)
+ {
+ if (communicationModes() == newCommunicationModes)
+ return;
+ m_permission.setCommunicationModes(newCommunicationModes);
+ emit communicationModesChanged();
+ }
+
+signals:
+ void communicationModesChanged();
+
+private:
+ CommunicationPermission m_permission;
};
#endif // ENUMPROPERTY_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumproblems.h b/tests/auto/qml/qmlcppcodegen/data/enumproblems.h
index 08a00acf7e..36f97bec5a 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumproblems.h
+++ b/tests/auto/qml/qmlcppcodegen/data/enumproblems.h
@@ -1,10 +1,11 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef ENUMPROBLEMS_H
#define ENUMPROBLEMS_H
#include <QObject>
+#include <QtCore/qflags.h>
#include <QtQml/qqml.h>
#include <QtQml/qqmlregistration.h>
@@ -45,9 +46,74 @@ class FooThingWrapper {
class FooFactory : public QObject {
Q_OBJECT
QML_ELEMENT
+ Q_PROPERTY(T8 t8 READ t8 CONSTANT FINAL)
+ Q_PROPERTY(T16 t16 READ t16 CONSTANT FINAL)
public:
+ enum T8: qint8 {
+ A, B, C
+ };
+ Q_ENUM(T8)
+
+ enum T16: qint16 {
+ D = 500, E, F
+ };
+ Q_ENUM(T16)
+
+ T8 t8() const { return C; }
+ T16 t16() const { return E; }
+
Q_INVOKABLE Foo* get(Foo::Type type) const { return new Foo(type); }
};
+class ControlFlags : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_UNCREATABLE("Flag Container Class")
+public:
+
+ enum Option {
+ ControlA = 0x1,
+ ControlB = 0x2,
+ Both = ControlA | ControlB
+ };
+
+ Q_DECLARE_FLAGS(Options, Option)
+ Q_FLAG(Option)
+};
+
+class ScopedEnum : public QObject {
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Data)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+
+public:
+ enum class DType {
+ A = 27, B
+ };
+ Q_ENUM(DType)
+
+ enum EType {
+ C = 7, D
+ };
+ Q_ENUM(EType)
+};
+
+class UnscopedEnum : public QObject {
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Data2)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "true")
+
+public:
+ enum class DType {
+ A = 26, B
+ };
+ Q_ENUM(DType)
+
+ enum EType {
+ C = 6, D
+ };
+ Q_ENUM(EType)
+};
+
#endif // ENUMPROBLEMS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml b/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml
new file mode 100644
index 0000000000..55ac68592c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQuick
+
+Item {
+ property url emptyUrl: ""
+ property url sourceUrl: "some/path/file.png"
+
+ property bool emptyUrlStrict: emptyUrl === Qt.resolvedUrl("")
+ property bool emptyUrlWeak: emptyUrl == Qt.resolvedUrl("")
+
+ property bool sourceUrlStrict: sourceUrl === Qt.url("some/path/file.png");
+ property bool sourceUrlWeak: sourceUrl == Qt.url("some/path/file.png");
+
+ property bool sourceIsNotEmptyStrict: sourceUrl !== emptyUrl
+ property bool sourceIsNotEmptyWeak: sourceUrl != emptyUrl
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml b/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml
new file mode 100644
index 0000000000..cd0c433ea9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml
@@ -0,0 +1,14 @@
+pragma Strict
+
+import QtQml
+import QtQuick
+
+Window {
+ property var foo
+ Component.onCompleted: {
+ console.log(foo !== null)
+ console.log(foo === null)
+ console.log(foo !== undefined)
+ console.log(foo === undefined)
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml b/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml
new file mode 100644
index 0000000000..13855356f2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject theNull: null
+
+ function doFail() : string { return theNull.objectName }
+ function delegateFail() : string { doFail() }
+ function disbelieveFail() : string { delegateFail() }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml b/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml
new file mode 100644
index 0000000000..e8f51984b8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+B {
+ r: ({x: 4, y: 6, width: 8, height: 10})
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml b/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml
new file mode 100644
index 0000000000..e8f51984b8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+B {
+ r: ({x: 4, y: 6, width: 8, height: 10})
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml
index f90fb44fe1..3b0e4908ab 100644
--- a/tests/auto/qml/qmlcppcodegen/data/failures.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml
@@ -9,6 +9,7 @@ QtObject {
property string attachedForNasty: Nasty.objectName
property Nasty nasty: Nasty {
+ id: theNasty
objectName: Component.objectName
}
@@ -30,18 +31,10 @@ QtObject {
Component.onCompleted: doesNotExist()
- property string aString: self + "a"
-
property BirthdayParty party: BirthdayParty {
onPartyStarted: (foozle) => { objectName = foozle }
}
- signal foo()
- signal bar()
-
- // Cannot assign potential undefined
- onFoo: objectName = self.bar()
-
property int enumFromGadget1: GadgetWithEnum.CONNECTED + 1
property int enumFromGadget2: TT2.GadgetWithEnum.CONNECTED + 1
@@ -62,4 +55,55 @@ QtObject {
let a;
return a;
}
+
+ function getText(myArr: list<string>): string {
+ myArr.shiftss()
+ }
+
+ function readTracks(metadataList : list<badType>): int {
+ return metadataList.length
+ }
+
+ function dtzFail() : int {
+ for (var a = 10; a < 20; ++a) {
+ switch (a) {
+ case 11:
+ let b = 5;
+ break;
+ case 10:
+ console.log(b);
+ break;
+ }
+ }
+ return a;
+ }
+
+ // TODO: Drop these once we can manipulate QVariant-wrapped lists.
+ property list<withLength> withLengths
+ property int l: withLengths.length
+ property withLength w: withLengths[10]
+
+ property unconstructibleWithLength uwl: 12 + 1
+
+ // Cannot generate code for getters
+ property rect r3: ({ get x() { return 42; }, y: 4 })
+
+ property int nonIterable: {
+ var result = 1;
+ for (var a in Component)
+ ++result;
+ return result;
+ }
+
+ property alias selfself: self
+ property alias nastyBad: theNasty.bad
+ function writeToUnknown() : int {
+ self.selfself.nastyBad = undefined;
+ return 5;
+ }
+
+ readonly property int someNumber: 10
+ function writeToReadonly() { someNumber = 20 }
+
+ property var silly: [,0]
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml
new file mode 100644
index 0000000000..44b55e245a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml
@@ -0,0 +1,23 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+DynamicMeta {
+ id: self
+ value: 999
+
+ property double notResettable: 10
+ property double notResettable2: { return undefined }
+
+ property DynamicMeta shadowing: DynamicMeta {
+ property var shadowable: undefined
+ }
+
+ function doReset() { self.value = undefined }
+ function doReset2() { self.value = shadowing.shadowable }
+ function doNotReset() { self.notResettable = undefined }
+
+ signal aaa()
+ signal bbb()
+ onAaa: objectName = self.bbb()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
index b8bd466717..6634982de2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml b/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml
new file mode 100644
index 0000000000..3ea5cf98db
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+CommunicationPermission {
+ communicationModes: CommunicationPermission.Access
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
index 839e026b77..3c81cd2e7f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
+++ b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GADGETWITHENUM_H
#define GADGETWITHENUM_H
@@ -26,4 +26,40 @@ namespace GadgetWithEnumWrapper {
QML_NAMED_ELEMENT(NamespaceWithEnum)
};
+struct Gadget
+{
+ Q_GADGET
+ QML_VALUE_TYPE(gadget)
+
+public:
+ enum class Prop1 { High, low, VeryHigh, VeryLow };
+ Q_ENUM(Prop1)
+
+ enum class Prop2 { VeryHigh, High, low, VeryLow };
+ Q_ENUM(Prop2)
+};
+
+class Backend : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ Q_PROPERTY(prop priority READ priority FINAL CONSTANT)
+ Q_PROPERTY(Gadget gadget READ gadget FINAL CONSTANT)
+ Q_CLASSINFO("RegisterEnumsFromRelatedTypes", "false")
+
+public:
+ enum prop { High, low, VeryHigh, VeryLow };
+ Q_ENUM(prop)
+
+ explicit Backend(QObject *parent = nullptr) : QObject(parent) {}
+
+ prop priority() const { return m_priority; }
+ Gadget gadget() const { return m_gadget; }
+
+private:
+ prop m_priority = low;
+ Gadget m_gadget;
+};
+
#endif // GADGETWITHENUM_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h
new file mode 100644
index 0000000000..e8a24cd707
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h
@@ -0,0 +1,42 @@
+#ifndef GETOPTIONALLOOKUP_H
+#define GETOPTIONALLOOKUP_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class GOL_Object : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(int i READ i CONSTANT FINAL)
+ Q_PROPERTY(QString s READ s CONSTANT FINAL)
+ Q_PROPERTY(GOL_Object *childA READ childA WRITE setChildA NOTIFY childAChanged FINAL)
+ Q_PROPERTY(Enum e READ e CONSTANT FINAL)
+
+public:
+ GOL_Object(QObject *parent = nullptr) : QObject(parent) { }
+
+ int i() const { return m_i; }
+ void setI(int i) { m_i = i; }
+
+ QString s() const { return m_s; }
+ void setS(QString s) { m_s = s; }
+
+ GOL_Object *childA() const { return m_childA; }
+ void setChildA(GOL_Object *a) { m_childA = a; }
+
+ enum Enum { V1, V2 };
+ Q_ENUM(Enum)
+ Enum e() const { return Enum::V2; }
+
+signals:
+ void childAChanged();
+
+private:
+ int m_i = 5;
+ QString m_s = "6";
+ GOL_Object *m_childA = nullptr;
+};
+
+#endif // GETOPTIONALLOOKUP_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml
new file mode 100644
index 0000000000..ee360d7142
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml
@@ -0,0 +1,34 @@
+pragma Strict
+pragma ValueTypeBehavior: Addressable
+
+import QtQuick
+
+GOL_Object {
+ id: root
+
+ property rect r: Qt.rect(0, 0, 20, 50)
+ property point p: Qt.point(0, -10)
+ property var v: Qt.point(5, 5)
+ property var u: undefined
+
+ property int to1: root?.i
+ property string to2: root?.s
+ property GOL_Object to3: root?.childA
+ property var to4: root.childA?.i
+ property var to5: (undefined as GOL_Object)?.childA
+ property int to6: (root as GOL_Object)?.s.length
+
+ property int tv1: root.r?.bottom
+ property int tv2: root.p?.y
+
+ property int te1: root?.e
+ property int te2: GOL_Object?.V2
+ property bool te3: root?.e === GOL_Object?.V1
+ property bool te4: root?.e === GOL_Object?.V2
+
+ property int tc1: root?.p.y
+ property int tc2: root.r?.x
+
+ property var tc4: root?.childA?.s
+ property var tc5: root.childA?.s
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
index 2090926872..6ca1f7f66b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
@@ -1,3 +1,4 @@
+pragma Strict
import QtQuick
Item {
@@ -11,5 +12,9 @@ Item {
Text {
id: ttt
+ onTextChanged: {
+ root.objectName = "dead"
+ ttt.objectName = "context"
+ }
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml b/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml
new file mode 100644
index 0000000000..640e2bc22a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml
@@ -0,0 +1,14 @@
+import QtQuick
+
+Item {
+ id: root
+
+ Component {
+ id: comp
+ Rectangle {
+ color: "blue"
+ }
+ }
+
+ Component.onCompleted: comp.createObject(root, {"width": 200, "height": 200})
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml b/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml
new file mode 100644
index 0000000000..de31527e5b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml
@@ -0,0 +1,39 @@
+import QtQml
+
+QtObject {
+ id: self
+ objectName: "self"
+
+ component Inner : QtObject {
+ property QtObject shadowable: QtObject {
+ objectName: "shadowable"
+ }
+ }
+
+ component Outer : QtObject {
+ property Inner inner: Inner {}
+ }
+
+ component Evil : Outer {
+ property string inner: "evil"
+ }
+
+ property Outer outer: Outer {}
+ property Outer evil: Evil {}
+
+ property QtObject notShadowable: QtObject {
+ objectName: "notShadowable"
+ }
+
+ function getInnerShadowable() {
+ notShadowable = outer.inner.shadowable;
+ }
+
+ function setInnerShadowable() {
+ outer.inner.shadowable = self;
+ }
+
+ function turnEvil() {
+ outer = evil;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml b/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml
new file mode 100644
index 0000000000..e255f4e8f4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import TestTypes
+
+MyType {
+ a: myEnumType.type === 4 ? 2 : 1
+ property int b: method("12", "hh")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/interactive.qml b/tests/auto/qml/qmlcppcodegen/data/interactive.qml
index e857df96e7..be5e5f0d40 100644
--- a/tests/auto/qml/qmlcppcodegen/data/interactive.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/interactive.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
pragma Strict
import QtQuick 2.9
diff --git a/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml b/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml
new file mode 100644
index 0000000000..7304b7a6b9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject offset: QtObject {
+ id: a
+ property string mark
+ }
+
+ function markInputs(mark: string) {
+ offset.objectName = mark;
+ a.mark = mark;
+ }
+
+ Component.onCompleted: markInputs("hello")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/invisible.h b/tests/auto/qml/qmlcppcodegen/data/invisible.h
index a385ee975f..4f4ebb87ad 100644
--- a/tests/auto/qml/qmlcppcodegen/data/invisible.h
+++ b/tests/auto/qml/qmlcppcodegen/data/invisible.h
@@ -1,11 +1,12 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INVISIBLE_H
#define INVISIBLE_H
#include <QtCore/qobject.h>
#include <QtQmlIntegration/qqmlintegration.h>
+#include <QtQml/qqmllist.h>
class Invisible : public QObject
{
@@ -45,6 +46,39 @@ class DerivedFromInvisible : public Invisible
{
Q_OBJECT
QML_ELEMENT
+ Q_PROPERTY(double implicitWidth MEMBER m_implicitWidth NOTIFY implicitWidthChanged FINAL)
+public:
+ DerivedFromInvisible(QObject *parent = nullptr) : Invisible(parent) {}
+
+signals:
+ void implicitWidthChanged();
+
+private:
+ double m_implicitWidth = 27;
+};
+
+class WithListPropertyOfDerivedFromInvisible : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QQmlListProperty<DerivedFromInvisible> children READ children NOTIFY childrenChanged FINAL)
+
+public:
+ WithListPropertyOfDerivedFromInvisible(QObject *parent = nullptr) : QObject(parent)
+ {
+ m_children.append(new DerivedFromInvisible(this));
+ }
+
+ QQmlListProperty<DerivedFromInvisible> children()
+ {
+ return QQmlListProperty<DerivedFromInvisible>(this, &m_children);
+ }
+
+signals:
+ void childrenChanged();
+
+private:
+ QList<DerivedFromInvisible *> m_children;
};
#endif // INVISIBLE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/iteration.qml b/tests/auto/qml/qmlcppcodegen/data/iteration.qml
new file mode 100644
index 0000000000..8632eefa1b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/iteration.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQml
+
+QtObject {
+ property list<int> ints: [3, 4, 5]
+ property list<QtObject> objects: [
+ QtObject { objectName: "a" },
+ QtObject { objectName: "b" },
+ QtObject { objectName: "c" }
+ ]
+
+ Component.onCompleted: {
+ for (var a in objects) {
+ objectName += objects[a].objectName;
+ for (var b in ints)
+ objectName += ints[b];
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml
new file mode 100644
index 0000000000..ff372bca45
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml
@@ -0,0 +1,28 @@
+pragma Strict
+import QML
+
+QtObject {
+ id: self
+
+ property QtObject l1: QtObject { objectName: "klaus" }
+ property QtObject l2: QtObject { function toString(): string { return "teil" } }
+ property QtObject l3: QtObject { }
+
+ function jsArray() : list<var> { return [l1, l2, l3, l1, l2, l3] }
+ property list<QtObject> listProperty: [l1, l2, l3, l1, l2, l3]
+
+ property string jsArrayToString: jsArray().toString()
+ property string listPropertyToString: listProperty.toString()
+
+ property bool listPropertyIncludes: listProperty.includes(l3)
+ property bool jsArrayIncludes: jsArray().includes(l3)
+
+ property string listPropertyJoin: listProperty.join()
+ property string jsArrayJoin: jsArray().join()
+
+ property int listPropertyIndexOf: listProperty.indexOf(l2)
+ property int jsArrayIndexOf: jsArray().indexOf(l2)
+
+ property int listPropertyLastIndexOf: listProperty.lastIndexOf(l3)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml
new file mode 100644
index 0000000000..7426c692fe
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml
@@ -0,0 +1,17 @@
+import QML
+
+QtObject {
+ id: self
+
+ property QtObject l1
+ property QtObject l2
+ property QtObject l3
+
+ function jsArray() { return [l1, l2, l3, l1, l2, l3] }
+
+ property string jsArrayToString: jsArray().toString()
+ property bool jsArrayIncludes: jsArray().includes(l3)
+ property string jsArrayJoin: jsArray().join()
+ property int jsArrayIndexOf: jsArray().indexOf(l2)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml
new file mode 100644
index 0000000000..293e7cbda5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml
@@ -0,0 +1,26 @@
+pragma Strict
+import QML
+
+QtObject {
+ id: self
+
+ required property int i
+ required property int j
+ required property int k
+
+ property QtObject l1: QtObject { objectName: "klaus" }
+ property QtObject l2: QtObject { function toString(): string { return "teil" } }
+ property QtObject l3: QtObject { }
+
+ function jsArray() : list<var> { return [l1, l2, l3, l1, l2, l3] }
+ property list<QtObject> listProperty: [l1, l2, l3, l1, l2, l3]
+
+ property list<QtObject> listPropertySlice: listProperty.slice(i, j)
+ property list<var> jsArraySlice: jsArray().slice(i, j)
+
+ property int listPropertyIndexOf: listProperty.indexOf(l2, i)
+ property int jsArrayIndexOf: jsArray().indexOf(l2, i)
+
+ property int listPropertyLastIndexOf: listProperty.lastIndexOf(l3, i)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3, i)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml
new file mode 100644
index 0000000000..9e928bd6f6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml
@@ -0,0 +1,18 @@
+import QML
+
+QtObject {
+ id: self
+
+ required property int i
+ required property int j
+ required property int k
+
+ property QtObject l1
+ property QtObject l2
+ property QtObject l3
+
+ function jsArray() { return [l1, l2, l3, l1, l2, l3] }
+ property var jsArraySlice: jsArray().slice(i, j)
+ property int jsArrayIndexOf: jsArray().indexOf(l2, i)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3, i)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listConversion.qml b/tests/auto/qml/qmlcppcodegen/data/listConversion.qml
new file mode 100644
index 0000000000..ca86d9a1d6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listConversion.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+BirthdayParty {
+ id: self
+
+ guests: [
+ Person { name: "Horst 1" },
+ Person { name: "Horst 2" },
+ Person { name: "Horst 3" }
+ ]
+
+ property list<QtObject> o: self.guests
+ property list<string> s: self.guestNames
+ property list<var> v: self.stuffs
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml b/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml
new file mode 100644
index 0000000000..f3698d78ab
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+WithListPropertyOfDerivedFromInvisible {
+ property real width: children[0].implicitWidth
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listToString.qml b/tests/auto/qml/qmlcppcodegen/data/listToString.qml
new file mode 100644
index 0000000000..e9e4b85956
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listToString.qml
@@ -0,0 +1,25 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<string> stringList: ["one", "two"]
+ property list<int> intList: [1, 2]
+ property list<QtObject> objectList: [this, this]
+
+ Component.onCompleted: {
+ console.log(stringList)
+ console.log(stringList + "")
+
+ console.log(intList)
+ console.log(intList + "")
+
+ console.log(objectList)
+ console.log(objectList + "")
+
+ console.log(["a", "b"]);
+
+ // TODO: Cannot do this, yet, because we cannot coerce a list to string on the fly.
+ // We need to store it as list first.
+ // console.log(["a", "b"] + "");
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listprovider.h b/tests/auto/qml/qmlcppcodegen/data/listprovider.h
new file mode 100644
index 0000000000..076944b586
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listprovider.h
@@ -0,0 +1,24 @@
+#ifndef QLISTPROVIDER_H
+#define QLISTPROVIDER_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class QListProvider : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(ListProvider)
+
+public:
+ explicit QListProvider(QObject *parent = nullptr) : QObject(parent) { }
+
+ Q_INVOKABLE QList<int> intList() const
+ {
+ QList<int> list;
+ for (int i = 0; i < 3; ++i)
+ list.append(i);
+ return list;
+ }
+};
+
+#endif // QLISTPROVIDER_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/math.qml b/tests/auto/qml/qmlcppcodegen/data/math.qml
index cc6cd3741a..ad6303e682 100644
--- a/tests/auto/qml/qmlcppcodegen/data/math.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/math.qml
@@ -3,4 +3,5 @@ import QML
QtObject {
property int a: Math.max(5, 7, 9, -111)
property var b: 50 / 22
+ property real c: Math.PI * 2
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml b/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml
new file mode 100644
index 0000000000..654b699918
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml
@@ -0,0 +1,59 @@
+pragma Strict
+import QtQml
+import QtQuick
+
+Rectangle {
+ Component.onCompleted: {
+ // Math.max()
+ console.log(Math.max(1, 1));
+ console.log(Math.max(1, 2));
+ console.log(Math.max(2, 1));
+ console.log(Math.max(0, 0));
+ console.log(Math.max(-1, 0));
+ console.log(Math.max(0, -1));
+ console.log(Math.max(-1, -1));
+
+ console.log(Math.max(0, 0, 0));
+ console.log(Math.max(0, 0, 1, 0, 0, 0));
+ console.log(Math.max(-2, -1, 0, 1, 2));
+ console.log(Math.max(2, 1, 0, -1, -2));
+ console.log(Math.max(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ console.log(Math.max(0.0, 0.0, 0.0))
+ console.log(Math.max(-0.001, 0.001, 0.002))
+ console.log(Math.max(5.4, 1, 0.002))
+ console.log(Math.max(null, 0, -1, 8E-2, NaN, undefined, true, false, Infinity))
+ console.log(Math.max(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.max(0, -1, 8E-2, true, false))
+ console.log(Math.max(0, -1, 8E-2, false))
+ console.log(Math.max(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.max(-1, -8, null))
+ console.log(Math.max(undefined, 20, 70))
+
+ // Math.min()
+ console.log(Math.min(1, 1));
+ console.log(Math.min(1, +2));
+ console.log(Math.min(2, 1));
+ console.log(Math.min(0, 0));
+ console.log(Math.min(-1, 0));
+ console.log(Math.min(0, -1));
+ console.log(Math.min(-1, -1));
+
+ console.log(Math.min(0, 0, 0));
+ console.log(Math.min(0, 0, 1, 0, 0, 0));
+ console.log(Math.min(-2, -1, 0, 1, 2));
+ console.log(Math.min(2, 1, 0, -1, -2));
+ console.log(Math.min(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ console.log(Math.min(0.0, 0.0, 0.0))
+ console.log(Math.min(-0.001, 0.001, 0.002))
+ console.log(Math.min(5.4, 1, 0.002))
+ console.log(Math.min(null, 0, -1, 8E-2, NaN, undefined, true, false, Infinity))
+ console.log(Math.min(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.min(0, -1, 8E-2, true, false))
+ console.log(Math.min(0, -1, 8E-2, false))
+ console.log(Math.min(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.min(-1, -8, null))
+ console.log(Math.min(undefined, 20, 70))
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml
new file mode 100644
index 0000000000..fad74a28bd
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+
+import QML
+
+QtObject {
+ property double e: Math.E
+ property double ln10: Math.LN10
+ property double ln2: Math.LN2
+ property double log10e: Math.LOG10E
+ property double log2e: Math.LOG2E
+ property double pi: Math.PI
+ property double sqrt1_2: Math.SQRT1_2
+ property double sqrt2: Math.SQRT2
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml b/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml
new file mode 100644
index 0000000000..161e21e643
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml
@@ -0,0 +1,14 @@
+pragma Strict
+import QtQuick
+
+Item {
+ objectName: "a"
+
+ function f(arg: Item) : string {
+ // Read arg as QtObject and Item, merged into QtObject.
+ console.log(arg)
+ return arg.x
+ }
+
+ Component.onCompleted: objectName = f(null)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml b/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml
new file mode 100644
index 0000000000..5f4bb4ff87
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQuick
+
+Item {
+ objectName: "a"
+
+ function f(arg: Item) : string {
+ // Write arg as Item, read it as QtObject.
+ arg.x = 5
+ console.log(arg)
+ return arg.objectName
+ }
+
+ Component.onCompleted: objectName = f(null)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml b/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml
new file mode 100644
index 0000000000..b6b7179438
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+QtObject {
+ objectName: people[0].getName()
+ property list<Person> people: [
+ Person {
+ name: "no one"
+ }
+ ]
+
+ function boom() : string {
+ return people[1].getName()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/methods.qml b/tests/auto/qml/qmlcppcodegen/data/methods.qml
index 3abd14c9c1..c045c2249b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/methods.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/methods.qml
@@ -35,6 +35,8 @@ BirthdayParty {
}
function stuff(sn) {
+ // Warning: to not test for signal handlers like this in actual code.
+ // Use the helper methods in QQmlSignalNames instead.
if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase())
return sn
return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1)
diff --git a/tests/auto/qml/qmlcppcodegen/data/multiforeign.h b/tests/auto/qml/qmlcppcodegen/data/multiforeign.h
index 290b6370f5..6c46d5ad86 100644
--- a/tests/auto/qml/qmlcppcodegen/data/multiforeign.h
+++ b/tests/auto/qml/qmlcppcodegen/data/multiforeign.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MULTIFOREIGN_H
#define MULTIFOREIGN_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml b/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml
new file mode 100644
index 0000000000..61dfdb7ca5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml
@@ -0,0 +1,13 @@
+pragma Strict
+
+import TestTypes
+import QtQml
+
+QtObject {
+ property rect r: Qt.rect(1, 2, 3, 4)
+ property point p: Qt.point(5, 6);
+
+ property withLength wr: r
+ property withLength wp: p
+ property withLength wi: 17
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml b/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml
new file mode 100644
index 0000000000..8fe47b7296
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml
@@ -0,0 +1,33 @@
+import QtQuick
+
+Item {
+ id: root
+ visible: true
+
+ property var speaker
+ signal say_hello()
+
+ Component{
+ id: speakerComp
+ Text {
+ text: "HELLO"
+ function say_hello() {
+ console.log(text)
+ }
+ }
+ }
+
+ Timer {
+ interval: 1; running: true; repeat: false
+ onTriggered: root.say_hello();
+ }
+
+ Component.onCompleted:
+ {
+ root.speaker = speakerComp.createObject(root);
+
+ root.say_hello.connect(root.speaker.say_hello);
+
+ root.speaker.destroy();
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml
index 1f9af7169b..53b3697c9b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml
@@ -6,6 +6,7 @@ QtObject {
property int w: 1
property int x: 1
property int y: 1
+ property int z: 1
Component.onCompleted: {
var g = null;
if (g !== null) {
@@ -22,5 +23,15 @@ QtObject {
if (h === undefined) {
y = 5;
}
+
+ var o = this;
+ if (o != null)
+ z += 7;
+ if (o == null)
+ z += 6;
+ if (g == null)
+ z += 10;
+ if (g != null)
+ z += 20;
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml b/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml
new file mode 100644
index 0000000000..f84f93c5d2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml
@@ -0,0 +1,37 @@
+pragma Strict
+pragma ValueTypeBehavior: Addressable
+
+import QtQuick
+
+GOL_Object {
+ id: root
+
+ property int p1: 5 ?? -1
+ property string p2: "6" ?? "-1"
+
+ property var p3: undefined ?? undefined
+ property var p4: undefined ?? null
+ property var p5: undefined ?? -1
+ property var p6: undefined ?? "-1"
+
+ property var p7: null ?? undefined
+ property var p8: null ?? null
+ property var p9: null ?? -1
+ property var p10: null ?? "-1"
+
+ property int p11: GOL_Object.V2 ?? "-1"
+
+ property int p12: 1 ?? 2 ?? 3
+ property int p13: "1" ?? "2" ?? "3"
+ property var p14: undefined ?? "2" ?? undefined
+ property var p15: undefined ?? undefined ?? 1
+
+ property var p16
+ property var p17
+
+ Component.onCompleted: {
+ p16 = (root.childA as GOL_Object)?.i ?? -1
+ root.childA = root
+ p17 = (root.childA as GOL_Object)?.i ?? -1
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml
index f12d3f5ea2..ec848429e8 100644
--- a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml
@@ -4,23 +4,57 @@ import TestTypes
QtObject {
function writeValues() {
+ Druggeljug.myInt8 = 35
+ Druggeljug.myUint8 = 36
+ Druggeljug.myInt16 = 37
+ Druggeljug.myUint16 = 38
Druggeljug.myInt = 39
Druggeljug.myUint = 40
Druggeljug.myInt32 = 41
Druggeljug.myUint32 = 42
}
+ function negateValues() {
+ Druggeljug.myInt8 = -Druggeljug.myInt8;
+ Druggeljug.myUint8 = -Druggeljug.myUint8;
+ Druggeljug.myInt16 = -Druggeljug.myInt16;
+ Druggeljug.myUint16 = -Druggeljug.myUint16;
+ Druggeljug.myInt = -Druggeljug.myInt;
+ Druggeljug.myUint = -Druggeljug.myUint;
+ Druggeljug.myInt32 = -Druggeljug.myInt32;
+ Druggeljug.myUint32 = -Druggeljug.myUint32;
+ }
+
+ function shuffleValues() {
+ Druggeljug.myInt8 = Druggeljug.myUint8;
+ Druggeljug.myUint8 = Druggeljug.myInt16;
+ Druggeljug.myInt16 = Druggeljug.myUint16;
+ Druggeljug.myUint16 = Druggeljug.myInt;
+ Druggeljug.myInt = Druggeljug.myUint;
+ Druggeljug.myUint = Druggeljug.myInt32;
+ Druggeljug.myInt32 = Druggeljug.myUint32;
+ Druggeljug.myUint32 = Druggeljug.myInt8;
+ }
+
function readValueAsString(i: int) : string {
switch (i) {
- case 0: return Druggeljug.myInt;
- case 1: return Druggeljug.myUint;
- case 2: return Druggeljug.myInt32;
- case 3: return Druggeljug.myUint32;
+ case 0: return Druggeljug.myInt8;
+ case 1: return Druggeljug.myUint8;
+ case 2: return Druggeljug.myInt16;
+ case 3: return Druggeljug.myUint16;
+ case 4: return Druggeljug.myInt;
+ case 5: return Druggeljug.myUint;
+ case 6: return Druggeljug.myInt32;
+ case 7: return Druggeljug.myUint32;
default: return "";
}
}
function storeValues() {
+ Druggeljug.storeMyInt8(1330)
+ Druggeljug.storeMyUint8(1331)
+ Druggeljug.storeMyInt16(1332)
+ Druggeljug.storeMyUint16(1333)
Druggeljug.storeMyInt(1334)
Druggeljug.storeMyUint(1335)
Druggeljug.storeMyInt32(1336)
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml b/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml
new file mode 100644
index 0000000000..4804921b02
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml
@@ -0,0 +1,34 @@
+pragma Strict
+import QtQuick
+
+Item {
+ id: stack
+
+ property int current: 0
+
+ onCurrentChanged: setZOrders()
+ Component.onCompleted: setZOrders()
+
+ function setZOrders() {
+ for (var i = 0; i < Math.max(stack.children.length, 3); ++i) {
+ stack.children[i].z = (i == current ? 1 : 0)
+ stack.children[i].enabled = (i == current)
+ }
+ }
+
+ function zOrders() : list<int> {
+ return [
+ stack.children[0].z,
+ stack.children[1].z,
+ stack.children[2].z
+ ]
+ }
+
+ function clearChildren() {
+ children.length = 0;
+ }
+
+ Item {}
+ Item {}
+ Item {}
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml b/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml
new file mode 100644
index 0000000000..14f84c57d0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ readonly property ObjectWithStringListMethod foo: ObjectFactory.getFoo()
+ Component.onCompleted: console.log(foo ? foo.names().length : "-")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
index 348862985f..f43a0d5531 100644
--- a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
+++ b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef OBJECTWITHMETOD_H
#define OBJECTWITHMETOD_H
@@ -7,6 +7,7 @@
#include <QtCore/qobject.h>
#include <QtCore/qproperty.h>
#include <QtQml/qqml.h>
+#include <QtQml/private/qv4engine_p.h>
// Make objectName available. It doesn't exist on the builtin QtObject type
struct QObjectForeignForObjectName {
@@ -27,6 +28,20 @@ public:
Q_INVOKABLE int doThing() const { return theThing; }
QProperty<int> theThing;
QBindable<int> theThingBindable() { return QBindable<int>(&theThing); }
+
+ // The meta methods are populated back to front.
+ // The V4Function flag should not bleed into the others in either case.
+
+ Q_INVOKABLE void overloaded(QQmlV4FunctionPtr) { setObjectName(QStringLiteral("javaScript")); }
+ Q_INVOKABLE void overloaded(double) { setObjectName(QStringLiteral("double")); }
+ Q_INVOKABLE void overloaded(const QString &) { setObjectName(QStringLiteral("string")); }
+
+ Q_INVOKABLE void foo(const QString &bla) { setObjectName(bla); }
+ Q_INVOKABLE void foo(ObjectWithMethod *) { setObjectName(QStringLiteral("ObjectWithMethod")); }
+
+ Q_INVOKABLE void overloaded2(double) { setObjectName(QStringLiteral("double")); }
+ Q_INVOKABLE void overloaded2(const QString &) { setObjectName(QStringLiteral("string")); }
+ Q_INVOKABLE void overloaded2(QQmlV4FunctionPtr) { setObjectName(QStringLiteral("javaScript")); }
};
class OverriddenObjectName : public ObjectWithMethod
@@ -56,4 +71,40 @@ private:
QProperty<QString> m_objectName;
};
+class ObjectWithStringListMethod : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ explicit ObjectWithStringListMethod(QObject *parent = nullptr) : QObject(parent)
+ {
+ m_names.append("One");
+ m_names.append("Two");
+ }
+
+ Q_INVOKABLE QStringList names() const { return m_names; }
+
+private:
+ QStringList m_names;
+};
+
+class ObjectFactory : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ explicit ObjectFactory(QObject *parent = nullptr) : QObject(parent) {}
+ Q_INVOKABLE ObjectWithStringListMethod *getFoo()
+ {
+ if (!m_foo)
+ m_foo = new ObjectWithStringListMethod(this);
+ return m_foo;
+ }
+
+private:
+ ObjectWithStringListMethod *m_foo = nullptr;
+};
+
#endif // OBJECTWITHMETHOD_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml b/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml
new file mode 100644
index 0000000000..4dbc541721
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml
@@ -0,0 +1,77 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<QtObject> elms: [this, null, null]
+
+ property int found: find(this)
+ property int foundNot: findNot(this)
+ property int foundStrict: findStrict(this)
+ property int foundStrictNot: findStrictNot(this)
+
+ function find(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value == elm)
+ ++found;
+ }
+ return found;
+ }
+
+ function findNot(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value != elm)
+ ++found;
+ }
+ return found;
+ }
+
+ function findStrict(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value === elm)
+ ++found;
+ }
+ return found;
+ }
+
+ function findStrictNot(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value !== elm)
+ ++found;
+ }
+ return found;
+ }
+
+ property bool optionalNull: {
+ let a // Produces a QJsPrimitiveValue we can compare to null below
+ if (objectName.length === 0)
+ a = null
+ else
+ a = undefined
+
+ return a === null
+ }
+
+ property int undefinedEqualsUndefined: {
+ var matches = 0;
+
+ // Overrun the array so that we get some undefined !== undefined.
+ for (var i = 0; i < 4; i++) {
+ var val1 = elms[i]
+ for (var j = 0; j < 4; j++) {
+ var val2 = elms[j]
+ if (!(val1 !== val2))
+ ++matches
+ }
+ }
+
+ return matches;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/person.cpp b/tests/auto/qml/qmlcppcodegen/data/person.cpp
index 4dcd6fd56f..946dfbcdaa 100644
--- a/tests/auto/qml/qmlcppcodegen/data/person.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/person.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "person.h"
@@ -98,3 +98,21 @@ void Person::setCousins(const QList<Person *> &newCousins)
m_cousins = newCousins;
emit cousinsChanged();
}
+
+QRectF Person::area() const
+{
+ return m_area;
+}
+
+void Person::setArea(const QRectF &newArea)
+{
+ if (m_area.valueBypassingBindings() == newArea)
+ return;
+ m_area = newArea;
+ emit areaChanged();
+}
+
+QBindable<QRectF> Person::areaBindable()
+{
+ return QBindable<QRectF>(&m_area);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/person.h b/tests/auto/qml/qmlcppcodegen/data/person.h
index fba4a9e9a5..c46aa757a7 100644
--- a/tests/auto/qml/qmlcppcodegen/data/person.h
+++ b/tests/auto/qml/qmlcppcodegen/data/person.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PERSON_H
#define PERSON_H
@@ -8,6 +8,35 @@
#include <QtQml/qqml.h>
#include <QtQml/qqmlengine.h>
#include <QtCore/qproperty.h>
+#include <QtCore/qrect.h>
+
+struct Inner
+{
+ Q_GADGET
+ QML_VALUE_TYPE(inner)
+ QML_STRUCTURED_VALUE
+ Q_PROPERTY(int i MEMBER i)
+
+private:
+ friend bool operator==(const Inner &lhs, const Inner &rhs) { return lhs.i == rhs.i; }
+ friend bool operator!=(const Inner &lhs, const Inner &rhs) { return !(lhs == rhs); }
+
+ int i = 11;
+};
+
+struct Outer
+{
+ Q_GADGET
+ QML_VALUE_TYPE(outer)
+ QML_STRUCTURED_VALUE
+ Q_PROPERTY(Inner inner MEMBER inner)
+
+private:
+ friend bool operator==(const Outer &lhs, const Outer &rhs) { return lhs.inner == rhs.inner; }
+ friend bool operator!=(const Outer &lhs, const Outer &rhs) { return !(lhs == rhs); }
+
+ Inner inner;
+};
// Intentionally opaque type
class Barzle : public QObject {};
@@ -22,6 +51,8 @@ class Person : public QObject
Q_PROPERTY(QList<Barzle *> barzles READ barzles WRITE setBarzles NOTIFY barzlesChanged FINAL)
Q_PROPERTY(QList<Person *> cousins READ cousins WRITE setCousins NOTIFY cousinsChanged FINAL)
Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged FINAL)
+ Q_PROPERTY(QRectF area READ area WRITE setArea NOTIFY areaChanged) // not FINAL
+ Q_PROPERTY(QRectF area2 READ area WRITE setArea NOTIFY areaChanged BINDABLE areaBindable FINAL)
QML_ELEMENT
public:
Person(QObject *parent = nullptr);
@@ -52,6 +83,12 @@ public:
QList<Person *> cousins() const;
void setCousins(const QList<Person *> &newCousins);
+ QRectF area() const;
+ void setArea(const QRectF &newArea);
+ QBindable<QRectF> areaBindable();
+
+ Q_INVOKABLE QString getName() const { return m_name; }
+
signals:
void nameChanged();
void shoeSizeChanged();
@@ -62,6 +99,10 @@ signals:
void ambiguous(int a = 9);
void cousinsChanged();
+ void objectListHappened(const QList<QObject *> &);
+ void variantListHappened(const QList<QVariant> &);
+
+ void areaChanged();
private:
QString m_name;
@@ -70,6 +111,7 @@ private:
QList<Barzle *> m_barzles;
QList<Person *> m_cousins;
QProperty<QByteArray> m_data;
+ QProperty<QRectF> m_area;
};
class BarzleListRegistration
diff --git a/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml
new file mode 100644
index 0000000000..c7103eaf05
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+
+import QtQuick
+
+Window {
+ // If static properties of the Math global object are not directly
+ // supported, a warning should be issued in turn failing the build
+ // due to `pragma Strict`.
+ width: 200 * Math.PI
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml b/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml
new file mode 100644
index 0000000000..d0176e6b15
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml
@@ -0,0 +1,16 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ id: root
+
+ property int priority: Backend.gadget.VeryHigh
+ property int prop2: Backend.priority
+
+ property bool priorityIsVeryHigh: root.priority == Backend.VeryHigh
+
+ function cyclePriority() : int {
+ root.priority = Backend.gadget.High;
+ return root.priority;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml b/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml
new file mode 100644
index 0000000000..149638283b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml
@@ -0,0 +1,17 @@
+import QtQml
+
+QtObject {
+ id: testObj
+
+ // "readonly" means the identity of the list cannot be changed.
+ // Its contents can be changed.
+ readonly default property list<QtObject> theList
+
+ Component.onCompleted: {
+ for (var i = 0; i < 4; i++)
+ testObj.theList.push(testObj)
+ }
+
+ property int l: theList.length
+}
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml
new file mode 100644
index 0000000000..c6fda8c739
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml
@@ -0,0 +1,18 @@
+import QtQml
+
+QtObject {
+ id: mainItem
+ property int topPadding: 12
+ property int bottomPadding: 12
+
+ property int preferredHeight: mainItem.children.reduce(maximumImplicitHeightReducer, 0) + topPadding + bottomPadding
+ function maximumImplicitHeightReducer(accumulator: real, item: Binding): real {
+ return Math.max(accumulator, (item.objectName + "b").length);
+ }
+
+ property int preferredHeight2: mainItem.children.reduce((accumulator, item) => {
+ return Math.max(accumulator, (item.objectName + "b").length);
+ }, 0) + topPadding + bottomPadding
+
+ property list<Binding> children: [ Binding { objectName: "aaa" } ]
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml
new file mode 100644
index 0000000000..9352163ba7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml
@@ -0,0 +1,19 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: last
+ property int value: 10
+
+ function verify(i: int) {
+ if (last.value !== i)
+ console.error("failed", last.value, i);
+ else
+ console.log("success")
+ }
+
+ Component.onCompleted: {
+ verify(10)
+ verify(11)
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.h b/tests/auto/qml/qmlcppcodegen/data/resettable.h
new file mode 100644
index 0000000000..755c8de237
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/resettable.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef RESETTABLE_H
+#define RESETTABLE_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class ResettableProperty : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Resettable)
+ Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL)
+ Q_PROPERTY(qreal shadowable READ shadowable CONSTANT)
+
+public:
+ explicit ResettableProperty(QObject *parent = nullptr) : QObject(parent) {}
+ qreal value() const { return m_value; }
+ qreal shadowable() const { return 25; }
+
+public slots:
+ void resetValue() { setValue(0); }
+ void setValue(qreal value)
+ {
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit valueChanged();
+ }
+
+signals:
+ void valueChanged();
+
+private:
+ qreal m_value = 0;
+};
+
+#endif // RESETTABLE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.qml b/tests/auto/qml/qmlcppcodegen/data/resettable.qml
new file mode 100644
index 0000000000..561655032d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/resettable.qml
@@ -0,0 +1,23 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+Resettable {
+ id: self
+ value: 999
+
+ property double notResettable: 10
+ property double notResettable2: { return undefined }
+
+ property Resettable shadowing: Resettable {
+ property var shadowable: undefined
+ }
+
+ function doReset() { self.value = undefined }
+ function doReset2() { self.value = shadowing.shadowable }
+ function doNotReset() { self.notResettable = undefined }
+
+ signal aaa()
+ signal bbb()
+ onAaa: objectName = self.bbb()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml b/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml
new file mode 100644
index 0000000000..e0b85eb270
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml
@@ -0,0 +1,15 @@
+import QtQml
+
+QtObject {
+ id: remaining
+
+ property int bar: 0
+
+ Component.onCompleted: {
+ let remainingTime = 123
+ if (remainingTime < 0) {
+ remainingTime += 24 * 60 * 60
+ }
+ remaining.bar = isNaN(remainingTime) ? 0 : remainingTime
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml
new file mode 100644
index 0000000000..e23f180598
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml
@@ -0,0 +1,20 @@
+pragma ComponentBehavior: Bound
+
+import QtQml
+
+QtObject {
+ id: root
+
+ property QtObject b: QtObject {
+ id: bar
+ objectName: "outer"
+ }
+
+ property Instantiator i: Instantiator {
+ model: 1
+ delegate: QtObject {
+ property QtObject bar: QtObject { objectName: "inner" }
+ Component.onCompleted: root.objectName = bar.objectName
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml b/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml
new file mode 100644
index 0000000000..8b9f161b06
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml
@@ -0,0 +1,19 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ property int good: Data.DType.A
+ property int bad: Data.A
+
+ property int wrong: Data.EType.C
+ property int right: Data.C
+
+ property int notgood: Data2.DType.A
+ property int notbad: Data2.A
+
+ property int notwrong: Data2.EType.C
+ property int notright: Data2.C
+
+ property int passable: Enums.AppState.Blue
+ property int wild: Enums.Green
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h
new file mode 100644
index 0000000000..76c72fff36
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SEQUENCETOITERABLE_H
+#define SEQUENCETOITERABLE_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class Entry : public QObject {
+ Q_OBJECT
+
+public:
+ explicit Entry(const QString &name, QObject *parent = nullptr)
+ : QObject(parent), m_name(name)
+ {
+ setObjectName(name);
+ }
+
+private:
+ QString m_name;
+};
+
+class EntryWrapper {
+ Q_GADGET
+ QML_FOREIGN(Entry)
+ QML_NAMED_ELEMENT(Entry)
+ QML_UNCREATABLE("These are my Entry objects")
+};
+
+class EntryListRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(QList<Entry*>)
+ QML_ANONYMOUS
+ QML_SEQUENTIAL_CONTAINER(Entry*)
+};
+
+class EntrySource : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ explicit EntrySource(QObject* parent = nullptr) : QObject(parent) {
+ for (int i = 0; i < 10; i++) {
+ m_entries.push_back(new Entry(QString("Item %1").arg(i), this));
+ }
+ }
+ Q_INVOKABLE QList<Entry*> getEntries() const { return m_entries; }
+
+private:
+ QList<Entry*> m_entries;
+};
+
+#endif // SEQUENCETOITERABLE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml
new file mode 100644
index 0000000000..23e2645128
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml
@@ -0,0 +1,20 @@
+pragma Strict
+import QtQuick
+import TestTypes
+
+Item {
+ Component.onCompleted: () => {
+ repeater.model = EntrySource.getEntries()
+ }
+
+ Repeater {
+ id: repeater
+ Item {
+ required property int index
+ required property QtObject modelData
+ objectName: modelData + ": " + index
+ }
+ }
+
+ property int c: children.length
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml b/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml
new file mode 100644
index 0000000000..404ee8653b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: first
+ property int value
+
+ objectName: a.objectName
+ property QtObject a: QtObject {}
+ function t() { a.objectName = "a" }
+
+ Component.onCompleted: {
+ for (let i = 0; i < 10; ++i) {
+ first.value = i;
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml b/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml
new file mode 100644
index 0000000000..9975bfdfa4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property Component newEditConstraint: EditConstraint {}
+ property Variable variable: Variable {}
+ property EditConstraint edit
+
+ function trigger() {
+ change(variable, 55);
+ }
+
+ function change(v: Variable, newValue: int) {
+ edit = newEditConstraint.createObject(null, {myOutput: v}) as EditConstraint;
+ v.value = newValue;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml
new file mode 100644
index 0000000000..ccb50f4934
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml
@@ -0,0 +1,31 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property ShadowedObjectName shadowed1: ShadowedObjectName {}
+ property ShadowedObjectName shadowed2: ShadowedObjectName {}
+ property QtObject shadowed3: ShadowedObjectNameDerived {}
+
+ function returnShadowed2() : QtObject { return shadowed2 }
+
+ function a(mark: int) {
+ // as-cast can be optimized out if we're clever.
+ (shadowed1 as QtObject).objectName = mark;
+ }
+
+ function b(mark: int) {
+ // method return values can contain shadowed properties!
+ returnShadowed2().objectName = mark;
+ }
+
+ function c(mark: int) {
+ // Has to do an actual as-cast, but results in ShadowedObjectNameDerived!
+ (shadowed3 as ShadowedObjectName).objectName = mark;
+ }
+
+ Component.onCompleted: {
+ a(43);
+ b(42);
+ c(41);
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml
new file mode 100644
index 0000000000..590fb40b17
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml
@@ -0,0 +1,35 @@
+pragma Strict
+import QtQuick
+
+Item {
+ component B: Item {
+ function contains(point: point) : string {
+ return "b"
+ }
+ }
+
+
+ component C: Item {
+ function contains(point: point) : string {
+ return "c"
+ }
+ }
+
+ property Item a: Item {}
+ property B b: B {}
+ property C c: C {}
+
+ function doThing() : var { return a.contains(Qt.point(0, 0)) }
+
+ property var athing;
+ property var bthing;
+ property var cthing;
+
+ Component.onCompleted: {
+ athing = doThing();
+ a = b;
+ bthing = doThing();
+ a = c;
+ cthing = doThing();
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml
new file mode 100644
index 0000000000..0e130b9afc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml
@@ -0,0 +1,16 @@
+import QtQuick
+
+QtObject {
+ id: win
+
+ component Foo: QtObject {
+ property int progress: 0
+ }
+
+ property int progress: 0
+ readonly property Foo configuring: Foo {}
+
+ Component.onCompleted: {
+ win.configuring.progress = win?.progress ?? 0
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
index 58a567f4db..3a0349966d 100644
--- a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml b/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml
new file mode 100644
index 0000000000..9a07b206d4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml
@@ -0,0 +1,18 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+Person {
+ property list<var> varlist: [1, "foo", this, undefined, true]
+ property list<QtObject> objlist: [this, null, this]
+
+ function sendSignals() {
+ variantListHappened(varlist);
+ objectListHappened(objlist);
+ }
+
+ property int happening: 0
+
+ onObjectListHappened: (objects) => { happening += objects.length }
+ onVariantListHappened: (variants) => { happening += variants.length }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/state.h b/tests/auto/qml/qmlcppcodegen/data/state.h
index 1afcebc4ea..708e681781 100644
--- a/tests/auto/qml/qmlcppcodegen/data/state.h
+++ b/tests/auto/qml/qmlcppcodegen/data/state.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef STATE_H
#define STATE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml b/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml
new file mode 100644
index 0000000000..158dff2d0b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml
@@ -0,0 +1,8 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property rect r: ({x: 1, y: 2, width: 3, height: 4})
+ property rect r2: { var x = 42; return {x}; }
+ property weatherModelUrl w: ({ strings: ["one", "two", "three"] })
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/theme.cpp b/tests/auto/qml/qmlcppcodegen/data/theme.cpp
index c8a2612753..10302645c0 100644
--- a/tests/auto/qml/qmlcppcodegen/data/theme.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/theme.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "theme.h"
diff --git a/tests/auto/qml/qmlcppcodegen/data/theme.h b/tests/auto/qml/qmlcppcodegen/data/theme.h
index f70a3b440e..3e56ec1f9f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/theme.h
+++ b/tests/auto/qml/qmlcppcodegen/data/theme.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef THEME_H
#define THEME_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/thisObject.qml b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml
new file mode 100644
index 0000000000..50664eced2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject warned
+
+ function f(arg: QtObject) { warned = arg }
+ function warn() { f(this) }
+
+ Component.onCompleted: warn()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp
index 1853d2dc12..04e1797c7c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "timelinetheme.h"
diff --git a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h
index 335e43b570..3e7da77cc9 100644
--- a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h
+++ b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TIMELINETHEME_H
#define TIMELINETHEME_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml b/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml
new file mode 100644
index 0000000000..a73e28f642
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml
@@ -0,0 +1,14 @@
+pragma Strict
+import QtQml
+
+Component {
+ QtObject {
+ id: root
+
+ function myOpen() {
+ root.objectName = "foo"
+ }
+
+ Component.onCompleted: myOpen()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml b/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml
new file mode 100644
index 0000000000..e7bf5ccec9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ objectName: "??= ??/ ??' ??( ??) ??! ??< ??> ??-"
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp b/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp
new file mode 100644
index 0000000000..02629ad7f6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+
+#include <QtTest>
+#include <QtCore/qobject.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qbytearray.h>
+
+class tst_QmlCppCodegenVerify : public QObject
+{
+ Q_OBJECT
+private slots:
+ void verifyGeneratedSources_data();
+ void verifyGeneratedSources();
+};
+
+void tst_QmlCppCodegenVerify::verifyGeneratedSources_data()
+{
+ QTest::addColumn<QString>("file");
+
+ QDir a(":/a");
+ const QStringList entries = a.entryList(QDir::Files);
+ for (const QString &entry : entries)
+ QTest::addRow("%s", entry.toUtf8().constData()) << entry;
+}
+
+void tst_QmlCppCodegenVerify::verifyGeneratedSources()
+{
+ QFETCH(QString, file);
+ QFile a(":/a/" + file);
+ QFile b(":/b/" + file.replace("codegen_test_module", "codegen_test_module_verify"));
+
+ QVERIFY(a.open(QIODevice::ReadOnly));
+ QVERIFY(b.open(QIODevice::ReadOnly));
+
+ const QByteArray aData = a.readAll();
+ const QByteArray bData = b.readAll()
+ .replace("verify/TestTypes", "TestTypes")
+ .replace("verify_TestTypes", "TestTypes");
+
+ QCOMPARE(aData, bData);
+}
+
+QTEST_MAIN(tst_QmlCppCodegenVerify)
+
+#include "tst_qmlcppcodegen_verify.moc"
diff --git a/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml
new file mode 100644
index 0000000000..e76443a2e0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property double d: Math.max(undefined, 40)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/urlString.qml b/tests/auto/qml/qmlcppcodegen/data/urlString.qml
index 511c54532c..a83855ebdb 100644
--- a/tests/auto/qml/qmlcppcodegen/data/urlString.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/urlString.qml
@@ -9,5 +9,12 @@ QtObject {
Component.onCompleted: {
c = "http://dddddd.com";
self.d = "http://aaaaaa.com";
+ myUrlChanged(c)
+ }
+
+ signal myUrlChanged(urlParam: url)
+
+ onMyUrlChanged: (urlParam) => {
+ objectName = urlParam;
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml
new file mode 100644
index 0000000000..a775773dda
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml
@@ -0,0 +1,43 @@
+pragma ValueTypeBehavior: Addressable
+import QtQml
+
+QtObject {
+ id: root
+ property rect r: Qt.rect(10, 20, 3, 4)
+ property var v: r
+ property real x: (v as rect).x
+
+ function f(input: bool) : var {
+ if (input)
+ return 0
+ return Qt.point(2, 2)
+ }
+
+ property var vv: Qt.point(5, 5)
+ property var uu: undefined
+
+ property int tv3: (root.vv as point)?.x
+ property var tv4: (root.uu as rect)?.x
+ property int tc3: (root?.vv as point)?.y
+ property var tc6: (root?.uu as rect)?.height
+ property var tc7: (f(true) as point)?.x
+ property var tc8: (f(false) as point)?.x
+
+ property string greeting1
+ property string greeting2
+
+ readonly property string defaultGreeting: "Default Greeting"
+ property QtObject o: QtObject {
+ id: o
+ property var customGreeting
+ function greet() : string {
+ return (o.customGreeting as string) ?? root.defaultGreeting
+ }
+ }
+
+ Component.onCompleted: {
+ root.greeting1 = o.greet()
+ o.customGreeting = "Custom Greeting"
+ root.greeting2 = o.greet()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml
index cca634753d..d33133bb6b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml
@@ -1,4 +1,4 @@
-pragma ValueTypeBehavior: Copy
+pragma ValueTypeBehavior: Copy, Addressable
import QtQml
QtObject {
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml
new file mode 100644
index 0000000000..42a55e832d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml
@@ -0,0 +1,34 @@
+import QtQml
+
+
+QtObject {
+ id: root
+
+ property list<double> numbers: {
+ var result = [];
+ for (var i = 0; i < 10; ++i)
+ result[i] = i;
+ return result;
+ }
+
+ property rect r: ({x: 1, y: 2, width: 3, height: 4})
+
+ function evil() : double {
+ var numbers = root.numbers;
+ root.numbers = [];
+ var a = 0;
+ for (var j = 0; j < 10; ++j) {
+ a += numbers[j];
+ }
+ return a;
+ }
+
+ function fvil() : double {
+ var r = root.r;
+ root.r = {x: 5, y: 6, width: 7, height: 8};
+ return r.x;
+ }
+
+ property double e: evil()
+ property double f: fvil()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml
index 568f39820c..a87f88ecf9 100644
--- a/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml
@@ -1,4 +1,4 @@
-pragma ValueTypeBehavior: Reference
+pragma ValueTypeBehavior: Reference, Inaddressable
import QtQml
QtObject {
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMap.qml b/tests/auto/qml/qmlcppcodegen/data/variantMap.qml
new file mode 100644
index 0000000000..d7147ec5fc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantMap.qml
@@ -0,0 +1,27 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property Component shadowable: QtObject {}
+ property B b: B { id: theB }
+ property rect r: theB.r
+ property var v: { "1": null, "25": undefined, "19": "19" }
+
+ property Component c: Component {
+ id: unshadowable
+ QtObject {}
+ }
+
+ // We need this extra function in order to coerce the result of the shadowable
+ // method call back to QtObject
+ function createShadowable() : QtObject {
+ return shadowable.createObject(this, {objectName: "a"})
+ }
+
+ objectName: {
+ return createShadowable().objectName
+ + " " + unshadowable.createObject(this, {objectName: "b"}).objectName
+ }
+
+ Component.onCompleted: b.r = { x: 12, y: 13, width: 14, height: 15 }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h
new file mode 100644
index 0000000000..61d38228b0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h
@@ -0,0 +1,17 @@
+#pragma once
+#include <QObject>
+#include <QVariantMap>
+#include <QtQml/qqmlregistration.h>
+
+class VariantMapLookupFoo : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QVariantMap data READ data CONSTANT)
+
+public:
+ VariantMapLookupFoo(QObject *parent = nullptr) : QObject(parent) { }
+
+private:
+ QVariantMap data() const { return { { "value", 42 } }; }
+};
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml
new file mode 100644
index 0000000000..4e56cb9448
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import TestTypes
+import QtQuick
+
+Item {
+ property int i: moo.data.value
+
+ VariantMapLookupFoo {
+ id: moo
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml b/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml
new file mode 100644
index 0000000000..cca26265c9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+QtObject {
+ property DirectBindable a: DirectBindable {
+ id: aId
+ x: WeatherModelUrlUtils.url(1)
+ }
+
+ property IndirectBindable b: IndirectBindable {
+ id: bId
+ y: aId.x
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantreturn.h b/tests/auto/qml/qmlcppcodegen/data/variantreturn.h
new file mode 100644
index 0000000000..87718aaef3
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantreturn.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTERETURN_H
+#define VARIANTERETURN_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqmlregistration.h>
+
+#include "weathermoduleurl.h"
+
+class DirectBindable : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(WeatherModelUrl x READ x WRITE setX NOTIFY xChanged BINDABLE bindableX)
+
+public:
+ explicit DirectBindable(QObject *parent = nullptr) : QObject(parent) {}
+
+ WeatherModelUrl x() const { return m_x.value(); }
+ void setX(const WeatherModelUrl& newX) { m_x.setValue(newX);}
+ QBindable<WeatherModelUrl> bindableX() { return QBindable<WeatherModelUrl>(&m_x); }
+
+Q_SIGNALS:
+ void xChanged();
+
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(DirectBindable, WeatherModelUrl, m_x, &DirectBindable::xChanged)
+};
+
+class IndirectBindable : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(WeatherModelUrl y READ y WRITE setY NOTIFY yChanged BINDABLE bindableY)
+ Q_PROPERTY(int z READ z NOTIFY zChanged)
+
+public:
+ explicit IndirectBindable(QObject *parent = nullptr) : QObject(parent) {
+ m_z.setBinding([this]()->int {
+ return m_y.value().timeIndex() * 2;
+ });
+ }
+
+ WeatherModelUrl y() const { return m_y.value(); }
+ void setY(const WeatherModelUrl& newY) { m_y.setValue(newY); }
+ QBindable<WeatherModelUrl> bindableY() { return QBindable<WeatherModelUrl>(&m_y); }
+
+ int z() const { return m_z.value(); }
+ QBindable<int> bindableZ() const { return QBindable<int>(&m_z); }
+
+Q_SIGNALS:
+ void yChanged();
+ void zChanged();
+
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(IndirectBindable, WeatherModelUrl, m_y, &IndirectBindable::yChanged)
+ Q_OBJECT_BINDABLE_PROPERTY(IndirectBindable, int, m_z, &IndirectBindable::zChanged)
+};
+
+#endif // VARIANTRETURN_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml b/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml
new file mode 100644
index 0000000000..f5826d9e48
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ id: item
+ property point p: Qt.point(20, 10)
+
+ Component.onCompleted: {
+ item.p = undefined
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h b/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h
new file mode 100644
index 0000000000..998118dc5b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WEATHERMODELURL_H
+#define WEATHERMODELURL_H
+
+#include <QtQml/qqmlregistration.h>
+#include <QtCore/qobject.h>
+
+class WeatherModelUrl
+{
+ Q_GADGET
+ QML_STRUCTURED_VALUE
+ QML_VALUE_TYPE(weatherModelUrl)
+ Q_PROPERTY(qsizetype timeIndex READ timeIndex CONSTANT)
+ Q_PROPERTY(QStringList strings READ strings WRITE setStrings)
+
+public:
+ WeatherModelUrl() : m_timeIndex(-1) {}
+ WeatherModelUrl(qsizetype timeIdx) : m_timeIndex(timeIdx) {}
+
+ qsizetype timeIndex() const { return m_timeIndex; }
+
+ QStringList strings() const { return m_strings; }
+ void setStrings(const QStringList &newStrings)
+ {
+ if (m_strings != newStrings)
+ m_strings = newStrings;
+ }
+
+private:
+ friend bool operator==(const WeatherModelUrl &a, const WeatherModelUrl &b)
+ {
+ return a.m_timeIndex == b.m_timeIndex;
+ }
+
+ friend bool operator!=(const WeatherModelUrl &a, const WeatherModelUrl &b)
+ {
+ return !(a == b);
+ }
+
+ qsizetype m_timeIndex;
+ QStringList m_strings;
+};
+
+class WeatherModelUrlUtils : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ WeatherModelUrlUtils(QObject *parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE static WeatherModelUrl url(int timeIdx)
+ {
+ return WeatherModelUrl(timeIdx);
+ }
+};
+
+#endif // WEATHERMODELURL_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/withlength.h b/tests/auto/qml/qmlcppcodegen/data/withlength.h
index ba95522c53..26c6307f2b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/withlength.h
+++ b/tests/auto/qml/qmlcppcodegen/data/withlength.h
@@ -1,11 +1,13 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WITHLENGTH_H
#define WITHLENGTH_H
#include <QtCore/qobject.h>
-#include <QtQmlIntegration/qqmlintegration.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+#include <QtQml/qqml.h>
struct ValueTypeWithLength
{
@@ -18,6 +20,8 @@ struct ValueTypeWithLength
public:
ValueTypeWithLength() = default;
Q_INVOKABLE ValueTypeWithLength(int length) : m_length(length) {}
+ Q_INVOKABLE ValueTypeWithLength(QPointF point) : m_length(point.manhattanLength()) {}
+ Q_INVOKABLE ValueTypeWithLength(QRectF rect) : m_length(rect.width()) {}
Q_INVOKABLE QString toString() const { return QStringLiteral("no"); }
int length() const { return m_length; }
@@ -26,4 +30,24 @@ private:
int m_length = 19;
};
+struct InnerWithLength {
+ int m_length;
+};
+
+struct UnconstructibleWithLength
+{
+ Q_GADGET
+ QML_VALUE_TYPE(unconstructibleWithLength)
+
+ QML_FOREIGN(InnerWithLength)
+ QML_EXTENDED(UnconstructibleWithLength)
+
+public:
+ UnconstructibleWithLength() = default;
+ Q_INVOKABLE UnconstructibleWithLength(int length) : v{length} {}
+
+private:
+ InnerWithLength v;
+};
+
#endif // WITHLENGTH_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h b/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h
index dce78fa9c9..7de49f095a 100644
--- a/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h
+++ b/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WRAPWITHVARIANT_H
#define WRAPWITHVARIANT_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h b/tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h
new file mode 100644
index 0000000000..3c0fedd28b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/writableVariantMap.h
@@ -0,0 +1,31 @@
+#pragma once
+#include <QObject>
+#include <QVariantMap>
+#include <QtQml/qqmlregistration.h>
+
+class WritableVariantMap : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QVariantMap data READ data WRITE setData NOTIFY dataChanged)
+
+public:
+ WritableVariantMap(QObject *parent = nullptr) : QObject(parent) { }
+
+ QVariantMap data() const { return m_data; }
+ void setData(const QVariantMap &data)
+ {
+ if (m_data != data) {
+ m_data = data;
+ emit dataChanged();
+ }
+ }
+
+signals:
+ void dataChanged();
+
+private:
+ QVariantMap m_data;
+};
+
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml b/tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml
new file mode 100644
index 0000000000..536e53b408
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/writeVariantMap.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import StringBuilderTestTypes
+
+WritableVariantMap {
+ id: dragSource
+ property string modelData: "Drag Me"
+ data: ({
+ "text/plain": "%" + dragSource.modelData + "%"
+ })
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/writeback.qml b/tests/auto/qml/qmlcppcodegen/data/writeback.qml
new file mode 100644
index 0000000000..359f00efb7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/writeback.qml
@@ -0,0 +1,43 @@
+pragma Strict
+
+import TestTypes
+import QtQml
+
+Person {
+ id: self
+
+ area {
+ width: 19
+ height: 199
+ }
+
+ property list<int> ints: [4, 3, 2, 1]
+
+ property outer recursive
+ property Person shadowable: Person {
+ id: notShadowable
+ area.width: self.area.width
+ area2.height: self.area2.height
+ }
+
+ Component.onCompleted: {
+ area.width = 16
+ area2.height = 17
+
+ self.area.x = 4
+ self.area2.y = 5
+
+ // You cannot do this on the shadowable Person because
+ // shadowable.area may not actually be a QRectF anymore.
+ notShadowable.area.x = 40
+ notShadowable.area2.y = 50
+
+ self.recursive.inner.i = 99;
+
+ self.ints[0] = 12;
+ ints[1] = 22;
+ ints[6] = 33;
+ }
+
+ property int inner: recursive.inner.i
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 400075c08d..53cc068e8c 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -1,10 +1,16 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include "data/druggeljug.h"
#include <data/birthdayparty.h>
#include <data/cppbaseclass.h>
+#include <data/druggeljug.h>
+#include <data/enumProperty.h>
#include <data/enumproblems.h>
+#include <data/getOptionalLookup.h>
#include <data/objectwithmethod.h>
+#include <data/resettable.h>
+#include <data/weathermoduleurl.h>
+#include <data/withlength.h>
#include <QtQml/private/qqmlengine_p.h>
#include <QtQml/private/qqmlpropertycachecreator_p.h>
@@ -12,6 +18,7 @@
#include <QtTest>
#include <QtQml>
#include <QtGui/qcolor.h>
+#include <QtGui/qpa/qplatformdialoghelper.h>
#if QT_CONFIG(process)
#include <QtCore/qprocess.h>
@@ -26,146 +33,460 @@ class tst_QmlCppCodegen : public QObject
Q_OBJECT
private slots:
void initTestCase();
+ void cleanupTestCase();
- void simpleBinding();
- void cppValueTypeList();
+ void accessModelMethodFromOutSide();
+ void aliasLookup();
+ void ambiguousAs();
+ void ambiguousSignals();
void anchorsFill();
- void signalHandler();
- void idAccess();
- void globals();
- void multiLookup();
- void enums();
- void funcWithParams();
- void intOverflow();
- void stringLength();
- void scopeVsObject();
- void compositeTypeMethod();
- void excessiveParameters();
- void jsImport();
- void jsmoduleImport();
- void methods();
- void math();
- void unknownParameter();
+ void argumentConversion();
void array();
- void equalsUndefined();
- void conversions();
- void interestingFiles_data();
- void interestingFiles();
- void extendedTypes();
- void construct();
- void contextParam();
- void attachedType();
- void componentReturnType();
- void onAssignment();
- void failures();
- void enumScope();
- void unusedAttached();
+ void arrayCtor();
+ void asCast();
void attachedBaseEnum();
- void nullAccess();
- void interceptor();
- void nonNotifyable();
- void importsFromImportPath();
- void aliasLookup();
- void outOfBoundsArray();
- void compositeSingleton();
- void lotsOfRegisters();
- void inPlaceDecrement();
- void shifts();
- void valueTypeProperty();
- void propertyOfParent();
- void accessModelMethodFromOutSide();
- void functionArguments();
+ void attachedSelf();
+ void attachedType();
+ void badSequence();
+ void basicBlocksWithBackJump();
+ void basicBlocksWithBackJump_infinite();
+ void basicDTZ();
+ void bindToValueType();
void bindingExpression();
- void voidFunction();
- void overriddenProperty();
- void listLength();
- void parentProperty();
- void registerElimination();
- void asCast();
- void noQQmlData();
- void scopeObjectDestruction();
+ void blockComments();
+ void boolCoercions();
+ void boolPointerMerge();
+ void boundComponents();
+ void callContextPropertyLookupResult();
+ void callWithSpread();
void colorAsVariant();
- void bindToValueType();
- void undefinedResets();
- void innerObjectNonShadowable();
- void ownPropertiesNonShadowable();
- void modulePrefix();
void colorString();
- void urlString();
- void callContextPropertyLookupResult();
+ void compareOriginals();
+ void comparisonTypes();
+ void componentReturnType();
+ void compositeSingleton();
+ void compositeTypeMethod();
+ void consoleObject();
+ void consoleTrace();
+ void construct();
+ void contextParam();
+ void conversionDecrement();
+ void conversionInDeadCode();
+ void conversions();
+ void convertPrimitiveToVar();
+ void convertQJSPrimitiveValueToIntegral();
+ void convertToOriginalReadAcumulatorForUnaryOperators();
+ void cppMethodListReturnType();
+ void cppValueTypeList();
+ void dateConstruction();
+ void dateConversions();
void deadShoeSize();
- void listIndices();
- void jsMathObject();
- void intEnumCompare();
- void attachedSelf();
- void functionReturningVoid();
- void functionCallOnNamespaced();
+ void dialogButtonBox();
+ void enumConversion();
+ void enumFromBadSingleton();
+ void enumLookup();
+ void enumMarkedAsFlag();
+ void enumProblems();
+ void enumScope();
+ void enums();
+ void enforceSignature();
+ void enumsInOtherObject();
+ void equalityQObjects();
+ void equalityQUrl();
+ void equalityTestsWithNullOrUndefined();
+ void equalityVarAndNonStorable();
+ void equalityVarAndStorable();
+ void equalsUndefined();
+ void evadingAmbiguity();
+ void exceptionFromInner();
+ void excessiveParameters();
+ void extendedTypes();
+ void failures();
+ void fallbackLookups();
+ void fileImportsContainCxxTypes();
+ void flagEnum();
void flushBeforeCapture();
- void unknownAttached();
- void variantlist();
- void popContextAfterRet();
- void revisions();
- void invisibleBase();
- void notEqualsInt();
- void infinities();
- void blockComments();
+ void fromBoolValue();
+ void funcWithParams();
+ void functionArguments();
+ void functionCallOnNamespaced();
void functionLookup();
- void objectInVar();
+ void functionReturningVoid();
void functionTakingVar();
- void testIsnan();
- void fallbackLookups();
- void typedArray();
- void prefixedType();
- void evadingAmbiguity();
- void fromBoolValue();
- void invisibleTypes();
+ void getLookupOfScript();
+ void getOptionalLookup();
+ void getOptionalLookup_data();
+ void getOptionalLookupOnQJSValueNonStrict();
+ void getOptionalLookupShadowed();
+ void globals();
+ void idAccess();
+ void ignoredFunctionReturn();
+ void importsFromImportPath();
+ void inPlaceDecrement();
+ void inaccessibleProperty();
+ void indirectlyShadowable();
+ void infinities();
+ void infinitiesToInt();
+ void innerObjectNonShadowable();
+ void intEnumCompare();
+ void intOverflow();
+ void intToEnum();
+ void interceptor();
+ void interestingFiles();
+ void interestingFiles_data();
+ void internalConversion();
void invalidPropertyType();
- void valueTypeLists();
- void boundComponents();
+ void invisibleBase();
void invisibleListElementType();
- void typePropertyClash();
- void objectToString();
- void throwObjectName();
+ void invisibleSingleton();
+ void invisibleTypes();
+ void iteration();
void javaScriptArgument();
- void translation();
- void stringArg();
- void conversionDecrement();
- void unstoredUndefined();
- void registerPropagation();
- void argumentConversion();
- void badSequence();
- void enumLookup();
- void trivialSignalHandler();
- void stringToByteArray();
+ void jsArrayMethods();
+ void jsArrayMethodsWithParams();
+ void jsArrayMethodsWithParams_data();
+ void jsImport();
+ void jsMathObject();
+ void jsmoduleImport();
+ void lengthAccessArraySequenceCompat();
+ void letAndConst();
+ void listAsArgument();
+ void listConversion();
+ void listIndices();
+ void listLength();
+ void listOfInvisible();
void listPropertyAsModel();
- void notNotString();
+ void listToString();
+ void lotsOfRegisters();
+ void math();
+ void mathMinMax();
void mathOperations();
- void inaccessibleProperty();
- void typePropagationLoop();
- void signatureIgnored();
- void listAsArgument();
- void letAndConst();
- void signalIndexMismatch();
- void callWithSpread();
- void nullComparison();
- void consoleObject();
+ void mathStaticProperties();
+ void mergedObjectReadWrite();
+ void methodOnListLookup();
+ void methods();
+ void modulePrefix();
+ void multiDirectory_data();
+ void multiDirectory();
void multiForeign();
+ void multiLookup();
+ void multipleCtors();
void namespaceWithEnum();
- void enumProblems();
- void enumConversion();
- void ambiguousSignals();
- void fileImportsContainCxxTypes();
- void lengthAccessArraySequenceCompat();
- void storeElementSideEffects();
+ void noQQmlData();
+ void nonNotifyable();
+ void notEqualsInt();
+ void notNotString();
+ void nullAccess();
+ void nullAccessInsideSignalHandler();
+ void nullComparison();
+ void nullishCoalescing();
+ void nullishCoalescing_data();
void numbersInJsPrimitive();
- void infinitiesToInt();
- void equalityVarAndNonStorable();
- void equalityQObjects();
- void dateConversions();
+ void objectInVar();
+ void objectLookupOnListElement();
+ void objectToString();
+ void objectWithStringListMethod();
+ void onAssignment();
+ void optionalComparison();
+ void outOfBoundsArray();
+ void overriddenProperty();
+ void ownPropertiesNonShadowable();
+ void parentProperty();
+ void popContextAfterRet();
+ void prefixedType();
+ void propertyOfParent();
+ void reduceWithNullThis();
+ void readEnumFromInstance();
+ void readonlyListProperty();
+ void registerElimination();
+ void registerPropagation();
+ void renameAdjust();
+
+ void resettableProperty();
+ void resettableProperty_data();
+
+ void returnAfterReject();
+ void revisions();
+ void scopeIdLookup();
+ void scopeObjectDestruction();
+ void scopeVsObject();
+ void scopedEnum();
+ void sequenceToIterable();
+ void setLookupConversion();
+ void setLookupOriginalScope();
+ void shadowedAsCasts();
+ void shadowedMethod();
+ void shadowedPrimitiveCmpEqNull();
+ void shifts();
+ void signalHandler();
+ void signalIndexMismatch();
+ void signalsWithLists();
+ void signatureIgnored();
+ void simpleBinding();
+ void storeElementSideEffects();
+ void storeMetaEnum();
+ void stringArg();
+ void stringLength();
+ void stringToByteArray();
+ void structuredValueType();
+ void testIsnan();
+ void thisObject();
+ void throwObjectName();
+ void topLevelComponent();
+ void translation();
+ void trigraphs();
+ void trivialSignalHandler();
+ void typePropagationLoop();
+ void typePropertyClash();
+ void typedArray();
+ void undefinedResets();
+ void undefinedToDouble();
+ void unknownAttached();
+ void unknownParameter();
+ void unstoredUndefined();
+ void unusedAttached();
+ void urlString();
void valueTypeBehavior();
- void invisibleSingleton();
+ void valueTypeLists();
+ void valueTypeProperty();
+ void variantMapLookup();
+ void variantReturn();
+ void variantlist();
+ void variantMap();
+ void voidConversion();
+ void voidFunction();
+ void writeBack();
+ void writeVariantMap();
};
+static QByteArray arg1()
+{
+ const QStringList args = QCoreApplication::instance()->arguments();
+ return args.size() > 1 ? args[1].toUtf8() : QByteArray("undefined");
+}
+
+namespace QmlCacheGeneratedCode {
+namespace _qt_qml_TestTypes_failures_qml {
+extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];
+}
+}
+
+static void checkColorProperties(QQmlComponent *component)
+{
+ QVERIFY2(component->isReady(), qPrintable(component->errorString()));
+ QScopedPointer<QObject> rootObject(component->create());
+ QVERIFY(rootObject);
+
+ const QMetaObject *mo = QMetaType::fromName("QQuickIcon").metaObject();
+ QVERIFY(mo != nullptr);
+
+ const QMetaProperty prop = mo->property(mo->indexOfProperty("color"));
+ QVERIFY(prop.isValid());
+
+ const QVariant a = rootObject->property("a");
+ QVERIFY(a.isValid());
+
+ const QVariant iconColor = prop.readOnGadget(rootObject->property("icon").data());
+ QVERIFY(iconColor.isValid());
+
+ const QMetaType colorType = QMetaType::fromName("QColor");
+ QVERIFY(colorType.isValid());
+
+ QCOMPARE(a.metaType(), colorType);
+ QCOMPARE(iconColor.metaType(), colorType);
+
+ QCOMPARE(iconColor, a);
+}
+
+static const double numbers[] = {
+ qQNaN(), -qInf(),
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<float>::min(),
+ std::numeric_limits<qint32>::min(),
+ -1000.2, -100, -2, -1.333, -1, -0.84, -0.5,
+
+ // -0 and 0 are not different on the QML side. Therefore, don't keep them adjacent.
+ // Otherwise the bindings won't get re-evaluated.
+ std::copysign(0.0, -1), 1, 0.0,
+
+ 0.5, 0.77, 1.4545, 2, 199, 2002.13,
+ std::numeric_limits<qint32>::max(),
+ std::numeric_limits<quint32>::max(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<double>::max(),
+ qInf()
+};
+
+class MyCppType : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool useListDelegate
+ READ useListDelegate
+ WRITE setUseListDelegate
+ NOTIFY useListDelegateChanged)
+public:
+ explicit MyCppType(QObject * parent = nullptr) : QObject(parent) {}
+
+ bool useListDelegate() const { return m_useListDelegate; }
+ void setUseListDelegate(bool useListDelegate)
+ {
+ if (useListDelegate != m_useListDelegate) {
+ m_useListDelegate = useListDelegate;
+ emit useListDelegateChanged();
+ }
+ }
+
+signals:
+ void useListDelegateChanged();
+
+private:
+ bool m_useListDelegate = false;
+};
+
+class InvisibleListElementType : public QObject
+{
+ Q_OBJECT
+public:
+ InvisibleListElementType(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+template<typename T>
+QString toOperand(double arg);
+
+template<>
+QString toOperand<double>(double arg)
+{
+ if (qIsNull(arg))
+ return std::signbit(arg) ? QStringLiteral("(-0)") : QStringLiteral("(0)");
+
+ return u'(' + QJSPrimitiveValue(arg).toString() + u')';
+}
+
+template<>
+QString toOperand<int>(double arg)
+{
+ const int iArg = QJSPrimitiveValue(arg).toInteger();
+ return u'(' + QJSPrimitiveValue(iArg).toString() + u')';
+}
+
+template<>
+QString toOperand<bool>(double arg)
+{
+ const bool bArg = QJSPrimitiveValue(arg).toBoolean();
+ return u'(' + QJSPrimitiveValue(bArg).toString() + u')';
+}
+
+template<typename T1, typename T2>
+double jsEval(double arg1, double arg2, const QString &op, QJSEngine *engine)
+{
+ auto evalBinary = [&](const QString &jsOp) {
+ return engine->evaluate(toOperand<T1>(arg1) + jsOp + toOperand<T2>(arg2)).toNumber();
+ };
+
+ auto evalBinaryConst = [&](const QString &jsOp) {
+ return engine->evaluate(toOperand<T1>(arg1) + jsOp + u'9').toNumber();
+ };
+
+ auto evalUnary = [&](const QString &jsOp) {
+ return engine->evaluate(jsOp + toOperand<T1>(arg1)).toNumber();
+ };
+
+ auto evalInPlace = [&](const QString &jsOp) {
+ return engine->evaluate(
+ u"(function() {var a = "_s + toOperand<T1>(arg1)+ u"; return "_s
+ + jsOp + u"a;})()"_s).toNumber();
+ };
+
+ if (op == u"unot")
+ return evalUnary(u"!"_s);
+ if (op == u"uplus")
+ return evalUnary(u"+"_s);
+ if (op == u"uminus")
+ return evalUnary(u"-"_s);
+ if (op == u"ucompl")
+ return evalUnary(u"~"_s);
+
+ if (op == u"increment")
+ return evalInPlace(u"++"_s);
+ if (op == u"decrement")
+ return evalInPlace(u"--"_s);
+
+ if (op == u"add")
+ return evalBinary(u"+"_s);
+ if (op == u"sub")
+ return evalBinary(u"-"_s);
+ if (op == u"mul")
+ return evalBinary(u"*"_s);
+ if (op == u"div")
+ return evalBinary(u"/"_s);
+ if (op == u"exp")
+ return evalBinary(u"**"_s);
+ if (op == u"mod")
+ return evalBinary(u"%"_s);
+
+ if (op == u"bitAnd")
+ return evalBinary(u"&"_s);
+ if (op == u"bitOr")
+ return evalBinary(u"|"_s);
+ if (op == u"bitXor")
+ return evalBinary(u"^"_s);
+
+ if (op == u"bitAndConst")
+ return evalBinaryConst(u"&"_s);
+ if (op == u"bitOrConst")
+ return evalBinaryConst(u"|"_s);
+ if (op == u"bitXorConst")
+ return evalBinaryConst(u"^"_s);
+
+ if (op == u"ushr")
+ return evalBinary(u">>>"_s);
+ if (op == u"shr")
+ return evalBinary(u">>"_s);
+ if (op == u"shl")
+ return evalBinary(u"<<"_s);
+
+ if (op == u"ushrConst")
+ return evalBinaryConst(u">>>"_s);
+ if (op == u"shrConst")
+ return evalBinaryConst(u">>"_s);
+ if (op == u"shlConst")
+ return evalBinaryConst(u"<<"_s);
+
+ qDebug() << op;
+ Q_UNREACHABLE_RETURN(0);
+}
+
+static QList<QString> convertToStrings(const QList<qint64> &ints)
+{
+ QList<QString> strings;
+ for (qint64 i : ints)
+ strings.append(QString::number(i));
+ return strings;
+}
+
+static QRegularExpression bindingLoopMessage(const QUrl &url, char var)
+{
+ // The actual string depends on how many times QObject* was registered with what parameters.
+ return QRegularExpression(
+ "%1:4:1: QML [^:]+: Binding loop detected for property \"%2\""_L1
+ .arg(url.toString()).arg(QLatin1Char(var)));
+}
+
+static void listsEqual(QObject *listProp, QObject *array, const char *method)
+{
+ const QByteArray listPropertyPropertyName = QByteArray("listProperty") + method;
+ const QByteArray jsArrayPropertyName = QByteArray("jsArray") + method;
+
+ const QQmlListReference listPropertyProperty(listProp, listPropertyPropertyName.constData());
+ const QVariantList jsArrayProperty = array->property(jsArrayPropertyName.constData()).toList();
+
+ const qsizetype listPropertyCount = listPropertyProperty.count();
+ QCOMPARE(listPropertyCount, jsArrayProperty.count());
+
+ for (qsizetype i = 0; i < listPropertyCount; ++i)
+ QCOMPARE(listPropertyProperty.at(i), jsArrayProperty.at(i).value<QObject *>());
+}
+
void tst_QmlCppCodegen::initTestCase()
{
#ifdef QT_TEST_FORCE_INTERPRETER
@@ -173,40 +494,91 @@ void tst_QmlCppCodegen::initTestCase()
#endif
}
-void tst_QmlCppCodegen::simpleBinding()
+void tst_QmlCppCodegen::cleanupTestCase()
+{
+ // This code checks for basic blocks validation failures in the tests
+ QStringList expectedFailures = {
+ "codegen_test_module_basicBlocksWithBackJump_infinite_qml.cpp",
+ "codegen_test_module_verify_basicBlocksWithBackJump_infinite_qml.cpp",
+ };
+
+ QString generatedCppFolder = GENERATED_CPP_FOLDER;
+ QDirIterator dirIterator(generatedCppFolder, { "*.cpp" }, QDir::Files);
+ while (dirIterator.hasNext()) {
+ QFile file(dirIterator.next());
+ if (!file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text)) {
+ qDebug() << "Couldn't open generated file";
+ continue;
+ }
+
+ const auto content = file.readAll();
+ if (bool validationFailed = content.contains("// QV4_BASIC_BLOCK_VALIDATION_FAILED:"_L1)) {
+ if (expectedFailures.contains(dirIterator.fileInfo().fileName())) {
+ QEXPECT_FAIL("", "Expected failure", Continue);
+ }
+ const auto message = file.fileName() + ": Basic blocks validation failed.";
+ QVERIFY2(!validationFailed, message.toStdString().c_str());
+ }
+ }
+}
+
+void tst_QmlCppCodegen::accessModelMethodFromOutSide()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/AccessModelMethodsFromOutside.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtDebugMsg, "3");
+ QTest::ignoreMessage(QtDebugMsg, "Apple");
QScopedPointer<QObject> object(component.create());
- QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
- QCOMPARE(object->property("foo").toInt(), int(3));
- {
- CppBaseClass *base = qobject_cast<CppBaseClass *>(object.data());
- Q_ASSERT(base);
- QVERIFY(!base->cppProp.hasBinding());
- QCOMPARE(base->cppProp.value(), 7);
- QVERIFY(base->cppProp2.hasBinding());
- QCOMPARE(base->cppProp2.value(), 14);
- base->cppProp.setValue(9);
- QCOMPARE(base->cppProp.value(), 9);
- QCOMPARE(base->cppProp2.value(), 18);
- }
+ QCOMPARE(object->property("cost1").toDouble(), 3);
+ QCOMPARE(object->property("name1").toString(), u"Orange"_s);
+ QCOMPARE(object->property("cost2").toDouble(), 1.95);
+ QCOMPARE(object->property("name2").toString(), u"Banana"_s);
}
-void tst_QmlCppCodegen::cppValueTypeList()
+void tst_QmlCppCodegen::aliasLookup()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/aliasLookup.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
- QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
- QCOMPARE(object->property("a").toInt(), 16);
- QMetaObject::invokeMethod(object.data(), "incA");
- QCOMPARE(object->property("a").toInt(), 17);
+ QVERIFY(!object.isNull());
- QCOMPARE(object->property("b").toDouble(), 0.25);
- QMetaObject::invokeMethod(object.data(), "incB");
- QCOMPARE(object->property("b").toDouble(), 13.5);
+ const QVariant t = object->property("t");
+ QCOMPARE(t.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(t.toString(), u"12"_s);
+}
+
+void tst_QmlCppCodegen::ambiguousAs()
+{
+ QQmlEngine e;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/ambiguousAs.qml"_s);
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("other").value<QObject *>(), o.data());
+ o->setProperty("useSelf", QVariant::fromValue(false));
+ QCOMPARE(o->property("other").value<QObject *>(), nullptr);
+}
+
+void tst_QmlCppCodegen::ambiguousSignals()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguousSignals.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"tomorrow"_s);
+ Person *p = qobject_cast<Person *>(o.data());
+ QVERIFY(p);
+ emit p->ambiguous(12);
+ QCOMPARE(o->objectName(), u"12foo"_s);
+ emit p->ambiguous();
+ QCOMPARE(o->objectName(), u"9foo"_s);
}
void tst_QmlCppCodegen::anchorsFill()
@@ -231,342 +603,629 @@ void tst_QmlCppCodegen::anchorsFill()
QCOMPARE(child->property("width").toInt(), 47);
}
-void tst_QmlCppCodegen::signalHandler()
+void tst_QmlCppCodegen::argumentConversion()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signal.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/argumentConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ auto checkNaN = [&](const char *propName) {
+ const QVariant prop = o->property(propName);
+ QCOMPARE(prop.metaType(), QMetaType::fromType<double>());
+ QVERIFY(qIsNaN(prop.toDouble()));
+ };
+
+ checkNaN("a");
+ checkNaN("b");
+ checkNaN("e");
+
+ QCOMPARE(o->property("c").toDouble(), 3.0);
+ QCOMPARE(o->property("d").toDouble(), -1.0);
+ QCOMPARE(o->property("f").toDouble(), 10.0);
+}
+
+void tst_QmlCppCodegen::array()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/array.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->objectName(), QString());
- QCOMPARE(object->property("ff").toInt(), 4);
+ const QJSValue value1 = object->property("values1").value<QJSValue>();
+ QVERIFY(value1.isArray());
+ QCOMPARE(value1.property(u"length"_s).toInt(), 3);
+ QCOMPARE(value1.property(0).toInt(), 1);
+ QCOMPARE(value1.property(1).toInt(), 2);
+ QCOMPARE(value1.property(2).toInt(), 3);
- object->setObjectName(u"foo"_s);
- QCOMPARE(object->property("ff").toInt(), 12);
+ const QJSValue value2 = object->property("values2").value<QJSValue>();
+ QVERIFY(value2.isArray());
+ QCOMPARE(value2.property(u"length"_s).toInt(), 0);
}
-void tst_QmlCppCodegen::idAccess()
+void tst_QmlCppCodegen::arrayCtor()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/idAccess.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/arrayCtor.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QVERIFY(object->property("y").toInt() != 48);
- QCOMPARE(object->property("y").toInt(), 12);
- object->setProperty("z", 13);
- QCOMPARE(object->property("y").toInt(), 13);
- object->setProperty("x", QVariant::fromValue(333));
- QCOMPARE(object->property("y").toInt(), 48);
+ QCOMPARE(object->property("defaultCtor"), QVariant::fromValue(QList<int>()));
+ QCOMPARE(object->property("oneArgCtor"), QVariant::fromValue(QList<int>(5)));
+ QCOMPARE(object->property("multiArgCtor"), QVariant::fromValue(QList<int>({2, 3, 3, 4})));
+ QCOMPARE(object->property("arrayTrue"), QVariant::fromValue(QList<bool>({true})));
+ QCOMPARE(object->property("arrayFalse"), QVariant::fromValue(QList<bool>({false})));
+ QCOMPARE(object->property("arrayNegative"), QVariant::fromValue(QList<double>()));
+}
- // The binding was broken by setting the property
- object->setProperty("z", 14);
- QCOMPARE(object->property("y").toInt(), 48);
+void tst_QmlCppCodegen::asCast()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/asCast.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
- QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_s);
- QFont f = qvariant_cast<QFont>(ttt->property("font"));
- QCOMPARE(f.pointSize(), 22);
+ QQmlContext *context = qmlContext(root.data());
+ const QObject *object = context->objectForName(u"object"_s);
+ const QObject *item = context->objectForName(u"item"_s);
+ const QObject *rectangle = context->objectForName(u"rectangle"_s);
+ const QObject *dummy = context->objectForName(u"dummy"_s);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsObject")), object);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsItem")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsObject")), item);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsItem")), item);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsObject")), rectangle);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsItem")), rectangle);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsRectangle")), rectangle);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsObject")), dummy);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsItem")), dummy);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsDummy")), dummy);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsObject")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsItem")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsObject")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsItem")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsDummy")), nullptr);
}
-static QByteArray arg1()
+void tst_QmlCppCodegen::attachedBaseEnum()
{
- const QStringList args = QCoreApplication::instance()->arguments();
- return args.size() > 1 ? args[1].toUtf8() : QByteArray("undefined");
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/attachedBaseEnum.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QObject *drag = qvariant_cast<QObject *>(object->property("drag"));
+ QVERIFY(drag);
+
+ // Drag.YAxis is 2, but we cannot #include it here.
+ bool ok = false;
+ QCOMPARE(drag->property("axis").toInt(&ok), 2);
+ QVERIFY(ok);
}
-void tst_QmlCppCodegen::globals()
+void tst_QmlCppCodegen::attachedSelf()
{
QQmlEngine engine;
- int exitCode = -1;
- QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; });
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/globals.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/SelectionRectangle.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- const QByteArray message = QByteArray("Start 2 ") + arg1();
- QTest::ignoreMessage(QtDebugMsg, message.constData());
+ QObject *handle = qvariant_cast<QObject *>(o->property("aa"));
+ QVERIFY(handle);
+ QVERIFY(qvariant_cast<QObject *>(handle->property("rect")) != nullptr);
+}
+void tst_QmlCppCodegen::attachedType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/text.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QTRY_COMPARE(exitCode, 0);
+ QCOMPARE(object->property("dayz").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
+ QCOMPARE(object->property("oParty").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QObject *application = qvariant_cast<QObject *>(object->property("application"));
- QVERIFY(application);
- QCOMPARE(QString::fromUtf8(application->metaObject()->className()),
- u"QQuickApplication"_s);
+ QObject *party = qvariant_cast<QObject *>(object->property("party"));
+ QVERIFY(party);
+ QCOMPARE(party->property("eee").toInt(), 21);
+ QCOMPARE(party->property("fff").toInt(), 33);
+ QCOMPARE(object->property("ggg").toInt(), 37);
+}
- QTest::ignoreMessage(QtDebugMsg, "End");
- QMetaObject::invokeMethod(application, "aboutToQuit");
+void tst_QmlCppCodegen::badSequence()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/badSequence.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- const QVariant somewhere = object->property("somewhere");
- QCOMPARE(somewhere.userType(), QMetaType::QUrl);
- QCOMPARE(qvariant_cast<QUrl>(somewhere).toString(), u"qrc:/somewhere/else.qml"_s);
+ Person *self = qobject_cast<Person *>(o.data());
+ QVERIFY(self);
+ QVERIFY(self->barzles().isEmpty());
+ QVERIFY(self->cousins().isEmpty());
- const QVariant somewhereString = object->property("somewhereString");
- QCOMPARE(somewhereString.userType(), QMetaType::QString);
- QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_s);
+ Person *other = o->property("other").value<Person *>();
+ QVERIFY(other);
- const QVariant plain = object->property("plain");
- QCOMPARE(plain.userType(), QMetaType::QUrl);
- QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_s);
+ QVERIFY(other->barzles().isEmpty());
+ QVERIFY(other->cousins().isEmpty());
+
+ Barzle f1;
+ Barzle f2;
+ const QList<Barzle *> barzles { &f1, &f2 };
+ const QList<Person *> cousins { self, other };
+
+ other->setBarzles(barzles);
+ QCOMPARE(self->barzles(), barzles);
+ QCOMPARE(self->property("l").toInt(), 2);
+
+ other->setCousins(cousins);
+ QCOMPARE(self->cousins(), cousins);
+ QCOMPARE(self->property("m").toInt(), 2);
+
+ QQmlListProperty<Person> others
+ = self->property("others").value<QQmlListProperty<Person>>();
+ QCOMPARE(others.count(&others), 2);
+ QCOMPARE(others.at(&others, 0), cousins[0]);
+ QCOMPARE(others.at(&others, 1), cousins[1]);
+
+ QQmlListProperty<Person> momsCousins
+ = self->property("momsCousins").value<QQmlListProperty<Person>>();
+ QCOMPARE(momsCousins.count(&momsCousins), 2);
+ QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]);
+ QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]);
+
+ QQmlListProperty<Person> dadsCousins
+ = self->property("dadsCousins").value<QQmlListProperty<Person>>();
+ QCOMPARE(dadsCousins.count(&dadsCousins), 1);
+ QCOMPARE(dadsCousins.at(&dadsCousins, 0), other);
}
-void tst_QmlCppCodegen::multiLookup()
+static bool expectingMessage = false;
+static void handler(QtMsgType type, const QMessageLogContext &, const QString &message)
+{
+ QVERIFY(expectingMessage);
+ QCOMPARE(type, QtDebugMsg);
+ QCOMPARE(message, u"false");
+ expectingMessage = false;
+}
+
+void tst_QmlCppCodegen::basicBlocksWithBackJump()
{
- // Multiple lookups of singletons (Qt in this case) don't clash with one another.
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/immediateQuit.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicBlocksWithBackJump.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
- const QByteArray message = QByteArray("End: ") + arg1();
- QTest::ignoreMessage(QtDebugMsg, message.constData());
+ const auto oldHandler = qInstallMessageHandler(&handler);
+ const auto guard = qScopeGuard([oldHandler]() { qInstallMessageHandler(oldHandler); });
- QSignalSpy quitSpy(&engine, &QQmlEngine::quit);
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(quitSpy.size(), 1);
+ // t1 does not log anything
+ QMetaObject::invokeMethod(o.data(), "t1");
+
+ // t2 logs "false" exactly once
+ expectingMessage = true;
+ QMetaObject::invokeMethod(o.data(), "t2");
+ QVERIFY(!expectingMessage);
+
+ // t3 logs "false" exactly once
+ expectingMessage = true;
+ QMetaObject::invokeMethod(o.data(), "t3");
+ QVERIFY(!expectingMessage);
}
-void tst_QmlCppCodegen::enums()
+void tst_QmlCppCodegen::basicBlocksWithBackJump_infinite()
{
QQmlEngine engine;
- {
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Enums.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicBlocksWithBackJump_infinite.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+}
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/Enums.qml:4:1: "
- "QML Enums: Layout must be attached to Item elements");
- QScopedPointer<QObject> object(component.create());
+void tst_QmlCppCodegen::basicDTZ()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicDTZ.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
- QVERIFY(!object.isNull());
- bool ok = false;
- QCOMPARE(object->property("appState").toInt(&ok), 2);
- QVERIFY(ok);
- QCOMPARE(object->property("color").toString(), u"blue"_s);
+ QCOMPARE(o->property("title").toString(), u"none");
- QTRY_COMPARE(object->property("appState").toInt(&ok), 1);
- QVERIFY(ok);
- QCOMPARE(object->property("color").toString(), u"green"_s);
+ QMetaObject::invokeMethod(o.data(), "t1");
+ QMetaObject::invokeMethod(o.data(), "t2");
+ QMetaObject::invokeMethod(o.data(), "t3");
+
+ QCOMPARE(o->property("title").toString(), u"Baz 41");
+}
+
+void tst_QmlCppCodegen::bindToValueType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/bindToValueType.qml"_s));
+ checkColorProperties(&component);
+}
+
+void tst_QmlCppCodegen::bindingExpression()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BindingExpression.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
+
+ double width = 200;
+ double y = 10;
+ for (int i = 0; i < 10; ++i) {
+ QCOMPARE(object->property("width").toDouble(), width);
+ QCOMPARE(object->property("height").toDouble(), width);
+ QCOMPARE(object->property("y").toDouble(), y);
- const auto func = qmlAttachedPropertiesFunction(
- object.data(), QMetaType::fromName("QQuickLayout*").metaObject());
+ const double childY = y + (width - 100) / 2;
+ QCOMPARE(child->property("y").toDouble(), childY);
+ QCOMPARE(object->property("mass"), childY > 100 ? u"heavy"_s : u"light"_s);
+ QCOMPARE(object->property("test_division").toDouble(), width / 1000 + 50);
+ QCOMPARE(object->property("test_ternary").toDouble(), 2.2);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/enumsInOtherObject.qml:4:25: "
- "QML Enums: Layout must be attached to Item elements");
- QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
+ const int test_switch = object->property("test_switch").toInt();
+ switch (int(width) % 3) {
+ case 0:
+ QCOMPARE(test_switch, 130);
+ break;
+ case 1:
+ QCOMPARE(test_switch, 380);
+ break;
+ case 2:
+ QCOMPARE(test_switch, 630);
+ break;
+ }
- const QVariant prop = attached->property("alignment");
- QVERIFY(prop.isValid());
- QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter);
+ width = 200 * i;
+ y = 10 + i;
+ object->setProperty("width", width);
+ object->setProperty("y", y);
}
- {
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumsInOtherObject.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("color").toString(), u"blue"_s);
- QTRY_COMPARE(object->property("color").toString(), u"green"_s);
+}
+
+void tst_QmlCppCodegen::blockComments()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/blockComments.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("implicitHeight").toDouble(), 8.0);
+}
+
+void tst_QmlCppCodegen::boolCoercions()
+{
+ QQmlEngine e;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/boolCoercions.qml"_s);
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":41:5: Unable to assign [undefined] to bool"_L1));
+ QScopedPointer<QObject> o(c.create());
+
+ for (char p = '1'; p <= '8'; ++p) {
+ const QVariant t = o->property(qPrintable(QLatin1String("t%1").arg(p)));
+ QCOMPARE(t.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(t.toBool());
+ }
+
+ for (char p = '1'; p <= '5'; ++p) {
+ const QVariant f = o->property(qPrintable(QLatin1String("f%1").arg(p)));
+ QCOMPARE(f.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(!f.toBool());
}
}
-void tst_QmlCppCodegen::funcWithParams()
+void tst_QmlCppCodegen::boolPointerMerge()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/boolPointerMerge.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QObject *item = o->property("item").value<QObject *>();
+ QVERIFY(item);
+ QCOMPARE(item->property("ppp").toInt(), -99);
+}
+
+void tst_QmlCppCodegen::boundComponents()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/funcWithParams.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("bar").toInt(), 30);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/boundComponents.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QObject *c1o = o->property("o").value<QObject *>();
+ QVERIFY(c1o != nullptr);
+ QCOMPARE(c1o->objectName(), u"bar"_s);
+
+ QObject *c2o = c1o->property("o").value<QObject *>();
+ QVERIFY(c2o != nullptr);
+ QCOMPARE(c2o->objectName(), u"bar12"_s);
}
-void tst_QmlCppCodegen::intOverflow()
+void tst_QmlCppCodegen::callContextPropertyLookupResult()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intOverflow.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("a").toDouble(), 1.09951162778e+12);
- QCOMPARE(object->property("b").toInt(), 5);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callContextPropertyLookupResult.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr);
}
-void tst_QmlCppCodegen::stringLength()
+void tst_QmlCppCodegen::callWithSpread()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringLength.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("stringLength").toInt(), 8);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callWithSpread.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtCriticalMsg, "That is great!");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
}
-void tst_QmlCppCodegen::scopeVsObject()
+void tst_QmlCppCodegen::colorAsVariant()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeVsObject.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorAsVariant.qml"_s));
+ checkColorProperties(&component);
+}
+
+void tst_QmlCppCodegen::colorString()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorString.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(qvariant_cast<QColor>(rootObject->property("c")), QColor::fromRgb(0xdd, 0xdd, 0xdd));
+ QCOMPARE(qvariant_cast<QColor>(rootObject->property("d")), QColor::fromRgb(0xaa, 0xaa, 0xaa));
+ QCOMPARE(qvariant_cast<QColor>(rootObject->property("e")), QColor::fromRgb(0x11, 0x22, 0x33));
+}
+
+void tst_QmlCppCodegen::compareOriginals()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compareOriginals.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("objectName").toString(), u"foobar"_s);
+
+ QCOMPARE(object->property("compareOriginals").toInt(), 5);
+ QVERIFY(object->property("optionalThis").toBool());
}
-void tst_QmlCppCodegen::compositeTypeMethod()
+void tst_QmlCppCodegen::comparisonTypes()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositeTypeMethod.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/comparisonTypes.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QSignalSpy spy(object.data(), SIGNAL(foo()));
- QTRY_VERIFY(spy.size() > 0);
+
+ QCOMPARE(object->property("found").toInt(), 1);
+ QCOMPARE(object->property("foundStrict").toInt(), 0);
+
+ QCOMPARE(object->property("foundNot").toInt(), 2);
+ QCOMPARE(object->property("foundNotStrict").toInt(), 10);
}
-void tst_QmlCppCodegen::excessiveParameters()
+void tst_QmlCppCodegen::componentReturnType()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/excessiveParameters.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/componentReturnType.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QSignalSpy spy(object.data(), SIGNAL(foo()));
- QTRY_VERIFY(spy.size() > 0);
+
+ QCOMPARE(object->property("count").toInt(), 10);
+ QCOMPARE(QQmlListReference(object.data(), "children").count(), 11);
}
-void tst_QmlCppCodegen::jsImport()
+void tst_QmlCppCodegen::compositeSingleton()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsimport.qml"_s));
+ engine.addImportPath(u":/qt/qml/TestTypes/imports/"_s);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositesingleton.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("value").toInt(), 42);
+ QScopedPointer<QObject> o(component.create());
+ QCOMPARE(o->property("x").toDouble(), 4.5);
+ QCOMPARE(o->property("y").toDouble(), 10.0);
+ QCOMPARE(o->property("smooth").toBool(), true);
}
-void tst_QmlCppCodegen::jsmoduleImport()
+void tst_QmlCppCodegen::compositeTypeMethod()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsmoduleimport.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositeTypeMethod.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("ok").toBool(), true);
- QVariant okFunc = object->property("okFunc");
- QCOMPARE(okFunc.metaType(), QMetaType::fromType<QJSValue>());
- QJSValue val = engine.toScriptValue(okFunc);
- QJSValue result = val.call();
- QVERIFY(result.isBool());
- QVERIFY(result.toBool());
+ QSignalSpy spy(object.data(), SIGNAL(foo()));
+ QTRY_VERIFY(spy.size() > 0);
}
-void tst_QmlCppCodegen::methods()
+void tst_QmlCppCodegen::consoleObject()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/methods.qml"_s));
- QVERIFY(component.isReady());
-
- QTest::ignoreMessage(QtDebugMsg, "The Bar");
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_s));
- QScopedPointer<QObject> obj(component.create());
- QVERIFY(obj);
- BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data()));
-
- QVERIFY(party && party->host());
- QCOMPARE(party->guestCount(), 5);
+ static const QString urlString = u"qrc:/qt/qml/TestTypes/consoleObject.qml"_s;
+ QQmlComponent c(&engine, QUrl(urlString));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- bool foundGreen = false;
- bool foundFoo = false;
- for (int ii = 0; ii < party->guestCount(); ++ii) {
- if (party->guest(ii)->name() == u"William Green"_s)
- foundGreen = true;
- if (party->guest(ii)->name() == u"The Foo"_s)
- foundFoo = true;
- }
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtInfoMsg, "b 4.55");
+ QTest::ignoreMessage(QtWarningMsg, "b 4.55");
+ QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
- QVERIFY(foundGreen);
- QVERIFY(foundFoo);
+ // Unfortunately we cannot check the logging category with QTest::ignoreMessage
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtInfoMsg, "b 4.55");
+ QTest::ignoreMessage(QtWarningMsg, "b 4.55");
+ QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
- QCOMPARE(obj->property("n1").toString(), u"onGurk"_s);
- QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_s);
- QCOMPARE(obj->property("n3"), QVariant());
+ const QRegularExpression re(u"QQmlComponentAttached\\(0x[0-9a-f]+\\) b 4\\.55"_s);
+ QTest::ignoreMessage(QtDebugMsg, re);
+ QTest::ignoreMessage(QtDebugMsg, re);
+ QTest::ignoreMessage(QtInfoMsg, re);
+ QTest::ignoreMessage(QtWarningMsg, re);
+ QTest::ignoreMessage(QtCriticalMsg, re);
- {
- QVariant ret;
- obj->metaObject()->invokeMethod(obj.data(), "retrieveVar", Q_RETURN_ARG(QVariant, ret));
- QCOMPARE(ret.typeId(), QMetaType::QString);
- QCOMPARE(ret.toString(), u"Jack Smith"_s);
- }
+ QTest::ignoreMessage(QtDebugMsg, "a undefined b false null 7");
+ QTest::ignoreMessage(QtDebugMsg, "");
+ QTest::ignoreMessage(QtDebugMsg, "4");
+ QTest::ignoreMessage(QtDebugMsg, "");
- {
- QString ret;
- obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret));
- QCOMPARE(ret, u"Jack Smith"_s);
- }
+ const QRegularExpression re2(u"QQmlComponentAttached\\(0x[0-9a-f]+\\)"_s);
+ QTest::ignoreMessage(QtDebugMsg, re2);
- QCOMPARE(party->host()->shoeSize(), 12);
- obj->metaObject()->invokeMethod(obj.data(), "storeElement");
- QCOMPARE(party->host()->shoeSize(), 13);
- QJSManagedValue v = engine.toManagedValue(obj->property("dresses"));
- QVERIFY(v.isArray());
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QJSManagedValue inner(v.property(2), &engine);
- QVERIFY(inner.isArray());
- QCOMPARE(inner.property(0).toInt(), 1);
- QCOMPARE(inner.property(1).toInt(), 2);
- QCOMPARE(inner.property(2).toInt(), 3);
+ auto oldHandler = qInstallMessageHandler(
+ [](QtMsgType, const QMessageLogContext &ctxt, const QString &) {
+ QCOMPARE(ctxt.file, urlString.toUtf8());
+ QCOMPARE(ctxt.function, QByteArray("expression for onCompleted"));
+ QVERIFY(ctxt.line > 0);
+ });
+ const auto guard = qScopeGuard([oldHandler]() { qInstallMessageHandler(oldHandler); });
- QCOMPARE(obj->property("enumValue").toInt(), 2);
+ QScopedPointer<QObject> p(c.create());
+ QVERIFY(!p.isNull());
}
-void tst_QmlCppCodegen::math()
+void tst_QmlCppCodegen::consoleTrace()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/math.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleTrace.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+#if !defined(QT_NO_DEBUG) || defined(QT_TEST_FORCE_INTERPRETER)
+ // All line numbers in debug mode or when interpreting
+
+ QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6)
+b (qrc:/qt/qml/TestTypes/consoleTrace.qml:5)
+a (qrc:/qt/qml/TestTypes/consoleTrace.qml:4)
+expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml:7))");
+#else
+ // Only top-most line number otherwise
+
+ QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6)
+b (qrc:/qt/qml/TestTypes/consoleTrace.qml)
+a (qrc:/qt/qml/TestTypes/consoleTrace.qml)
+expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml))");
+#endif
+
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("a").toInt(), 9);
- QCOMPARE(object->property("b").toDouble(), 50.0 / 22.0);
}
-void tst_QmlCppCodegen::unknownParameter()
+void tst_QmlCppCodegen::construct()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownParameter.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/construct.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("cppProp").toInt(), 18);
+
+ const QJSManagedValue v = engine.toManagedValue(object->property("foo"));
+ QVERIFY(v.isError());
+ QCOMPARE(v.toString(), u"Error: bar"_s);
+
+ QCOMPARE(object->property("aaa").toInt(), 12);
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/construct.qml:9: Error: ouch");
+ object->metaObject()->invokeMethod(object.data(), "ouch");
+ QCOMPARE(object->property("aaa").toInt(), 13);
}
-void tst_QmlCppCodegen::array()
+void tst_QmlCppCodegen::contextParam()
{
+ // The compiler cannot resolve context parameters.
+ // Make sure the binding is interpreted.
+
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/array.qml"_s));
+
+ QVariantMap m;
+ m.insert(u"foo"_s, 10);
+ engine.rootContext()->setContextProperty(u"contextParam"_s, m);
+
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/contextParam.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- const QJSValue value1 = object->property("values1").value<QJSValue>();
- QVERIFY(value1.isArray());
- QCOMPARE(value1.property(u"length"_s).toInt(), 3);
- QCOMPARE(value1.property(0).toInt(), 1);
- QCOMPARE(value1.property(1).toInt(), 2);
- QCOMPARE(value1.property(2).toInt(), 3);
- const QJSValue value2 = object->property("values2").value<QJSValue>();
- QVERIFY(value2.isArray());
- QCOMPARE(value2.property(u"length"_s).toInt(), 0);
+ QCOMPARE(object->property("foo").toInt(), 10);
}
-void tst_QmlCppCodegen::equalsUndefined()
+void tst_QmlCppCodegen::conversionDecrement()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalsUndefined.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionDecrement.qml"_s));
- QCOMPARE(object->property("a").toInt(), 50);
- QCOMPARE(object->property("b").toInt(), 5000);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("currentPageIndex").toInt(), 0);
+ o->setProperty("pages", 5);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+ o->setProperty("pages", 4);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 0);
+ o->setProperty("pages", 6);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 4);
+ o->setProperty("pages", 60);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+}
+
+void tst_QmlCppCodegen::conversionInDeadCode()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionInDeadCode.qml"_s));
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toInt(), -4);
+ QCOMPARE(o->property("b").toInt(), 12);
+ QCOMPARE(o->property("c").toInt(), 10);
+ QCOMPARE(o->property("d").toInt(), 10);
+ QCOMPARE(o->property("e").toInt(), 10);
+ QCOMPARE(o->property("f").toInt(), 20);
+ QCOMPARE(o->property("g").toInt(), 33);
}
void tst_QmlCppCodegen::conversions()
@@ -699,157 +1358,260 @@ void tst_QmlCppCodegen::conversions()
QVERIFY(!undef.isValid());
}
-void tst_QmlCppCodegen::interestingFiles_data()
+void tst_QmlCppCodegen::convertPrimitiveToVar()
{
- QTest::addColumn<QString>("file");
- QTest::addColumn<bool>("isValid");
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertPrimitiveToVar.qml"_s));
- QTest::addRow("conversions2") << u"conversions2.qml"_s << true;
- QTest::addRow("TestCase") << u"TestCase.qml"_s << true;
- QTest::addRow("layouts") << u"layouts.qml"_s << true;
- QTest::addRow("interactive") << u"interactive.qml"_s << true;
- QTest::addRow("Panel") << u"Panel.qml"_s << true;
- QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_s << true;
- QTest::addRow("Root") << u"ProgressBar/Root.qml"_s << true;
- QTest::addRow("noscope") << u"noscope.qml"_s << true;
- QTest::addRow("dynamicscene") << u"dynamicscene.qml"_s << true;
- QTest::addRow("curlygrouped") << u"curlygrouped.qml"_s << true;
- QTest::addRow("cycleHead") << u"cycleHead.qml"_s << false;
- QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_s << true;
- QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_s << true;
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("offsetValue").toInt(), 41);
}
-void tst_QmlCppCodegen::interestingFiles()
+void tst_QmlCppCodegen::convertQJSPrimitiveValueToIntegral()
{
- QFETCH(QString, file);
- QFETCH(bool, isValid);
-
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/%1"_s.arg(file)));
- if (isValid) {
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- } else {
- QVERIFY(component.isError());
- }
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertQJSPrimitiveValueToIntegral.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
}
-void tst_QmlCppCodegen::extendedTypes()
+void tst_QmlCppCodegen::convertToOriginalReadAcumulatorForUnaryOperators()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/extendedTypes.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
-
- QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30");
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertToOriginalReadAcumulatorForUnaryOperators.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
- QCOMPARE(object->property("a").toInt(), 6);
- QCOMPARE(qvariant_cast<QSizeF>(object->property("b")), QSizeF(10, 20));
- QCOMPARE(object->property("c").toInt(), 30);
- QCOMPARE(object->property("d").toString(), u"QSizeF(10, 20)"_s);
+void tst_QmlCppCodegen::cppMethodListReturnType()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/CppMethodListReturnType.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QCOMPARE(object->property("e").toInt(), 2);
+ QCOMPARE(o->property("list").toList()[2].toInt(), 2);
}
-void tst_QmlCppCodegen::construct()
+void tst_QmlCppCodegen::cppValueTypeList()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/construct.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
-
- const QJSManagedValue v = engine.toManagedValue(object->property("foo"));
- QVERIFY(v.isError());
- QCOMPARE(v.toString(), u"Error: bar"_s);
+ QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
+ QCOMPARE(object->property("a").toInt(), 16);
+ QMetaObject::invokeMethod(object.data(), "incA");
+ QCOMPARE(object->property("a").toInt(), 17);
- QCOMPARE(object->property("aaa").toInt(), 12);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/construct.qml:9: Error: ouch");
- object->metaObject()->invokeMethod(object.data(), "ouch");
- QCOMPARE(object->property("aaa").toInt(), 13);
+ QCOMPARE(object->property("b").toDouble(), 0.25);
+ QMetaObject::invokeMethod(object.data(), "incB");
+ QCOMPARE(object->property("b").toDouble(), 13.5);
}
-void tst_QmlCppCodegen::contextParam()
+void tst_QmlCppCodegen::dateConstruction()
{
- // The compiler cannot resolve context parameters.
- // Make sure the binding is interpreted.
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConstruction.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QDateTime now = QDateTime::currentDateTime();
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->property("now").value<QDateTime>().toMSecsSinceEpoch() >= now.toMSecsSinceEpoch());
+ QCOMPARE(o->property("now2"), o->property("now"));
+ QCOMPARE(o->property("fromString").value<QDateTime>(),
+ QDateTime(QDate(1995, 12, 17), QTime(3, 24), QTimeZone::LocalTime));
+ QCOMPARE(o->property("fromNumber").value<QDateTime>().toMSecsSinceEpoch(), 777);
+ QCOMPARE(o->property("fromPrimitive").value<QDateTime>().toMSecsSinceEpoch(), 57);
+ o->setObjectName("foo"_L1);
+ QCOMPARE(o->property("fromPrimitive").value<QDateTime>(),
+ QDateTime(QDate(1997, 2, 13), QTime(13, 4, 12), QTimeZone::LocalTime));
+
+ QCOMPARE(o->property("from2").value<QDateTime>(),
+ QDateTime(QDate(1996, 2, 1), QTime(), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from3").value<QDateTime>(),
+ QDateTime(QDate(1996, 3, 3), QTime(), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from4").value<QDateTime>(),
+ QDateTime(QDate(1996, 4, 4), QTime(5, 0), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from5").value<QDateTime>(),
+ QDateTime(QDate(1996, 5, 5), QTime(6, 7), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from6").value<QDateTime>(),
+ QDateTime(QDate(1996, 6, 6), QTime(7, 8, 9), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from7").value<QDateTime>(),
+ QDateTime(QDate(1996, 7, 7), QTime(8, 9, 10, 11), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from8").value<QDateTime>(),
+ QDateTime(QDate(1996, 8, 8), QTime(9, 10, 11, 12), QTimeZone::LocalTime));
+
+ QCOMPARE(o->property("withUnderflow").value<QDateTime>(),
+ QDateTime(QDate(-6, 7, 24), QTime(16, 51, 50, 990), QTimeZone::LocalTime));
+ QCOMPARE(o->property("invalid").value<QDateTime>(), QDateTime());
+}
+void tst_QmlCppCodegen::dateConversions()
+{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- QVariantMap m;
- m.insert(u"foo"_s, 10);
- engine.rootContext()->setContextProperty(u"contextParam"_s, m);
+ Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug");
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/contextParam.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate());
+ const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime());
+
+ QCOMPARE(o->property("date").value<QDateTime>(), refDate);
+ QCOMPARE(o->property("time").value<QDateTime>(), refTime);
+
+ QCOMPARE(o->property("dateString").toString(),
+ (engine.coerceValue<QDateTime, QString>(refDate)));
+ QCOMPARE(o->property("dateNumber").toDouble(),
+ (engine.coerceValue<QDateTime, double>(refDate)));
+ QCOMPARE(o->property("timeString").toString(),
+ (engine.coerceValue<QDateTime, QString>(refTime)));
+ QCOMPARE(o->property("timeNumber").toDouble(),
+ (engine.coerceValue<QDateTime, double>(refTime)));
+
+ QMetaObject::invokeMethod(o.data(), "shuffle");
+
+ QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate)));
+ QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime)));
+
+ const QDate date = ref->myDate();
+ const QTime time = ref->myTime();
+
+ QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date)));
+ QCOMPARE(o->property("dateNumber").toDouble(), (engine.coerceValue<QDate, double>(date)));
+ QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time)));
+ QCOMPARE(o->property("timeNumber").toDouble(), (engine.coerceValue<QTime, double>(time)));
+
+ QMetaObject::invokeMethod(o.data(), "fool");
+
+ QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time)));
+ QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date)));
+
+ QMetaObject::invokeMethod(o.data(), "invalidate");
+ QMetaObject::invokeMethod(o.data(), "shuffle");
+
+ QCOMPARE(o->property("dateString").toString(), "Invalid Date"_L1);
+ QVERIFY(qIsNaN(o->property("dateNumber").toDouble()));
+ QCOMPARE(o->property("timeString").toString(), "Invalid Date"_L1);
+ QVERIFY(qIsNaN(o->property("timeNumber").toDouble()));
- QCOMPARE(object->property("foo").toInt(), 10);
}
-void tst_QmlCppCodegen::attachedType()
+void tst_QmlCppCodegen::deadShoeSize()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/text.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("dayz").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QCOMPARE(object->property("oParty").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
-
- QObject *party = qvariant_cast<QObject *>(object->property("party"));
- QVERIFY(party);
- QCOMPARE(party->property("eee").toInt(), 21);
- QCOMPARE(party->property("fff").toInt(), 33);
- QCOMPARE(object->property("ggg").toInt(), 37);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/deadShoeSize.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/deadShoeSize.qml:5: Error: ouch");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("shoeSize").toInt(), 0);
}
-void tst_QmlCppCodegen::componentReturnType()
+void tst_QmlCppCodegen::dialogButtonBox()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/componentReturnType.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
+ const QUrl copy(u"qrc:/qt/qml/TestTypes/dialogButtonBox.qml"_s);
+ QQmlComponent c(&engine, copy);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QObject *footer = o->property("footer").value<QObject *>();
+ QVERIFY(footer);
- QCOMPARE(object->property("count").toInt(), 10);
- QCOMPARE(QQmlListReference(object.data(), "children").count(), 11);
+ QCOMPARE(footer->property("standardButtons").value<QPlatformDialogHelper::StandardButton>(),
+ QPlatformDialogHelper::Ok | QPlatformDialogHelper::Cancel);
}
-void tst_QmlCppCodegen::onAssignment()
+void tst_QmlCppCodegen::enumConversion()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/pressAndHoldButton.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QCOMPARE(object->property("pressed").toBool(), false);
- QCOMPARE(object->property("scale").toDouble(), 1.0);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- object->metaObject()->invokeMethod(object.data(), "press");
- QTRY_COMPARE(object->property("pressed").toBool(), true);
- QCOMPARE(object->property("scale").toDouble(), 0.9);
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("test").toInt(), 0x04);
+ QCOMPARE(o->property("test_1").toBool(), true);
+ QCOMPARE(o->objectName(), u"0m"_s);
+}
- object->metaObject()->invokeMethod(object.data(), "release");
- QCOMPARE(object->property("pressed").toBool(), false);
- QCOMPARE(object->property("scale").toDouble(), 1.0);
+void tst_QmlCppCodegen::enumFromBadSingleton()
+{
+ QQmlEngine e;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/enumFromBadSingleton.qml"_s);
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+#if QT_DEPRECATED_SINCE(6,4)
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(
+ url.toString()
+ + u":5:5: TypeError: Cannot read property 'TestA' of undefined"_s));
+#else
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(
+ url.toString()
+ + u":5:5: ReferenceError: DummyObjekt is not defined"_s));
+#endif
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QVERIFY(o->objectName().isEmpty());
}
-namespace QmlCacheGeneratedCode {
-namespace _qt_qml_TestTypes_failures_qml {
-extern const QQmlPrivate::TypedFunction aotBuiltFunctions[];
+void tst_QmlCppCodegen::enumLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumLookup.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QCOMPARE(o->property("ready").toBool(), true);
}
+
+void tst_QmlCppCodegen::enumMarkedAsFlag()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumMarkedAsFlag.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QCOMPARE(o->property("flagValue").toInt(), 3);
}
-void tst_QmlCppCodegen::failures()
+void tst_QmlCppCodegen::enumProblems()
{
- const auto &aotFailure
- = QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0];
- QVERIFY(aotFailure.argumentTypes.isEmpty());
- QVERIFY(!aotFailure.functionPtr);
- QCOMPARE(aotFailure.extraData, 0);
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumProblems.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> outer(c.create());
+ QVERIFY(!outer.isNull());
+ QObject *inner = outer->property("o").value<QObject *>();
+ QVERIFY(inner);
+
+ Foo *bar = inner->property("bar").value<Foo *>();
+ QVERIFY(bar);
+ QCOMPARE(bar->type(), Foo::Component);
+
+ Foo *fighter = inner->property("fighter").value<Foo *>();
+ QVERIFY(fighter);
+ QCOMPARE(fighter->type(), Foo::Fighter);
+
+ QCOMPARE(outer->property("a").toInt(), FooFactory::B);
+ QCOMPARE(outer->property("b").toInt(), FooFactory::C);
+ QCOMPARE(outer->property("c").toInt(), FooFactory::D);
+ QCOMPARE(outer->property("d").toInt(), FooFactory::E);
}
void tst_QmlCppCodegen::enumScope()
@@ -861,283 +1623,370 @@ void tst_QmlCppCodegen::enumScope()
QCOMPARE(object->property("flow").toInt(), 1);
}
-void tst_QmlCppCodegen::unusedAttached()
+void tst_QmlCppCodegen::enums()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unusedAttached.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Enums.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/Enums.qml:4:1: "
+ "QML Enums: Layout must be attached to Item elements");
QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ bool ok = false;
+ QCOMPARE(object->property("appState").toInt(&ok), 2);
+ QVERIFY(ok);
+ QCOMPARE(object->property("color").toString(), u"blue"_s);
+
+ QTRY_COMPARE(object->property("appState").toInt(&ok), 1);
+ QVERIFY(ok);
+ QCOMPARE(object->property("color").toString(), u"green"_s);
+
const auto func = qmlAttachedPropertiesFunction(
- object.data(), QMetaType::fromName("QQuickKeyNavigationAttached*").metaObject());
+ object.data(), QMetaType::fromName("QQuickLayout*").metaObject());
+
QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
- const QVariant prop = attached->property("priority");
+
+ const QVariant prop = attached->property("alignment");
QVERIFY(prop.isValid());
- QCOMPARE(QByteArray(prop.metaType().name()), "QQuickKeyNavigationAttached::Priority");
- bool ok = false;
- QCOMPARE(prop.toInt(&ok), 0);
- QVERIFY(ok);
+ QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter);
+
}
-void tst_QmlCppCodegen::attachedBaseEnum()
+void tst_QmlCppCodegen::enforceSignature()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/attachedBaseEnum.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enforceSignature.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QObject *drag = qvariant_cast<QObject *>(object->property("drag"));
- QVERIFY(drag);
+ const QVariant a = object->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(a.value<QObject *>(), nullptr);
- // Drag.YAxis is 2, but we cannot #include it here.
- bool ok = false;
- QCOMPARE(drag->property("axis").toInt(&ok), 2);
- QVERIFY(ok);
+ const QVariant b = object->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(b.value<QObject *>(), nullptr);
}
-void tst_QmlCppCodegen::nullAccess()
+void tst_QmlCppCodegen::enumsInOtherObject()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccess.qml"_s));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/enumsInOtherObject.qml:4:25: "
+ "QML Enums: Layout must be attached to Item elements");
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumsInOtherObject.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
-
- QTest::ignoreMessage(QtWarningMsg,
- "qrc:/qt/qml/TestTypes/nullAccess.qml:4:5: TypeError: "
- "Cannot read property 'width' of null");
- QTest::ignoreMessage(QtWarningMsg,
- "qrc:/qt/qml/TestTypes/nullAccess.qml:5:5: TypeError: "
- "Cannot read property 'height' of null");
- QTest::ignoreMessage(QtWarningMsg,
- "qrc:/qt/qml/TestTypes/nullAccess.qml:6: TypeError: Value is null and "
- "could not be converted to an object");
QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("color").toString(), u"blue"_s);
+ QTRY_COMPARE(object->property("color").toString(), u"green"_s);
+}
- QCOMPARE(object->property("width").toDouble(), 0.0);
- QCOMPARE(object->property("height").toDouble(), 0.0);
+void tst_QmlCppCodegen::equalityQObjects()
+{
+ QQmlEngine engine;
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQObjects.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull() && !c1.isError());
+
+ QVERIFY(object->property("derivedIsNotNull").toBool());
+ QVERIFY(object->property("nullObjectIsNull").toBool());
+ QVERIFY(object->property("nonNullObjectIsNotNull").toBool());
+ QVERIFY(object->property("compareSameObjects").toBool());
+ QVERIFY(object->property("compareDifferentObjects").toBool());
+ QVERIFY(object->property("compareObjectWithNullObject").toBool());
+
+ QVERIFY(object->property("nonStrict_derivedIsNotNull").toBool());
+ QVERIFY(object->property("nonStrict_nullObjectIsNull").toBool());
+ QVERIFY(object->property("nonStrict_nonNullObjectIsNotNull").toBool());
+ QVERIFY(object->property("nonStrict_compareSameObjects").toBool());
+ QVERIFY(object->property("nonStrict_compareDifferentObjects").toBool());
+ QVERIFY(object->property("nonStrict_compareObjectWithNullObject").toBool());
}
-void tst_QmlCppCodegen::interceptor()
+void tst_QmlCppCodegen::equalityQUrl()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/interceptor.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("x").toInt(), 100);
- QCOMPARE(object->property("y").toInt(), 100);
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQUrl.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QVERIFY(object->property("width").toInt() != 200);
- QVERIFY(object->property("height").toInt() != 200);
- QVERIFY(object->property("qProperty1").toInt() != 300);
- QVERIFY(object->property("qProperty2").toInt() != 300);
- QTRY_COMPARE(object->property("width").toInt(), 200);
- QTRY_COMPARE(object->property("height").toInt(), 200);
- QTRY_COMPARE(object->property("qProperty1").toInt(), 300);
- QTRY_COMPARE(object->property("qProperty2").toInt(), 300);
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull() && !c1.isError());
+ QVERIFY(object->property("emptyUrlStrict").toBool());
+ QVERIFY(object->property("emptyUrlWeak").toBool());
+ QVERIFY(object->property("sourceUrlStrict").toBool());
+ QVERIFY(object->property("sourceUrlWeak").toBool());
+ QVERIFY(object->property("sourceIsNotEmptyStrict").toBool());
+ QVERIFY(object->property("sourceIsNotEmptyWeak").toBool());
}
-void tst_QmlCppCodegen::nonNotifyable()
+void tst_QmlCppCodegen::equalityTestsWithNullOrUndefined()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nonNotifyable.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityTestsWithNullOrUndefined.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o);
+}
- QCOMPARE(qvariant_cast<QDateTime>(object->property("dayz")),
- QDateTime(QDate(2121, 1, 12), QTime()));
- QCOMPARE(qvariant_cast<QDateTime>(object->property("oParty")),
- QDateTime(QDate(2111, 12, 11), QTime()));
+void tst_QmlCppCodegen::equalityVarAndNonStorable()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityVarAndNonStorable.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull() && !c1.isError());
+ QVERIFY(!object->property("aIsNull").toBool());
+ QVERIFY(object->property("aIsNotNull").toBool());
+ QVERIFY(object->property("aIsNotUndefined").toBool());
+ QVERIFY(object->property("objectIsNotNull").toBool());
+ QVERIFY(!object->property("typedArrayIsNull").toBool());
+ QVERIFY(object->property("isUndefined").toBool());
+ QVERIFY(!object->property("derivedIsNull").toBool());
+
+ QVERIFY(object->property("primitiveIsNull").toBool());
+ QVERIFY(object->property("primitiveIsDefined").toBool());
+ QVERIFY(object->property("primitiveIsUndefined").toBool());
+
+ QVERIFY(object->property("jsValueIsNull").toBool());
+ QVERIFY(object->property("jsValueIsDefined").toBool());
+ QVERIFY(object->property("jsValueIsUndefined").toBool());
+
+ QVERIFY(object->property("nullVarIsUndefined").toBool());
+ QVERIFY(object->property("nullIsUndefined").toBool());
+ QVERIFY(object->property("nullVarIsNull").toBool());
+ QVERIFY(object->property("nullIsNotUndefined").toBool());
}
-void tst_QmlCppCodegen::importsFromImportPath()
+void tst_QmlCppCodegen::equalityVarAndStorable()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml"_s));
+ QQmlComponent planner(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Planner.qml"_s));
+ QVERIFY2(!planner.isError(), qPrintable(planner.errorString()));
+ QScopedPointer<QObject> p(planner.create());
+ QVERIFY(!p.isNull());
- // We might propagate the import path, eventually, but for now instantiating is not important.
- // If the compiler accepts the file, it's probably fine.
- QVERIFY(component.isError());
- QCOMPARE(component.errorString(),
- u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_s);
+ QQmlComponent variable(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Variable.qml"_s));
+ QVERIFY2(!variable.isError(), qPrintable(variable.errorString()));
+ QScopedPointer<QObject> v(variable.create());
+ QVERIFY(!v.isNull());
+
+ QVERIFY(p->objectName().isEmpty());
+ QMetaObject::invokeMethod(p.data(), "typeErasedRemoveOne", v.data());
+ QCOMPARE(p->objectName(), u"n");
+
+ v->setProperty("value", 1);
+ QMetaObject::invokeMethod(p.data(), "typeErasedRemoveOne", v.data());
+ QCOMPARE(p->objectName(), u"nd");
+
+ QQmlComponent constraint(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BaseConstraint.qml"_s));
+ QVERIFY2(!constraint.isError(), qPrintable(constraint.errorString()));
+ QScopedPointer<QObject> c(constraint.create());
+ QVERIFY(!c.isNull());
+
+ c->setProperty("output", QVariant::fromValue(v.data()));
+ QCOMPARE(v->property("mark").toInt(), 0);
+ QMetaObject::invokeMethod(p.data(), "typeErasedRun", c.data());
+ QCOMPARE(v->property("mark").toInt(), 5);
+
+ QTest::ignoreMessage(QtDebugMsg, "success");
+ QMetaObject::invokeMethod(p.data(), "verify", 10);
+
+ QTest::ignoreMessage(QtCriticalMsg, "failed 10 11");
+ QMetaObject::invokeMethod(p.data(), "verify", 11);
}
-void tst_QmlCppCodegen::aliasLookup()
+void tst_QmlCppCodegen::equalsUndefined()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/aliasLookup.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalsUndefined.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- const QVariant t = object->property("t");
- QCOMPARE(t.metaType(), QMetaType::fromType<QString>());
- QCOMPARE(t.toString(), u"12"_s);
+ QCOMPARE(object->property("a").toInt(), 50);
+ QCOMPARE(object->property("b").toInt(), 5000);
}
-void tst_QmlCppCodegen::outOfBoundsArray()
+void tst_QmlCppCodegen::evadingAmbiguity()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/outOfBounds.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QTest::ignoreMessage(QtDebugMsg, "oob undefined");
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QVERIFY(object->metaObject()->indexOfProperty("oob") > 0);
- QVERIFY(!object->property("oob").isValid());
- const QVariant oob2 = object->property("oob2");
- QCOMPARE(oob2.metaType(), QMetaType::fromType<QObject *>());
- QCOMPARE(oob2.value<QObject *>(), nullptr);
+ // We need to add an import path here because we cannot namespace the implicit import.
+ // The implicit import is what we use for all the other tests, even if we explicitly
+ // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
+ engine.addImportPath(u":/"_s);
+
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous1/Ambiguous.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QScopedPointer<QObject> o1(c1.create());
+ QCOMPARE(o1->objectName(), QStringLiteral("Ambiguous"));
+ QCOMPARE(o1->property("i").toString(), QStringLiteral("Ambiguous1"));
+
+ QQmlComponent c2(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous2/Ambiguous.qml"_s));
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ QScopedPointer<QObject> o2(c2.create());
+ QCOMPARE(o2->objectName(), QStringLiteral("Ambiguous"));
+ QCOMPARE(o2->property("i").toString(), QStringLiteral("Ambiguous2"));
}
-void tst_QmlCppCodegen::compositeSingleton()
+void tst_QmlCppCodegen::exceptionFromInner()
{
QQmlEngine engine;
- engine.addImportPath(u":/qt/qml/TestTypes/imports/"_s);
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositesingleton.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/exceptionFromInner.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> o(component.create());
- QCOMPARE(o->property("x").toDouble(), 4.5);
- QCOMPARE(o->property("y").toDouble(), 10.0);
- QCOMPARE(o->property("smooth").toBool(), true);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/exceptionFromInner.qml:7: TypeError: "
+ "Cannot read property 'objectName' of null");
+ QMetaObject::invokeMethod(object.data(), "disbelieveFail");
}
-void tst_QmlCppCodegen::lotsOfRegisters()
+void tst_QmlCppCodegen::excessiveParameters()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/page.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/excessiveParameters.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
+ QSignalSpy spy(object.data(), SIGNAL(foo()));
+ QTRY_VERIFY(spy.size() > 0);
+}
- const auto compare = [&]() {
- const qreal implicitBackgroundWidth = object->property("implicitBackgroundWidth").toDouble();
- const qreal leftInset = object->property("leftInset").toDouble();
- const qreal rightInset = object->property("rightInset").toDouble();
- const qreal contentWidth = object->property("contentWidth").toDouble();
- const qreal leftPadding = object->property("leftPadding").toDouble();
- const qreal rightPadding = object->property("rightPadding").toDouble();
- const qreal implicitFooterWidth = object->property("implicitFooterWidth").toDouble();
- const qreal implicitHeaderWidth = object->property("implicitHeaderWidth").toDouble();
+void tst_QmlCppCodegen::extendedTypes()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/extendedTypes.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- const qreal implicitWidth = object->property("implicitWidth").toDouble();
- QCOMPARE(implicitWidth, qMax(qMax(implicitBackgroundWidth + leftInset + rightInset,
- contentWidth + leftPadding + rightPadding),
- qMax(implicitHeaderWidth, implicitFooterWidth)));
- };
+ QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- compare();
+ QCOMPARE(object->property("a").toInt(), 6);
+ QCOMPARE(qvariant_cast<QSizeF>(object->property("b")), QSizeF(10, 20));
+ QCOMPARE(object->property("c").toInt(), 30);
+ QCOMPARE(object->property("d").toString(), u"QSizeF(10, 20)"_s);
- const QList<const char *> props = {
- "leftInset", "rightInset", "contentWidth", "leftPadding", "rightPadding"
- };
+ QCOMPARE(object->property("e").toInt(), 2);
+}
- for (int i = 0; i < 100; ++i) {
- QVERIFY(object->setProperty(props[i % props.size()], (i * 17) % 512));
- compare();
- }
+void tst_QmlCppCodegen::failures()
+{
+ const auto &aotFailure
+ = QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0];
+ QVERIFY(!aotFailure.functionPtr);
+ QCOMPARE(aotFailure.functionIndex, 0);
}
-void tst_QmlCppCodegen::inPlaceDecrement()
+void tst_QmlCppCodegen::fallbackLookups()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dialog.qml"_s));
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QObject *header = qvariant_cast<QObject *>(object->property("header"));
- QVERIFY(header);
- QObject *background = qvariant_cast<QObject *>(header->property("background"));
- QObject *parent = qvariant_cast<QObject *>(background->property("parent"));
+ const QUrl document(u"qrc:/qt/qml/TestTypes/fallbacklookups.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
- QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
+ QCOMPARE(o->objectName(), QString());
+ int result = 0;
- QVERIFY(object->setProperty("width", QVariant::fromValue(17)));
- QVERIFY(parent->property("width").toInt() > 0);
- QVERIFY(object->setProperty("height", QVariant::fromValue(53)));
- QVERIFY(parent->property("height").toInt() > 0);
+ QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 16);
+ QCOMPARE(o->objectName(), QStringLiteral("aa93"));
- QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
- QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
+ QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 17);
+ QCOMPARE(o->objectName(), QStringLiteral("bb94"));
- QCOMPARE(object->property("a").toInt(), 1024);
+ QObject *singleton = nullptr;
+ QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton));
+ QVERIFY(singleton);
+
+ QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 18);
+ QCOMPARE(singleton->objectName(), QStringLiteral("cc95"));
+
+ QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 19);
+ QCOMPARE(singleton->objectName(), QStringLiteral("dd96"));
}
-void tst_QmlCppCodegen::shifts()
+void tst_QmlCppCodegen::fileImportsContainCxxTypes()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/shifts.qml"_s));
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
-
- QCOMPARE(object->property("a").toInt(), 9728);
- QCOMPARE(object->property("b").toInt(), 4864);
- QCOMPARE(object->property("c").toInt(), 19448);
- QCOMPARE(object->property("d").toInt(), 9731);
- QCOMPARE(object->property("e").toInt(), 0);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/usingCxxTypesFromFileImports.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"horst guenther"_s);
}
-void tst_QmlCppCodegen::valueTypeProperty()
+void tst_QmlCppCodegen::flagEnum()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeProperty.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/flagEnum.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QFont font = qvariant_cast<QFont>(object->property("font"));
- QCOMPARE(object->property("foo").toString(), font.family());
- font.setFamily(u"Bar"_s);
- object->setProperty("font", QVariant::fromValue(font));
- QCOMPARE(object->property("foo").toString(), u"Bar"_s);
+ QQmlCommunicationPermission *p = qobject_cast<QQmlCommunicationPermission *>(o.data());
+ QCOMPARE(p->communicationModes(), CommunicationPermission::Access);
}
-void tst_QmlCppCodegen::propertyOfParent()
+void tst_QmlCppCodegen::flushBeforeCapture()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/RootWithoutId.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
-
- QObject *child = qmlContext(object.data())->objectForName(u"item"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noBindingLoop.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- bool expected = false;
+ QCOMPARE(o->property("deviation").toDouble(), 9.0 / 3.3333);
+ QCOMPARE(o->property("samples").toInt(), 16);
+ QCOMPARE(o->property("radius").toDouble(), 8.0);
+}
- for (int i = 0; i < 3; ++i) {
- const QVariant foo = object->property("foo");
- QCOMPARE(foo.metaType(), QMetaType::fromType<bool>());
- QCOMPARE(foo.toBool(), expected);
+void tst_QmlCppCodegen::fromBoolValue()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fromBoolValue.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->property("a").toBool(), true);
+ o->setProperty("x", 100);
+ QCOMPARE(o->property("a").toBool(), false);
- const QVariant bar = object->property("bar");
- QCOMPARE(bar.metaType(), QMetaType::fromType<bool>());
- QCOMPARE(bar.toBool(), expected);
+ QCOMPARE(o->property("width").toInt(), 100);
+ QCOMPARE(o->property("b").toBool(), false);
- const QVariant visible = child->property("visible");
- QCOMPARE(visible.metaType(), QMetaType::fromType<bool>());
- QCOMPARE(visible.toBool(), expected);
+ QScopedPointer<QObject> parent(c.create());
+ o->setProperty("parent", QVariant::fromValue(parent.data()));
+ QCOMPARE(o->property("width").toInt(), 100);
+ QCOMPARE(o->property("b").toBool(), false);
- expected = !expected;
- object->setProperty("foo", expected);
- }
+ o->setProperty("state", QVariant::fromValue(u"foo"_s));
+ QCOMPARE(o->property("width").toInt(), 0);
+ QCOMPARE(o->property("b").toBool(), false);
}
-void tst_QmlCppCodegen::accessModelMethodFromOutSide()
+void tst_QmlCppCodegen::funcWithParams()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/AccessModelMethodsFromOutside.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
-
- QTest::ignoreMessage(QtDebugMsg, "3");
- QTest::ignoreMessage(QtDebugMsg, "Apple");
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/funcWithParams.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
-
- QCOMPARE(object->property("cost1").toDouble(), 3);
- QCOMPARE(object->property("name1").toString(), u"Orange"_s);
- QCOMPARE(object->property("cost2").toDouble(), 1.95);
- QCOMPARE(object->property("name2").toString(), u"Banana"_s);
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("bar").toInt(), 30);
}
// QML-generated types have no C++ names, but we want to call a method that
@@ -1150,42 +1999,55 @@ void tst_QmlCppCodegen::accessModelMethodFromOutSide()
// the metatype of the argument we pass does not match the metatype of the
// argument the method expects. In order to work around it, we specialize
// qMetaTypeInterfaceForType() and produce a "correct" metatype this way.
-class Dummy_QMLTYPE_0;
+class Dummy2_QMLTYPE_0;
// We set this to the actual value retrieved from an actual instance of the QML
// type before retrieving the metatype interface for the first time.
static const QtPrivate::QMetaTypeInterface *dummyMetaTypeInterface = nullptr;
template<>
-const QtPrivate::QMetaTypeInterface *QtPrivate::qMetaTypeInterfaceForType<Dummy_QMLTYPE_0 *>() {
+const QtPrivate::QMetaTypeInterface *QtPrivate::qMetaTypeInterfaceForType<Dummy2_QMLTYPE_0 *>() {
return dummyMetaTypeInterface;
}
void tst_QmlCppCodegen::functionArguments()
{
+ qmlClearTypeRegistrations();
+
QQmlEngine engine;
+ QQmlComponent preheat(&engine);
+ preheat.setData(R"(
+ import QtQml
+ import TestTypes
+ QtObject {
+ objectName: Style.objectName
+ }
+ )", QUrl(u"qrc:/qt/qml/TestTypes/preheat.qml"_s));
+ QVERIFY2(preheat.isReady(), qPrintable(preheat.errorString()));
+ QScopedPointer<QObject> hot(preheat.create());
+ QVERIFY(!hot.isNull());
- // Ensure that Dummy gets counter value 0. Don't do that at home
+ // Ensure that Dummy gets counter value 1. Don't do that at home
QScopedValueRollback<QAtomicInt> rb(QQmlPropertyCacheCreatorBase::classIndexCounter, 0);
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Dummy.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Dummy2.qml"_s));
QVERIFY2(component.isReady(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
const QMetaObject *metaObject = object->metaObject();
dummyMetaTypeInterface = metaObject->metaType().iface();
const QByteArray className = QByteArray(metaObject->className());
- QCOMPARE(className, "Dummy_QMLTYPE_0");
+ QCOMPARE(className, "Dummy2_QMLTYPE_0");
int result;
int a = 1;
bool b = false;
- Dummy_QMLTYPE_0 *c = nullptr;
+ Dummy2_QMLTYPE_0 *c = nullptr;
double d = -1.2;
int e = 3;
metaObject->invokeMethod(
object.data(), "someFunction", Q_RETURN_ARG(int, result),
- Q_ARG(int, a), Q_ARG(bool, b), Q_ARG(Dummy_QMLTYPE_0 *, c),
+ Q_ARG(int, a), Q_ARG(bool, b), Q_ARG(Dummy2_QMLTYPE_0 *, c),
Q_ARG(double, d), Q_ARG(int, e));
QCOMPARE(result, 42);
@@ -1198,473 +2060,780 @@ void tst_QmlCppCodegen::functionArguments()
QCOMPARE(concatenated, u"foobar"_s);
}
-void tst_QmlCppCodegen::bindingExpression()
+void tst_QmlCppCodegen::functionCallOnNamespaced()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BindingExpression.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
-
- QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
-
- double width = 200;
- double y = 10;
- for (int i = 0; i < 10; ++i) {
- QCOMPARE(object->property("width").toDouble(), width);
- QCOMPARE(object->property("height").toDouble(), width);
- QCOMPARE(object->property("y").toDouble(), y);
-
- const double childY = y + (width - 100) / 2;
- QCOMPARE(child->property("y").toDouble(), childY);
- QCOMPARE(object->property("mass"), childY > 100 ? u"heavy"_s : u"light"_s);
- QCOMPARE(object->property("test_division").toDouble(), width / 1000 + 50);
- QCOMPARE(object->property("test_ternary").toDouble(), 2.2);
-
- const int test_switch = object->property("test_switch").toInt();
- switch (int(width) % 3) {
- case 0:
- QCOMPARE(test_switch, 130);
- break;
- case 1:
- QCOMPARE(test_switch, 380);
- break;
- case 2:
- QCOMPARE(test_switch, 630);
- break;
- }
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themergood.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("i").toInt(), 12);
+ }
- width = 200 * i;
- y = 10 + i;
- object->setProperty("width", width);
- object->setProperty("y", y);
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themerbad.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(5.0, 10.0, 1.0, 1.0)));
}
}
-void tst_QmlCppCodegen::voidFunction()
+void tst_QmlCppCodegen::functionLookup()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/voidfunction.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QVERIFY(object->objectName().isEmpty());
- object->metaObject()->invokeMethod(object.data(), "doesNotReturnValue");
- QCOMPARE(object->objectName(), u"barbar"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionLookup.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ const QVariant foo = o->property("bar");
+ QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>());
+ const QJSManagedValue method(engine.toScriptValue(foo), &engine);
+ QVERIFY(method.isFunction());
+ const QJSValue result = method.call();
+ QVERIFY(result.isString());
+ QCOMPARE(result.toString(), QStringLiteral("a99"));
}
-void tst_QmlCppCodegen::overriddenProperty()
+void tst_QmlCppCodegen::functionReturningVoid()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/childobject.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->objectName(), u"kraut"_s);
- QCOMPARE(object->property("doneThing").toInt(), 5);
- QCOMPARE(object->property("usingFinal").toInt(), 5);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionReturningVoid.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- auto checkAssignment = [&]() {
- const QString newName = u"worscht"_s;
- QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName));
- QCOMPARE(object->objectName(), newName);
- };
- checkAssignment();
+ // It should be able to call the methods and wrap the void values into invalid QVariants,
+ // without crashing.
+ QVERIFY(o->metaObject()->indexOfProperty("aa") >= 0);
+ QVERIFY(o->metaObject()->indexOfProperty("bb") >= 0);
+ QVERIFY(!o->property("aa").isValid());
+ QVERIFY(!o->property("bb").isValid());
+}
- ObjectWithMethod *benign = new ObjectWithMethod(object.data());
- benign->theThing = 10;
- benign->setObjectName(u"cabbage"_s);
- object->setProperty("child", QVariant::fromValue(benign));
- QCOMPARE(object->objectName(), u"cabbage"_s);
- checkAssignment();
- QCOMPARE(object->property("doneThing").toInt(), 10);
- QCOMPARE(object->property("usingFinal").toInt(), 10);
+void tst_QmlCppCodegen::functionTakingVar()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/functionTakingVar.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- OverriddenObjectName *evil = new OverriddenObjectName(object.data());
- QTest::ignoreMessage(QtWarningMsg,
- "Final member fff is overridden in class OverriddenObjectName. "
- "The override won't be used.");
- object->setProperty("child", QVariant::fromValue(evil));
+ QVERIFY(!o->property("c").isValid());
- QCOMPARE(object->objectName(), u"borschtsch"_s);
+ int value = 11;
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine);
+ void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) };
+ QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() };
+ e->executeRuntimeFunction(document, 0, o.data(), 1, args, types);
- checkAssignment();
- QCOMPARE(object->property("doneThing").toInt(), 7);
- QCOMPARE(object->property("usingFinal").toInt(), 5);
+ QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
}
-void tst_QmlCppCodegen::listLength()
+void tst_QmlCppCodegen::getLookupOfScript()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listlength.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("l").toInt(), 2);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/NotificationItem.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"heading"_s);
}
-void tst_QmlCppCodegen::parentProperty()
+void tst_QmlCppCodegen::getOptionalLookup_data()
{
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/parentProp.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("c").toInt(), 11);
- QCOMPARE(object->property("i").toInt(), 22);
- object->setProperty("a", QVariant::fromValue(22));
- QCOMPARE(object->property("c").toInt(), 28);
- object->setProperty("implicitWidth", QVariant::fromValue(14));
- QCOMPARE(object->property("i").toInt(), 26);
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QVariant>("expected");
- QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
- QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_s);
- QObject *evil = qmlContext(object.data())->objectForName(u"evil"_s);
+ // Objects
+ QTest::addRow("int on object") << u"to1"_s << QVariant(5);
+ QTest::addRow("string on object") << u"to2"_s << QVariant("6");
+ QTest::addRow("object on object") << u"to3"_s << QVariant::fromValue<QObject *>(nullptr);
+ QTest::addRow("int on null") << u"to4"_s << QVariant(); // undefined
+ QTest::addRow("any on undefined as object") << u"to5"_s << QVariant(); // undefined
+ QTest::addRow("int on string on object") << u"to6"_s << QVariant(1);
- child->setProperty("parent", QVariant::fromValue(sibling));
+ // Value Types
+ QTest::addRow("int on rect") << u"tv1"_s << QVariant(50);
+ QTest::addRow("int on point") << u"tv2"_s << QVariant(-10);
+ QTest::addRow("int on undefined as point") << u"tv4"_s << QVariant(); // undefined
- QCOMPARE(child->property("b").toInt(), 0);
- QCOMPARE(child->property("i").toInt(), 28);
- QCOMPARE(object->property("i").toInt(), 56);
+ // Enums
+ QTest::addRow("enum on object") << u"te1"_s << QVariant(1);
+ QTest::addRow("enum on type") << u"te2"_s << QVariant(1);
+ QTest::addRow("enums comparison 1") << u"te3"_s << QVariant(false);
+ QTest::addRow("enums comparison 2") << u"te4"_s << QVariant(true);
- child->setProperty("parent", QVariant::fromValue(evil));
+ // Complex chains
+ QTest::addRow("mixed 1") << u"tc1"_s << QVariant(-10);
+ QTest::addRow("mixed 2") << u"tc2"_s << QVariant(0);
+ QTest::addRow("early out 1") << u"tc4"_s << QVariant(); // undefined
+ QTest::addRow("early out 2") << u"tc5"_s << QVariant(); // undefined
+}
- QCOMPARE(child->property("b").toInt(), 5994);
- QCOMPARE(object->property("c").toInt(), 5996);
+void tst_QmlCppCodegen::getOptionalLookup()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/getOptionalLookup.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(child->property("i").toInt(), 443);
- QCOMPARE(object->property("i").toInt(), 886);
+ QFETCH(QString, propertyName);
+ QFETCH(QVariant, expected);
- {
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/specificParent.qml"_s));
+ QVariant actual = o->property(propertyName.toLocal8Bit());
+ QCOMPARE(actual, expected);
+}
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+void tst_QmlCppCodegen::getOptionalLookupOnQJSValueNonStrict()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/GetOptionalLookupOnQJSValueNonStrict.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(rootObject->property("a").toReal(), 77.0);
- }
+ QVERIFY(o->property("b").toBool());
}
-void tst_QmlCppCodegen::registerElimination()
+void tst_QmlCppCodegen::getOptionalLookupShadowed()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerelimination.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ const QUrl document(u"qrc:/qt/qml/TestTypes/GetOptionalLookupShadowed.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->property("res").toString(), "a");
+}
+
+void tst_QmlCppCodegen::globals()
+{
+ QQmlEngine engine;
+ int exitCode = -1;
+ QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; });
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/globals.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ const QByteArray message = QByteArray("Start 2 ") + arg1();
+ QTest::ignoreMessage(QtDebugMsg, message.constData());
+
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
+ QTRY_COMPARE(exitCode, 0);
- // Increment of 23 hits both 0 and 460
- for (int input = -23; input < 700; input += 23) {
- object->setProperty("input", input);
- if (input <= 0 || input >= 460)
- QCOMPARE(object->property("output").toInt(), 459);
- else
- QCOMPARE(object->property("output").toInt(), input);
- }
+ QObject *application = qvariant_cast<QObject *>(object->property("application"));
+ QVERIFY(application);
+ QCOMPARE(QString::fromUtf8(application->metaObject()->className()),
+ u"QQuickApplication"_s);
+
+ QTest::ignoreMessage(QtDebugMsg, "End");
+ QMetaObject::invokeMethod(application, "aboutToQuit");
+
+ const QVariant somewhere = object->property("somewhere");
+ QCOMPARE(somewhere.userType(), QMetaType::QUrl);
+ QCOMPARE(qvariant_cast<QUrl>(somewhere).toString(), u"qrc:/somewhere/else.qml"_s);
+
+ const QVariant somewhereString = object->property("somewhereString");
+ QCOMPARE(somewhereString.userType(), QMetaType::QString);
+ QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_s);
+
+ const QVariant plain = object->property("plain");
+ QCOMPARE(plain.userType(), QMetaType::QUrl);
+ QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_s);
}
-void tst_QmlCppCodegen::asCast()
+void tst_QmlCppCodegen::idAccess()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/asCast.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> root(component.create());
- QVERIFY(!root.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/idAccess.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QQmlContext *context = qmlContext(root.data());
- const QObject *object = context->objectForName(u"object"_s);
- const QObject *item = context->objectForName(u"item"_s);
- const QObject *rectangle = context->objectForName(u"rectangle"_s);
- const QObject *dummy = context->objectForName(u"dummy"_s);
+ QVERIFY(object->property("y").toInt() != 48);
+ QCOMPARE(object->property("y").toInt(), 12);
+ object->setProperty("z", 13);
+ QCOMPARE(object->property("y").toInt(), 13);
+ object->setProperty("x", QVariant::fromValue(333));
+ QCOMPARE(object->property("y").toInt(), 48);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsObject")), object);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsItem")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsRectangle")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsDummy")), nullptr);
+ // The binding was broken by setting the property
+ object->setProperty("z", 14);
+ QCOMPARE(object->property("y").toInt(), 48);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsObject")), item);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsItem")), item);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsRectangle")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsDummy")), nullptr);
+ QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_s);
+ QFont f = qvariant_cast<QFont>(ttt->property("font"));
+ QCOMPARE(f.pointSize(), 22);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsObject")), rectangle);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsItem")), rectangle);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsRectangle")), rectangle);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsDummy")), nullptr);
+ QObject::connect(object.data(), &QObject::objectNameChanged, ttt, [&](){
+ ttt->setParent(nullptr);
+ QJSEngine::setObjectOwnership(ttt, QJSEngine::CppOwnership);
+ object.reset(ttt);
+ });
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsObject")), dummy);
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsItem")), dummy);
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsRectangle")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsDummy")), dummy);
+ QVERIFY(object->objectName().isEmpty());
+ QVERIFY(ttt->objectName().isEmpty());
+ ttt->setProperty("text", u"kill"_s);
+ QCOMPARE(object.data(), ttt);
+ QCOMPARE(ttt->objectName(), u"context"_s);
}
-void tst_QmlCppCodegen::noQQmlData()
+void tst_QmlCppCodegen::ignoredFunctionReturn()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noQQmlData.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ignoredFunctionReturn.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+}
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
- "Cannot read property 'name' of null");
- QScopedPointer<QObject> root(component.create());
- QVERIFY(!root.isNull());
+void tst_QmlCppCodegen::importsFromImportPath()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml"_s));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data());
- QVERIFY(party != nullptr);
+ // We might propagate the import path, eventually, but for now instantiating is not important.
+ // If the compiler accepts the file, it's probably fine.
+ QVERIFY(component.isError());
+ QCOMPARE(component.errorString(),
+ u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_s);
+}
- QCOMPARE(party->host(), nullptr);
- QCOMPARE(party->property("n").toString(), QString());
+void tst_QmlCppCodegen::inPlaceDecrement()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dialog.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *header = qvariant_cast<QObject *>(object->property("header"));
+ QVERIFY(header);
+ QObject *background = qvariant_cast<QObject *>(header->property("background"));
+ QObject *parent = qvariant_cast<QObject *>(background->property("parent"));
- Person *host1 = new Person(party);
- party->setHost(host1);
- QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
- host1->setName(u"Marge"_s);
- QCOMPARE(party->property("n").toString(), u"Marge in da house!"_s);
+ QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
+ QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
- "Cannot read property 'name' of null");
+ QVERIFY(object->setProperty("width", QVariant::fromValue(17)));
+ QVERIFY(parent->property("width").toInt() > 0);
+ QVERIFY(object->setProperty("height", QVariant::fromValue(53)));
+ QVERIFY(parent->property("height").toInt() > 0);
- // Doesn't crash
- party->setHost(nullptr);
+ QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
+ QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
- // Lookups are initialized now, and we introduce an object without QQmlData
- Person *host2 = new Person(party);
- party->setHost(host2);
- QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
- host2->setName(u"Homer"_s);
- QCOMPARE(party->property("n").toString(), u"Homer in da house!"_s);
+ QCOMPARE(object->property("a").toInt(), 1024);
+}
- QMetaObject::invokeMethod(party, "burn");
- engine.collectGarbage();
+void tst_QmlCppCodegen::inaccessibleProperty()
+{
+ QQmlEngine engine;
- // Does not crash
- party->setProperty("inDaHouse", u" burns!"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/versionmismatch.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- // Mr Burns may or may not burn, depending on whether we use lookups.
- // If using lookups, the binding is aborted when we find the isQueuedForDeletion flag.
- // If reading the property directly, we don't have to care about it.
- QVERIFY(party->property("n").toString().startsWith(u"Mr Burns"_s));
+ QCOMPARE(o->property("c").toInt(), 5);
}
-void tst_QmlCppCodegen::scopeObjectDestruction()
+void tst_QmlCppCodegen::indirectlyShadowable()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fileDialog.qml"_s));
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ const QString url = u"qrc:/qt/qml/TestTypes/indirectlyShadowable.qml"_s;
+ QQmlComponent c(&engine, QUrl(url));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QObject *dialog = rootObject->property("dialog").value<QObject *>();
- QVERIFY(dialog);
+ const auto verifyShadowable = [&](const QString &objectName) {
+ QObject *outer = o->property("outer").value<QObject *>();
+ QVERIFY(outer);
+ QObject *inner = outer->property("inner").value<QObject *>();
+ QVERIFY(inner);
+ QObject *shadowable = inner->property("shadowable").value<QObject *>();
+ QVERIFY(shadowable);
+ QCOMPARE(shadowable->objectName(), objectName);
+ };
- // We cannot check the warning messages. The AOT compiled code complains about reading the
- // "parent" property of an object scheduled for deletion. The runtime silently returns undefined
- // at that point and then complains about not being able to read a property on undefined.
+ const auto verifyNotShadowable = [&](const QString &objectName) {
+ QObject *notShadowable = o->property("notShadowable").value<QObject *>();
+ QCOMPARE(notShadowable->objectName(), objectName);
+ };
- // Doesn't crash, even though it triggers bindings on scope objects scheduled for deletion.
- QMetaObject::invokeMethod(dialog, "open");
-}
+ const auto verifyEvil = [&]() {
+ QObject *outer = o->property("outer").value<QObject *>();
+ QVERIFY(outer);
+ QCOMPARE(outer->property("inner").toString(), u"evil"_s);
+ };
-static void checkColorProperties(QQmlComponent *component)
-{
- QVERIFY2(component->isReady(), qPrintable(component->errorString()));
- QScopedPointer<QObject> rootObject(component->create());
- QVERIFY(rootObject);
+ verifyShadowable(u"shadowable"_s);
+ verifyNotShadowable(u"notShadowable"_s);
- const QMetaObject *mo = QMetaType::fromName("QQuickIcon").metaObject();
- QVERIFY(mo != nullptr);
+ QMetaObject::invokeMethod(o.data(), "setInnerShadowable");
- const QMetaProperty prop = mo->property(mo->indexOfProperty("color"));
- QVERIFY(prop.isValid());
+ verifyShadowable(u"self"_s);
+ verifyNotShadowable(u"notShadowable"_s);
- const QVariant a = rootObject->property("a");
- QVERIFY(a.isValid());
+ QMetaObject::invokeMethod(o.data(), "getInnerShadowable");
- const QVariant iconColor = prop.readOnGadget(rootObject->property("icon").data());
- QVERIFY(iconColor.isValid());
+ verifyShadowable(u"self"_s);
+ verifyNotShadowable(u"self"_s);
- const QMetaType colorType = QMetaType::fromName("QColor");
- QVERIFY(colorType.isValid());
+ QMetaObject::invokeMethod(o.data(), "turnEvil");
- QCOMPARE(a.metaType(), colorType);
- QCOMPARE(iconColor.metaType(), colorType);
+ verifyEvil();
+ verifyNotShadowable(u"self"_s);
- QCOMPARE(iconColor, a);
+ // Does not produce an error message because JavaScript.
+ QMetaObject::invokeMethod(o.data(), "setInnerShadowable");
+
+ verifyEvil();
+ verifyNotShadowable(u"self"_s);
+
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":29: Error: Cannot assign [undefined] to QObject*"_s));
+ QMetaObject::invokeMethod(o.data(), "getInnerShadowable");
+
+ verifyEvil();
+ verifyNotShadowable(u"self"_s);
}
-void tst_QmlCppCodegen::colorAsVariant()
+void tst_QmlCppCodegen::infinities()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorAsVariant.qml"_s));
- checkColorProperties(&component);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinities.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity());
+ QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity());
+
+ const double positiveZero = o->property("positiveZero").toDouble();
+ QCOMPARE(positiveZero, 0.0);
+ QVERIFY(!std::signbit(positiveZero));
+
+ const double negativeZero = o->property("negativeZero").toDouble();
+ QCOMPARE(negativeZero, -0.0);
+ QVERIFY(std::signbit(negativeZero));
+
+ QVERIFY(qIsNaN(o->property("naN").toDouble()));
}
-void tst_QmlCppCodegen::bindToValueType()
+void tst_QmlCppCodegen::infinitiesToInt()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/bindToValueType.qml"_s));
- checkColorProperties(&component);
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinitiesToInt.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ const char *props[] = {"a", "b", "c"};
+ for (const char *prop : props) {
+ const QVariant i = o->property(prop);
+ QCOMPARE(i.metaType(), QMetaType::fromType<int>());
+ bool ok = false;
+ QCOMPARE(i.toInt(&ok), 0);
+ QVERIFY(ok);
+ }
}
-void tst_QmlCppCodegen::undefinedResets()
+void tst_QmlCppCodegen::innerObjectNonShadowable()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedResets.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ownProperty.qml"_s));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> rootObject(component.create());
QVERIFY(rootObject);
- Person *person = qobject_cast<Person *>(rootObject.data());
- QVERIFY(person);
- QCOMPARE(person->shoeSize(), 0);
- QCOMPARE(person->name(), u"Marge"_s);
-
- person->setShoeSize(11);
+ QCOMPARE(rootObject->objectName(), u"foo"_s);
+}
- QCOMPARE(person->shoeSize(), 11);
- QCOMPARE(person->name(), u"Bart"_s);
+void tst_QmlCppCodegen::intEnumCompare()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intEnumCompare.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("a").toBool(), true);
+ QCOMPARE(o->property("b").toBool(), false);
+ QCOMPARE(o->property("c").toBool(), true);
+ QCOMPARE(o->property("d").toBool(), false);
+ }
- person->setShoeSize(10);
- QCOMPARE(person->shoeSize(), 10);
- QCOMPARE(person->name(), u"Marge"_s);
+ {
+ // We cannot use Qt.red in QML because it's lower case.
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumInvalid.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("c").toBool(), true);
+ QCOMPARE(o->property("d").toBool(), false);
+ }
+}
- person->setName(u"no one"_s);
- QCOMPARE(person->name(), u"no one"_s);
+void tst_QmlCppCodegen::intOverflow()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intOverflow.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("a").toDouble(), 1.09951162778e+12);
+ QCOMPARE(object->property("b").toInt(), 5);
+}
- person->setObjectName(u"the one"_s);
- QCOMPARE(person->name(), u"Bart"_s);
+void tst_QmlCppCodegen::intToEnum()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intToEnum.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ MyType *m = qobject_cast<MyType *>(o.data());
+ QCOMPARE(m->a(), MyType::D);
+ QCOMPARE(m->property("b").toInt(), 24);
}
-void tst_QmlCppCodegen::innerObjectNonShadowable()
+void tst_QmlCppCodegen::interceptor()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ownProperty.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/interceptor.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ QCOMPARE(object->property("x").toInt(), 100);
+ QCOMPARE(object->property("y").toInt(), 100);
- QCOMPARE(rootObject->objectName(), u"foo"_s);
+ QVERIFY(object->property("width").toInt() != 200);
+ QVERIFY(object->property("height").toInt() != 200);
+ QVERIFY(object->property("qProperty1").toInt() != 300);
+ QVERIFY(object->property("qProperty2").toInt() != 300);
+ QTRY_COMPARE(object->property("width").toInt(), 200);
+ QTRY_COMPARE(object->property("height").toInt(), 200);
+ QTRY_COMPARE(object->property("qProperty1").toInt(), 300);
+ QTRY_COMPARE(object->property("qProperty2").toInt(), 300);
}
-void tst_QmlCppCodegen::ownPropertiesNonShadowable()
+void tst_QmlCppCodegen::interestingFiles()
{
+ QFETCH(QString, file);
+ QFETCH(bool, isValid);
+
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/overriddenMember.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/%1"_s.arg(file)));
+ if (isValid) {
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ } else {
+ QVERIFY(component.isError());
+ }
+}
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+void tst_QmlCppCodegen::interestingFiles_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<bool>("isValid");
- QCOMPARE(rootObject->property("ppp").toInt(), 16);
- QCOMPARE(rootObject->property("ppp2").toInt(), 9);
- QCOMPARE(rootObject->property("ppp3").toInt(), 12);
+ QTest::addRow("conversions2") << u"conversions2.qml"_s << true;
+ QTest::addRow("TestCase") << u"TestCase.qml"_s << true;
+ QTest::addRow("layouts") << u"layouts.qml"_s << true;
+ QTest::addRow("interactive") << u"interactive.qml"_s << true;
+ QTest::addRow("Panel") << u"Panel.qml"_s << true;
+ QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_s << true;
+ QTest::addRow("Root") << u"ProgressBar/Root.qml"_s << true;
+ QTest::addRow("noscope") << u"noscope.qml"_s << true;
+ QTest::addRow("dynamicscene") << u"dynamicscene.qml"_s << true;
+ QTest::addRow("curlygrouped") << u"curlygrouped.qml"_s << true;
+ QTest::addRow("cycleHead") << u"cycleHead.qml"_s << false;
+ QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_s << true;
+ QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_s << true;
}
-void tst_QmlCppCodegen::modulePrefix()
+void tst_QmlCppCodegen::internalConversion()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/modulePrefix.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/internalConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ QObject *offset = obj->property("offset").value<QObject *>();
+ QVERIFY(offset);
- QCOMPARE(rootObject->property("foo").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QCOMPARE(rootObject->property("bar").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QCOMPARE(rootObject->property("baz").toString(), QStringLiteral("ItIsTheSingleton"));
+ QCOMPARE(offset->objectName(), "hello"_L1);
+ QCOMPARE(offset->property("mark").toString(), "hello"_L1);
}
-void tst_QmlCppCodegen::colorString()
+void tst_QmlCppCodegen::invalidPropertyType()
{
+ // Invisible on purpose
+ qmlRegisterType<MyCppType>("App", 1, 0, "MyCppType");
+
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorString.qml"_s));
+ QQmlComponent okComponent(&engine, QUrl(u"qrc:/qt/qml/TestTypes/OkType.qml"_s));
+ QVERIFY2(okComponent.isReady(), qPrintable(okComponent.errorString()));
+ QScopedPointer<QObject> picker(okComponent.create());
+ QVERIFY2(!picker.isNull(), qPrintable(okComponent.errorString()));
+ QObject *inner = qmlContext(picker.data())->objectForName(u"inner"_s);
+ QVERIFY(inner);
+ MyCppType *myCppType = qobject_cast<MyCppType *>(inner);
+ QVERIFY(myCppType);
+ QVERIFY(!myCppType->useListDelegate());
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BadType.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.createWithInitialProperties(
+ QVariantMap {{u"picker"_s, QVariant::fromValue(picker.data())}}));
+ QVERIFY2(!o.isNull(), qPrintable(c.errorString()));
+ QVERIFY(!myCppType->useListDelegate());
- QCOMPARE(qvariant_cast<QColor>(rootObject->property("c")), QColor::fromRgb(0xdd, 0xdd, 0xdd));
- QCOMPARE(qvariant_cast<QColor>(rootObject->property("d")), QColor::fromRgb(0xaa, 0xaa, 0xaa));
- QCOMPARE(qvariant_cast<QColor>(rootObject->property("e")), QColor::fromRgb(0x11, 0x22, 0x33));
+ o->setProperty("useListDelegate", QVariant::fromValue<bool>(true));
+ QVERIFY(myCppType->useListDelegate());
}
-void tst_QmlCppCodegen::urlString()
+void tst_QmlCppCodegen::invisibleBase()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/urlString.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleBase.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("n")), o.data());
+}
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+void tst_QmlCppCodegen::invisibleListElementType()
+{
+ qmlRegisterType<InvisibleListElementType>("Invisible", 1, 0, "InvisibleListElement");
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleListElementType.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s));
- QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s));
- QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s));
+ QObject *a = o->property("a").value<QObject *>();
+ QVERIFY(a);
+
+ const QVariant x = a->property("x");
+ QCOMPARE(x.metaType(), QMetaType::fromType<QQmlListReference>());
+ const QQmlListReference ref = x.value<QQmlListReference>();
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 0);
}
+void tst_QmlCppCodegen::invisibleSingleton()
+{
+ // We may have seen Style.qml as singleton before, which would make the assignment pass.
+ // So let's flush the type registry.
+ qmlClearTypeRegistrations();
-void tst_QmlCppCodegen::callContextPropertyLookupResult()
+ QQmlEngine engine;
+ const QUrl copy(u"qrc:/qt/qml/HiddenTestTypes/hidden/Main.qml"_s);
+ QQmlComponent c(&engine, copy);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "qrc:/qt/qml/HiddenTestTypes/hidden/Main.qml:4:5: "
+ "Unable to assign [undefined] to QColor");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("c"), QVariant(QMetaType::fromName("QColor")));
+}
+
+void tst_QmlCppCodegen::invisibleTypes()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callContextPropertyLookupResult.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleTypes.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr);
+ QObject *singleton = qvariant_cast<QObject *>(o->property("singleton"));
+ QVERIFY(singleton != nullptr);
+ QCOMPARE(singleton->metaObject()->className(), "SingletonModel");
+
+ QObject *attached = qvariant_cast<QObject *>(o->property("attached"));
+ QVERIFY(attached != nullptr);
+ QCOMPARE(attached->metaObject()->className(), "AttachedAttached");
+
+// TODO: This doesn't work in interpreted mode:
+// const QMetaObject *meta = qvariant_cast<const QMetaObject *>(o->property("metaobject"));
+// QVERIFY(meta != nullptr);
+// QCOMPARE(meta->className(), "DerivedFromInvisible");
}
-void tst_QmlCppCodegen::deadShoeSize()
+void tst_QmlCppCodegen::iteration()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/deadShoeSize.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/iteration.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/deadShoeSize.qml:5: Error: ouch");
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("shoeSize").toInt(), 0);
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->objectName(), "a345b345c345"_L1);
}
-void tst_QmlCppCodegen::listIndices()
+void tst_QmlCppCodegen::javaScriptArgument()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listIndices.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/javaScriptArgument.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
- QQmlListReference list(o.data(), "items");
- QCOMPARE(list.count(), 3);
- for (int i = 0; i < 3; ++i)
- QCOMPARE(list.at(i), o.data());
- QCOMPARE(o->property("numItems").toInt(), 3);
- QCOMPARE(qvariant_cast<QObject *>(o->property("fractional")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(o->property("negativeZero")), o.data());
- QCOMPARE(qvariant_cast<QObject *>(o->property("infinity")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(o->property("nan")), nullptr);
+ QCOMPARE(o->property("a").toDouble(), 4.0);
+ QCOMPARE(o->property("b").toDouble(), 9.0);
+ QCOMPARE(o->property("c").toString(), u"5t-1"_s);
+ QCOMPARE(o->property("d").toString(), u"9"_s);
+ QCOMPARE(o->property("e").toString(), u"10"_s);
+ QCOMPARE(o->property("f").toString(), u"-10"_s);
+
+ const QStringList scales {
+ "0 ", "1 ", "10 ", "100 ", "1000 ", "9.77k", "97.7k", "977k", "9.54M", "95.4M", "954M",
+ "9.31G", "93.1G", "931G", "9.09T", "-1 ", "-10 ", "-100 ", "-1000 ", "-9.77k", "-97.7k",
+ "-977k", "-9.54M", "-95.4M", "-954M", "-9.31G", "-93.1G", "-931G", "-9.09T"
+ };
+
+ QCOMPARE(o->property("scales").value<QStringList>(), scales);
+
+ double thing = 12.0;
+ QString result;
+ QMetaObject::invokeMethod(
+ o.data(), "forwardArg", Q_RETURN_ARG(QString, result), Q_ARG(double, thing));
+ QCOMPARE(result, u"12 ");
}
-static const double numbers[] = {
- qQNaN(), -qInf(),
- std::numeric_limits<double>::min(),
- std::numeric_limits<float>::min(),
- std::numeric_limits<qint32>::min(),
- -1000.2, -100, -2, -1.333, -1, -0.84, -0.5,
+void tst_QmlCppCodegen::jsArrayMethods()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethods.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- // -0 and 0 are not different on the QML side. Therefore, don't keep them adjacent.
- // Otherwise the bindings won't get re-evaluated.
- std::copysign(0.0, -1), 1, 0.0,
+ QQmlComponent untyped(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsUntyped.qml"_s));
+ QVERIFY2(untyped.isReady(), qPrintable(untyped.errorString()));
+ QScopedPointer<QObject> check(untyped.create());
+ QVERIFY(!check.isNull());
- 0.5, 0.77, 1.4545, 2, 199, 2002.13,
- std::numeric_limits<qint32>::max(),
- std::numeric_limits<quint32>::max(),
- std::numeric_limits<float>::max(),
- std::numeric_limits<double>::max(),
- qInf()
-};
+ check->setProperty("l1", object->property("l1"));
+ check->setProperty("l2", object->property("l2"));
+ check->setProperty("l3", object->property("l3"));
+
+ QCOMPARE(object->property("listPropertyToString"), object->property("jsArrayToString"));
+ QCOMPARE(object->property("listPropertyToString"), check->property("jsArrayToString"));
+
+ QCOMPARE(object->property("listPropertyIncludes"), object->property("jsArrayIncludes"));
+ QVERIFY(object->property("listPropertyIncludes").toBool());
+
+ QCOMPARE(object->property("listPropertyJoin"), object->property("jsArrayJoin"));
+ QCOMPARE(object->property("listPropertyJoin"), check->property("jsArrayJoin"));
+ QVERIFY(object->property("listPropertyJoin").toString().contains(QStringLiteral("klaus")));
+
+ QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyIndexOf").toInt(), 1);
+
+ QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf").toInt(), 5);
+}
+
+void tst_QmlCppCodegen::jsArrayMethodsWithParams()
+{
+ QFETCH(int, i);
+ QFETCH(int, j);
+ QFETCH(int, k);
+ QQmlEngine engine;
+ QQmlComponent component
+ (&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsWithParams.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QQmlComponent untyped(
+ &engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsWithParamsUntyped.qml"_s));
+ QVERIFY2(untyped.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.createWithInitialProperties({
+ {QStringLiteral("i"), i},
+ {QStringLiteral("j"), j},
+ {QStringLiteral("k"), k}
+ }));
+ QVERIFY(!object.isNull());
+ QScopedPointer<QObject> check(untyped.createWithInitialProperties({
+ {QStringLiteral("i"), i},
+ {QStringLiteral("j"), j},
+ {QStringLiteral("k"), k}
+ }));
+ QVERIFY(!check.isNull());
+ check->setProperty("l1", object->property("l1"));
+ check->setProperty("l2", object->property("l2"));
+ check->setProperty("l3", object->property("l3"));
+
+ listsEqual(object.data(), object.data(), "Slice");
+ listsEqual(object.data(), check.data(), "Slice");
+ QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyIndexOf"), check->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf"), check->property("jsArrayLastIndexOf"));
+}
+
+void tst_QmlCppCodegen::jsArrayMethodsWithParams_data()
+{
+ QTest::addColumn<int>("i");
+ QTest::addColumn<int>("j");
+ QTest::addColumn<int>("k");
+
+ const int indices[] = {
+ std::numeric_limits<int>::min(),
+ -10, -3, -2, -1, 0, 1, 2, 3, 10,
+ std::numeric_limits<int>::max(),
+ };
+
+ // We cannot test the full cross product. So, take a random sample instead.
+ const qsizetype numIndices = sizeof(indices) / sizeof(int);
+ qsizetype seed = QRandomGenerator::global()->generate();
+ const int numSamples = 4;
+ for (int i = 0; i < numSamples; ++i) {
+ seed = qHash(i, seed);
+ const int vi = indices[qAbs(seed) % numIndices];
+ for (int j = 0; j < numSamples; ++j) {
+ seed = qHash(j, seed);
+ const int vj = indices[qAbs(seed) % numIndices];
+ for (int k = 0; k < numSamples; ++k) {
+ seed = qHash(k, seed);
+ const int vk = indices[qAbs(seed) % numIndices];
+ const QString tag = QLatin1String("%1/%2/%3").arg(
+ QString::number(vi), QString::number(vj), QString::number(vk));
+ QTest::newRow(qPrintable(tag)) << vi << vj << vk;
+
+ // output all the tags so that we can find out
+ // what combination caused a test to hang.
+ qDebug().noquote() << "scheduling" << tag;
+ }
+ }
+ }
+}
+
+void tst_QmlCppCodegen::jsImport()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsimport.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("value").toInt(), 42);
+}
void tst_QmlCppCodegen::jsMathObject()
{
@@ -1707,708 +2876,1247 @@ void tst_QmlCppCodegen::jsMathObject()
qDebug() << name << "failed.";
}
-void tst_QmlCppCodegen::intEnumCompare()
+void tst_QmlCppCodegen::jsmoduleImport()
{
QQmlEngine engine;
- {
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intEnumCompare.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("a").toBool(), true);
- QCOMPARE(o->property("b").toBool(), false);
- QCOMPARE(o->property("c").toBool(), true);
- QCOMPARE(o->property("d").toBool(), false);
- }
-
- {
- // We cannot use Qt.red in QML because it's lower case.
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumInvalid.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("c").toBool(), true);
- QCOMPARE(o->property("d").toBool(), false);
- }
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsmoduleimport.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("ok").toBool(), true);
+ QVariant okFunc = object->property("okFunc");
+ QCOMPARE(okFunc.metaType(), QMetaType::fromType<QJSValue>());
+ QJSValue val = engine.toScriptValue(okFunc);
+ QJSValue result = val.call();
+ QVERIFY(result.isBool());
+ QVERIFY(result.toBool());
}
-void tst_QmlCppCodegen::attachedSelf()
+void tst_QmlCppCodegen::lengthAccessArraySequenceCompat()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/SelectionRectangle.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ArraySequenceLengthInterop.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("length").toInt(), 100);
+}
- QObject *handle = qvariant_cast<QObject *>(o->property("aa"));
- QVERIFY(handle);
- QVERIFY(qvariant_cast<QObject *>(handle->property("rect")) != nullptr);
+void tst_QmlCppCodegen::letAndConst()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/letAndConst.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"ab"_s);
}
-void tst_QmlCppCodegen::functionReturningVoid()
+void tst_QmlCppCodegen::listAsArgument()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionReturningVoid.qml"_s));
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listAsArgument.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QCOMPARE(o->property("i").toInt(), 4);
+ QCOMPARE(o->property("j").toInt(), 2);
+ QCOMPARE(o->property("i1").toInt(), 2);
+ QCOMPARE(o->property("i2").toInt(), 4);
+ QCOMPARE(o->property("d").value<QObject *>()->objectName(), u"this one"_s);
- // It should be able to call the methods and wrap the void values into invalid QVariants,
- // without crashing.
- QVERIFY(o->metaObject()->indexOfProperty("aa") >= 0);
- QVERIFY(o->metaObject()->indexOfProperty("bb") >= 0);
- QVERIFY(!o->property("aa").isValid());
- QVERIFY(!o->property("bb").isValid());
+ int singleInt = 0;
+ QList<int> moreInts;
+ QMetaObject::invokeMethod(o.data(), "returnInts1", Q_RETURN_ARG(QList<int>, moreInts));
+ QCOMPARE(moreInts, QList<int>({5, 4, 3, 2, 1}));
+ QMetaObject::invokeMethod(o.data(), "selectSecondInt", Q_RETURN_ARG(int, singleInt), Q_ARG(QList<int>, moreInts));
+ QCOMPARE(singleInt, 4);
}
-void tst_QmlCppCodegen::functionCallOnNamespaced()
+void tst_QmlCppCodegen::listConversion()
{
- QQmlEngine engine;
- {
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themergood.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("i").toInt(), 12);
- }
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/listConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- {
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themerbad.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(5.0, 10.0, 1.0, 1.0)));
+ QQmlListProperty<QObject> list = o->property("o").value<QQmlListProperty<QObject>>();
+ QCOMPARE(list.count(&list), 3);
+ for (int i = 0; i < 3; ++i) {
+ QObject *entry = list.at(&list, i);
+ Person *person = qobject_cast<Person *>(entry);
+ QVERIFY(person);
+ QCOMPARE(person->name(), u"Horst %1"_s.arg(i + 1));
}
+
+ QStringList strings = o->property("s").value<QStringList>();
+ QCOMPARE(strings, QStringList({u"Horst 1"_s, u"Horst 2"_s, u"Horst 3"_s}));
+
+ QVariantList vars = o->property("v").toList();
+ QCOMPARE(vars, QVariantList({
+ QString(),
+ QVariant::fromValue<qsizetype>(3),
+ QVariant::fromValue<Person *>(nullptr)
+ }));
}
-void tst_QmlCppCodegen::flushBeforeCapture()
+void tst_QmlCppCodegen::listIndices()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noBindingLoop.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listIndices.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(o);
- QCOMPARE(o->property("deviation").toDouble(), 9.0 / 3.3333);
- QCOMPARE(o->property("samples").toInt(), 16);
- QCOMPARE(o->property("radius").toDouble(), 8.0);
+ QQmlListReference list(o.data(), "items");
+ QCOMPARE(list.count(), 3);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(list.at(i), o.data());
+ QCOMPARE(o->property("numItems").toInt(), 3);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("fractional")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("negativeZero")), o.data());
+ QCOMPARE(qvariant_cast<QObject *>(o->property("infinity")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("nan")), nullptr);
}
-void tst_QmlCppCodegen::unknownAttached()
+void tst_QmlCppCodegen::listLength()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownAttached.qml"_s));
- QVERIFY(c.isError());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listlength.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("l").toInt(), 2);
}
-void tst_QmlCppCodegen::variantlist()
+void tst_QmlCppCodegen::listOfInvisible()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantlist.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
-
- const QVariantList things = qvariant_cast<QVariantList>(o->property("things"));
- QCOMPARE(things.size(), 2);
- QCOMPARE(things[0].toString(), u"thing"_s);
- QCOMPARE(things[1].toInt(), 30);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listOfInvisible.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("width").toDouble(), 27.0);
}
-void tst_QmlCppCodegen::popContextAfterRet()
+void tst_QmlCppCodegen::listPropertyAsModel()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/popContextAfterRet.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listPropertyAsModel.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->objectName(), QString());
- o->setProperty("stackViewDepth", 1);
- QCOMPARE(o->objectName(), u"backgroundImage"_s);
- o->setProperty("stackViewDepth", 2);
- QCOMPARE(o->objectName(), u"backgroundBlur"_s);
- o->setProperty("stackViewDepth", 1);
- QCOMPARE(o->objectName(), u"backgroundImage"_s);
+ QQmlListReference children(o.data(), "children");
+ QCOMPARE(children.count(), 5);
}
-void tst_QmlCppCodegen::revisions()
+void tst_QmlCppCodegen::listToString()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/revisions.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listToString.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "[one,two]");
+ QTest::ignoreMessage(QtDebugMsg, "one,two");
+ QTest::ignoreMessage(QtDebugMsg, "[1,2]");
+ QTest::ignoreMessage(QtDebugMsg, "1,2");
+ QTest::ignoreMessage(
+ QtDebugMsg,
+ QRegularExpression("\\[QObject_QML_[0-9]+\\(0x[0-9a-f]+\\),"
+ "QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)\\]"));
+ QTest::ignoreMessage(
+ QtDebugMsg,
+ QRegularExpression("QObject_QML_[0-9]+\\(0x[0-9a-f]+\\),"
+ "QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)"));
+
+ QTest::ignoreMessage(QtDebugMsg, "[a,b]");
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+}
- QCOMPARE(o->property("delayed").toBool(), true);
- QCOMPARE(o->property("gotten").toInt(), 5);
+void tst_QmlCppCodegen::lotsOfRegisters()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/page.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ const auto compare = [&]() {
+ const qreal implicitBackgroundWidth = object->property("implicitBackgroundWidth").toDouble();
+ const qreal leftInset = object->property("leftInset").toDouble();
+ const qreal rightInset = object->property("rightInset").toDouble();
+ const qreal contentWidth = object->property("contentWidth").toDouble();
+ const qreal leftPadding = object->property("leftPadding").toDouble();
+ const qreal rightPadding = object->property("rightPadding").toDouble();
+ const qreal implicitFooterWidth = object->property("implicitFooterWidth").toDouble();
+ const qreal implicitHeaderWidth = object->property("implicitHeaderWidth").toDouble();
+
+ const qreal implicitWidth = object->property("implicitWidth").toDouble();
+ QCOMPARE(implicitWidth, qMax(qMax(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding),
+ qMax(implicitHeaderWidth, implicitFooterWidth)));
+ };
+
+ compare();
+
+ const QList<const char *> props = {
+ "leftInset", "rightInset", "contentWidth", "leftPadding", "rightPadding"
+ };
+
+ for (int i = 0; i < 100; ++i) {
+ QVERIFY(object->setProperty(props[i % props.size()], (i * 17) % 512));
+ compare();
+ }
}
-void tst_QmlCppCodegen::invisibleBase()
+void tst_QmlCppCodegen::math()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleBase.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(qvariant_cast<QObject *>(o->property("n")), o.data());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/math.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("a").toInt(), 9);
+ QCOMPARE(object->property("b").toDouble(), 50.0 / 22.0);
+ QCOMPARE(object->property("c").toDouble(), std::atan(1.0) * 8.0);
}
-void tst_QmlCppCodegen::notEqualsInt()
+void tst_QmlCppCodegen::mathMinMax()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notEqualsInt.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathMinMax.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ // Math.max()
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "9");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0.002");
+ QTest::ignoreMessage(QtDebugMsg, "5.4");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+ QTest::ignoreMessage(QtDebugMsg, "Infinity");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "0.08");
+ QTest::ignoreMessage(QtDebugMsg, "Infinity");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+
+ // Math.min()
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-2");
+ QTest::ignoreMessage(QtDebugMsg, "-2");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-0.001");
+ QTest::ignoreMessage(QtDebugMsg, "0.002");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-8");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QObject *t = qmlContext(o.data())->objectForName(u"t"_s);
- QVERIFY(t);
- QCOMPARE(t->property("text").toString(), u"Foo"_s);
- QMetaObject::invokeMethod(o.data(), "foo");
- QCOMPARE(t->property("text").toString(), u"Bar"_s);
+ QVERIFY(!o.isNull());
}
-void tst_QmlCppCodegen::infinities()
+void tst_QmlCppCodegen::mathOperations()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinities.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathOperations.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity());
- QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity());
+ const QMetaObject *metaObject = o->metaObject();
- const double positiveZero = o->property("positiveZero").toDouble();
- QCOMPARE(positiveZero, 0.0);
- QVERIFY(!std::signbit(positiveZero));
+ char t1;
+ char t2;
+ QString name;
+ const auto guard = qScopeGuard([&]() {
+ if (QTest::currentTestFailed()) {
+ qDebug() << t1 << t2 << name << "failed on:";
+ qDebug() << "doubles" << o->property("a").toDouble() << o->property("b").toDouble();
+ qDebug() << "integers" << o->property("ia").toInt() << o->property("ib").toInt();
+ qDebug() << "booleans" << o->property("ba").toBool() << o->property("bb").toBool();
+ }
+ });
- const double negativeZero = o->property("negativeZero").toDouble();
- QCOMPARE(negativeZero, -0.0);
- QVERIFY(std::signbit(negativeZero));
+ for (double a : numbers) {
+ for (double b : numbers) {
+ o->setProperty("a", a);
+ o->setProperty("b", b);
+ for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) {
+ const QMetaProperty prop = metaObject->property(i);
+ const QByteArray propName = prop.name();
- QVERIFY(qIsNaN(o->property("naN").toDouble()));
+ if (propName.size() < 3 || propName == "objectName")
+ continue;
+
+ t1 = propName[0];
+ t2 = propName[1];
+ name = QString::fromUtf8(propName.mid(2));
+
+ double expected;
+
+ switch (t2) {
+ case 'd':
+ case '_':
+ switch (t1) {
+ case 'd':
+ expected = jsEval<double, double>(a, b, name, &engine);
+ break;
+ case 'i':
+ expected = jsEval<int, double>(a, b, name, &engine);
+ break;
+ case 'b':
+ expected = jsEval<bool, double>(a, b, name, &engine);
+ break;
+ }
+ break;
+ case 'i':
+ switch (t1) {
+ case 'd':
+ expected = jsEval<double, int>(a, b, name, &engine);
+ break;
+ case 'i':
+ expected = jsEval<int, int>(a, b, name, &engine);
+ break;
+ case 'b':
+ expected = jsEval<bool, int>(a, b, name, &engine);
+ break;
+ }
+ break;
+ case 'b':
+ switch (t1) {
+ case 'd':
+ expected = jsEval<double, bool>(a, b, name, &engine);
+ break;
+ case 'i':
+ expected = jsEval<int, bool>(a, b, name, &engine);
+ break;
+ case 'b':
+ expected = jsEval<bool, bool>(a, b, name, &engine);
+ break;
+ }
+ break;
+ }
+
+ const double result = prop.read(o.data()).toDouble();
+ QCOMPARE(result, expected);
+ }
+ }
+ }
}
-void tst_QmlCppCodegen::blockComments()
+void tst_QmlCppCodegen::mathStaticProperties()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/blockComments.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("implicitHeight").toDouble(), 8.0);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathStaticProperties.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ // Approximate values based on
+ // https://262.ecma-international.org/14.0/#sec-value-properties-of-the-math-object
+ QCOMPARE(object->property("e").toDouble(), 2.7182818284590452354);
+ QCOMPARE(object->property("ln10").toDouble(), 2.302585092994046);
+ QCOMPARE(object->property("ln2").toDouble(), 0.6931471805599453);
+ QCOMPARE(object->property("log10e").toDouble(), 0.4342944819032518);
+ QCOMPARE(object->property("log2e").toDouble(), 1.4426950408889634);
+ QCOMPARE(object->property("pi").toDouble(), 3.1415926535897932);
+ QCOMPARE(object->property("sqrt1_2").toDouble(), 0.7071067811865476);
+ QCOMPARE(object->property("sqrt2").toDouble(), 1.4142135623730951);
}
-void tst_QmlCppCodegen::functionLookup()
+void tst_QmlCppCodegen::mergedObjectReadWrite()
+{
+ QQmlEngine e;
+ {
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/mergedObjectRead.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtDebugMsg, "null");
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("TypeError: Cannot read property 'x' of null"));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ }
+
+ {
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/mergedObjectWrite.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(
+ "TypeError: Value is null and could not be converted to an object"));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ }
+}
+
+void tst_QmlCppCodegen::methodOnListLookup()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionLookup.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- const QVariant foo = o->property("bar");
- QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>());
- const QJSManagedValue method(engine.toScriptValue(foo), &engine);
- QVERIFY(method.isFunction());
- const QJSValue result = method.call();
- QVERIFY(result.isString());
- QCOMPARE(result.toString(), QStringLiteral("a99"));
+ const QUrl url(u"qrc:/qt/qml/TestTypes/methodOnListLookup.qml"_s);
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->objectName(), u"no one");
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString()
+ + ":14: TypeError: Cannot call method 'getName' of undefined"_L1));
+ QMetaObject::invokeMethod(o.data(), "boom");
}
-void tst_QmlCppCodegen::objectInVar()
+void tst_QmlCppCodegen::methods()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectInVar.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/methods.qml"_s));
+ QVERIFY(component.isReady());
- bool result = false;
- QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
- QVERIFY(result);
+ QTest::ignoreMessage(QtDebugMsg, "The Bar");
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_s));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj);
+ BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data()));
- o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr));
- QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
- QVERIFY(!result);
+ QVERIFY(party && party->host());
+ QCOMPARE(party->guestCount(), 5);
+
+ bool foundGreen = false;
+ bool foundFoo = false;
+ for (int ii = 0; ii < party->guestCount(); ++ii) {
+ if (party->guest(ii)->name() == u"William Green"_s)
+ foundGreen = true;
+ if (party->guest(ii)->name() == u"The Foo"_s)
+ foundFoo = true;
+ }
+
+ QVERIFY(foundGreen);
+ QVERIFY(foundFoo);
+
+ QCOMPARE(obj->property("n1").toString(), u"onGurk"_s);
+ QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_s);
+ QCOMPARE(obj->property("n3"), QVariant());
+
+ {
+ QVariant ret;
+ obj->metaObject()->invokeMethod(obj.data(), "retrieveVar", Q_RETURN_ARG(QVariant, ret));
+ QCOMPARE(ret.typeId(), QMetaType::QString);
+ QCOMPARE(ret.toString(), u"Jack Smith"_s);
+ }
+
+ {
+ QString ret;
+ obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret));
+ QCOMPARE(ret, u"Jack Smith"_s);
+ }
+
+ QCOMPARE(party->host()->shoeSize(), 12);
+ obj->metaObject()->invokeMethod(obj.data(), "storeElement");
+ QCOMPARE(party->host()->shoeSize(), 13);
+ QJSManagedValue v = engine.toManagedValue(obj->property("dresses"));
+ QVERIFY(v.isArray());
+
+ QJSManagedValue inner(v.property(2), &engine);
+ QVERIFY(inner.isArray());
+ QCOMPARE(inner.property(0).toInt(), 1);
+ QCOMPARE(inner.property(1).toInt(), 2);
+ QCOMPARE(inner.property(2).toInt(), 3);
+
+ QCOMPARE(obj->property("enumValue").toInt(), 2);
}
-void tst_QmlCppCodegen::functionTakingVar()
+void tst_QmlCppCodegen::modulePrefix()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/functionTakingVar.qml"_s);
- QQmlComponent c(&engine, document);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/modulePrefix.qml"_s));
- QVERIFY(!o->property("c").isValid());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- int value = 11;
- QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine);
- void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) };
- QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() };
- e->executeRuntimeFunction(document, 0, o.data(), 1, args, types);
+ QCOMPARE(rootObject->property("foo").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
+ QCOMPARE(rootObject->property("bar").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
+ QCOMPARE(rootObject->property("baz").toString(), QStringLiteral("ItIsTheSingleton"));
+}
- QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
+void tst_QmlCppCodegen::multiDirectory_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addRow("from qt_add_qml_module")
+ << QUrl(u"qrc:/qt/qml/TestTypes/extra/extra.qml"_s);
+#ifndef VERY_OLD_CMAKE
+ QTest::addRow("from qt_target_qml_sources")
+ << QUrl(u"qrc:/qt/qml/TestTypes/extra2/extra.qml"_s);
+#endif
}
-void tst_QmlCppCodegen::testIsnan()
+void tst_QmlCppCodegen::multiDirectory()
{
+ QFETCH(QUrl, url);
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/isnan.qml"_s);
- QQmlComponent c(&engine, document);
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->property("r").value<QRectF>(), QRectF(4, 6, 8, 10));
+}
+
+void tst_QmlCppCodegen::multiForeign()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiforeign.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"not here and not there"_s);
+}
- QCOMPARE(o->property("good").toDouble(), 10.1);
- QVERIFY(qIsNaN(o->property("bad").toDouble()));
+void tst_QmlCppCodegen::multiLookup()
+{
+ // Multiple lookups of singletons (Qt in this case) don't clash with one another.
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/immediateQuit.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- const QVariant a = o->property("a");
- QCOMPARE(a.metaType(), QMetaType::fromType<bool>());
- QVERIFY(!a.toBool());
+ const QByteArray message = QByteArray("End: ") + arg1();
+ QTest::ignoreMessage(QtDebugMsg, message.constData());
- const QVariant b = o->property("b");
- QCOMPARE(b.metaType(), QMetaType::fromType<bool>());
- QVERIFY(b.toBool());
+ QSignalSpy quitSpy(&engine, &QQmlEngine::quit);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(quitSpy.size(), 1);
}
-void tst_QmlCppCodegen::fallbackLookups()
+void tst_QmlCppCodegen::multipleCtors()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/fallbacklookups.qml"_s);
- QQmlComponent c(&engine, document);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multipleCtors.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("wr").value<ValueTypeWithLength>().length(), 3);
+ QCOMPARE(o->property("wp").value<ValueTypeWithLength>().length(), 11);
+ QCOMPARE(o->property("wi").value<ValueTypeWithLength>().length(), 17);
+}
- QCOMPARE(o->objectName(), QString());
- int result = 0;
+void tst_QmlCppCodegen::namespaceWithEnum()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/namespaceWithEnum.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("i").toInt(), 2);
+}
- QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 16);
- QCOMPARE(o->objectName(), QStringLiteral("aa93"));
+void tst_QmlCppCodegen::noQQmlData()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noQQmlData.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 17);
- QCOMPARE(o->objectName(), QStringLiteral("bb94"));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
+ "Cannot read property 'name' of null");
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
- QObject *singleton = nullptr;
- QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton));
- QVERIFY(singleton);
+ BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data());
+ QVERIFY(party != nullptr);
- QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 18);
- QCOMPARE(singleton->objectName(), QStringLiteral("cc95"));
+ QCOMPARE(party->host(), nullptr);
+ QCOMPARE(party->property("n").toString(), QString());
- QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 19);
- QCOMPARE(singleton->objectName(), QStringLiteral("dd96"));
+ Person *host1 = new Person(party);
+ party->setHost(host1);
+ QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
+ host1->setName(u"Marge"_s);
+ QCOMPARE(party->property("n").toString(), u"Marge in da house!"_s);
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
+ "Cannot read property 'name' of null");
+
+ // Doesn't crash
+ party->setHost(nullptr);
+
+ // Lookups are initialized now, and we introduce an object without QQmlData
+ Person *host2 = new Person(party);
+ party->setHost(host2);
+ QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
+ host2->setName(u"Homer"_s);
+ QCOMPARE(party->property("n").toString(), u"Homer in da house!"_s);
+
+ QMetaObject::invokeMethod(party, "burn");
+ engine.collectGarbage();
+
+ // Does not crash
+ party->setProperty("inDaHouse", u" burns!"_s);
+
+ // Mr Burns may or may not burn, depending on whether we use lookups.
+ // If using lookups, the binding is aborted when we find the isQueuedForDeletion flag.
+ // If reading the property directly, we don't have to care about it.
+ QVERIFY(party->property("n").toString().startsWith(u"Mr Burns"_s));
}
-void tst_QmlCppCodegen::typedArray()
+void tst_QmlCppCodegen::nonNotifyable()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/typedArray.qml"_s);
- QQmlComponent c(&engine, document);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nonNotifyable.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(qvariant_cast<QDateTime>(object->property("dayz")),
+ QDateTime(QDate(2121, 1, 12), QTime()));
+ QCOMPARE(qvariant_cast<QDateTime>(object->property("oParty")),
+ QDateTime(QDate(2111, 12, 11), QTime()));
+}
+
+void tst_QmlCppCodegen::notEqualsInt()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notEqualsInt.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(o);
- QDateTime date;
- QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty());
- QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")),
- QList<int>({1, 2, 3, 4}));
- QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
- QList<QDateTime>({date, date, date}));
- QCOMPARE(qvariant_cast<QList<double>>(o->property("values5")),
- QList<double>({1, 2, 3.4, 30, 0, 0}));
- date = QDateTime::currentDateTime();
- o->setProperty("aDate", date);
- QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
- QList<QDateTime>({date, date, date}));
+ QObject *t = qmlContext(o.data())->objectForName(u"t"_s);
+ QVERIFY(t);
+ QCOMPARE(t->property("text").toString(), u"Foo"_s);
+ QMetaObject::invokeMethod(o.data(), "foo");
+ QCOMPARE(t->property("text").toString(), u"Bar"_s);
+}
- QQmlListProperty<QObject> values6
- = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6"));
- QCOMPARE(values6.count(&values6), 3);
- for (int i = 0; i < 3; ++i)
- QCOMPARE(values6.at(&values6, i), o.data());
+void tst_QmlCppCodegen::notNotString()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notNotString.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->property("inIntList").toInt(), 2);
- QCOMPARE(qvariant_cast<QDateTime>(o->property("inDateList")), date);
- QCOMPARE(o->property("inRealList").toDouble(), 30.0);
- QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f"));
+ QCOMPARE(o->property("notNotString").value<bool>(), false);
+ o->setObjectName(u"a"_s);
+ QCOMPARE(o->property("notNotString").value<bool>(), true);
+}
- const QMetaObject *metaObject = o->metaObject();
- QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)"));
- QVERIFY(method.isValid());
+void tst_QmlCppCodegen::nullAccess()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccess.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- // If LoadElement threw an exception the function would certainly return neither 10 nor 20.
- int result = 0;
- method.invoke(
- o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a")));
- QCOMPARE(result, 10);
- method.invoke(
- o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa")));
- QCOMPARE(result, 20);
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccess.qml:4:5: TypeError: "
+ "Cannot read property 'width' of null");
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccess.qml:5:5: TypeError: "
+ "Cannot read property 'height' of null");
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccess.qml:6: TypeError: Value is null and "
+ "could not be converted to an object");
+ QScopedPointer<QObject> object(component.create());
+
+ QCOMPARE(object->property("width").toDouble(), 0.0);
+ QCOMPARE(object->property("height").toDouble(), 0.0);
}
-void tst_QmlCppCodegen::prefixedType()
+void tst_QmlCppCodegen::nullAccessInsideSignalHandler()
{
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccessInsideSignalHandler.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccessInsideSignalHandler.qml:15: ReferenceError: "
+ "text is not defined");
+ QScopedPointer<QObject> object(component.create());
+ QSignalSpy spy(object.data(), SIGNAL(say_hello()));
+ QTRY_VERIFY(spy.size() > 0);
+}
- // We need to add an import path here because we cannot namespace the implicit import.
- // The implicit import is what we use for all the other tests, even if we explicitly
- // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
- engine.addImportPath(u":/"_s);
- const QUrl document(u"qrc:/qt/qml/TestTypes/prefixedMetaType.qml"_s);
- QQmlComponent c(&engine, document);
+void tst_QmlCppCodegen::nullComparison()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullComparison.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
- QCOMPARE(o->property("state").toInt(), 2);
- QVERIFY(qvariant_cast<QObject *>(o->property("a")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("b")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("c")) == nullptr);
+ QCOMPARE(o->property("v").toInt(), 1);
+ QCOMPARE(o->property("w").toInt(), 3);
+ QCOMPARE(o->property("x").toInt(), 1);
+ QCOMPARE(o->property("y").toInt(), 5);
+ QCOMPARE(o->property("z").toInt(), 18);
+}
- QVERIFY(qvariant_cast<QObject *>(o->property("d")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("e")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("f")) == nullptr);
+void tst_QmlCppCodegen::nullishCoalescing_data()
+{
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QVariant>("expected");
- QVERIFY(qvariant_cast<QObject *>(o->property("g")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("h")) != nullptr);
+ const auto undefinedValue = QVariant();
+ const auto nullValue = QVariant::fromMetaType(QMetaType::fromType<std::nullptr_t>(), nullptr);
- QCOMPARE(o->property("countG").toInt(), 11);
- QCOMPARE(o->property("countH").toInt(), 11);
-}
+ QTest::addRow("trivial-good-int") << "p1" << QVariant(5);
+ QTest::addRow("trivial-good-string") << "p2" << QVariant("6");
-void tst_QmlCppCodegen::evadingAmbiguity()
-{
- QQmlEngine engine;
+ QTest::addRow("trivial-bad-undefined-undefined") << "p3" << undefinedValue;
+ QTest::addRow("trivial-bad-undefined-null") << "p4" << nullValue;
+ QTest::addRow("trivial-bad-undefined-int") << "p5" << QVariant(-1);
+ QTest::addRow("trivial-bad-undefined-string") << "p6" << QVariant("-1");
- // We need to add an import path here because we cannot namespace the implicit import.
- // The implicit import is what we use for all the other tests, even if we explicitly
- // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
- engine.addImportPath(u":/"_s);
+ QTest::addRow("trivial-bad-null-undefined") << "p7" << undefinedValue;
+ QTest::addRow("trivial-bad-null-null") << "p8" << nullValue;
+ QTest::addRow("trivial-bad-null-int") << "p9" << QVariant(-1);
+ QTest::addRow("trivial-bad-null-string") << "p10" << QVariant("-1");
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous1/Ambiguous.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QScopedPointer<QObject> o1(c1.create());
- QCOMPARE(o1->objectName(), QStringLiteral("Ambiguous"));
- QCOMPARE(o1->property("i").toString(), QStringLiteral("Ambiguous1"));
+ QTest::addRow("enum1") << "p11" << QVariant(1);
- QQmlComponent c2(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous2/Ambiguous.qml"_s));
- QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
- QScopedPointer<QObject> o2(c2.create());
- QCOMPARE(o2->objectName(), QStringLiteral("Ambiguous"));
- QCOMPARE(o2->property("i").toString(), QStringLiteral("Ambiguous2"));
+ QTest::addRow("multiple ?? int") << "p12" << QVariant(1);
+ QTest::addRow("multiple ?? string") << "p13" << QVariant("1");
+ QTest::addRow("multiple ?? mixed2") << "p14" << QVariant("2");
+ QTest::addRow("multiple ?? mixed3") << "p15" << QVariant(1);
+
+ QTest::addRow("optional + nullish bad") << "p16" << QVariant(-1);
+ QTest::addRow("optional + nullish good") << "p17" << QVariant(5);
}
-void tst_QmlCppCodegen::fromBoolValue()
+void tst_QmlCppCodegen::nullishCoalescing()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fromBoolValue.qml"_s));
+ const QUrl document(u"qrc:/qt/qml/TestTypes/nullishCoalescing.qml"_s);
+ QQmlComponent c(&engine, document);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->property("a").toBool(), true);
- o->setProperty("x", 100);
- QCOMPARE(o->property("a").toBool(), false);
-
- QCOMPARE(o->property("width").toInt(), 100);
- QCOMPARE(o->property("b").toBool(), false);
+ QVERIFY(o);
- QScopedPointer<QObject> parent(c.create());
- o->setProperty("parent", QVariant::fromValue(parent.data()));
- QCOMPARE(o->property("width").toInt(), 100);
- QCOMPARE(o->property("b").toBool(), false);
+ QFETCH(QString, propertyName);
+ QFETCH(QVariant, expected);
- o->setProperty("state", QVariant::fromValue(u"foo"_s));
- QCOMPARE(o->property("width").toInt(), 0);
- QCOMPARE(o->property("b").toBool(), false);
+ QVariant actual = o->property(propertyName.toLocal8Bit());
+ QCOMPARE(actual, expected);
}
-void tst_QmlCppCodegen::invisibleTypes()
+void tst_QmlCppCodegen::numbersInJsPrimitive()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleTypes.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/numbersInJsPrimitive.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QObject *singleton = qvariant_cast<QObject *>(o->property("singleton"));
- QVERIFY(singleton != nullptr);
- QCOMPARE(singleton->metaObject()->className(), "SingletonModel");
+ const QList<qint64> zeroes
+ = {0, 0, 0, 0, 0, 0, 0, 0};
+ const QList<qint64> written
+ = {35, 36, 37, 38, 39, 40, 41, 42};
+ const QList<qint64> writtenNegative
+ = {-35, 220, -37, 65498, -39, 4294967256, -41, 4294967254};
+ const QList<QList<qint64>> writtenShuffled = {
+ { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 },
+ { -37, 218, -39, 65496, -41, 4294967254, -36, 4294967259 },
+ { -38, 217, -40, 65495, -42, 4294967260, -37, 4294967258 },
+ { -39, 216, -41, 65494, -36, 4294967259, -38, 4294967257 },
+ { -40, 215, -42, 65500, -37, 4294967258, -39, 4294967256 },
+ { -41, 214, -36, 65499, -38, 4294967257, -40, 4294967255 },
+ { -42, 220, -37, 65498, -39, 4294967256, -41, 4294967254 },
+ { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 },
+ };
- QObject *attached = qvariant_cast<QObject *>(o->property("attached"));
- QVERIFY(attached != nullptr);
- QCOMPARE(attached->metaObject()->className(), "AttachedAttached");
+ const QList<qint64> stored
+ = {50, 51, 1332, 1333, 1334, 1335, 1336, 1337};
+ const QList<qint64> storedNegative
+ = {-50, 205, -1332, 64203, -1334, 4294965961, -1336, 4294965959};
+ const QList<QList<qint64>> storedShuffled = {
+ { -51, 204, -1333, 64202, -1335, 4294965960, -1337, 4294967245 },
+ { -52, 203, -1334, 64201, -1336, 4294965959, -51, 4294967244 },
+ { -53, 202, -1335, 64200, -1337, 4294967245, -52, 4294967243 },
+ { -54, 201, -1336, 64199, -51, 4294967244, -53, 4294967242 },
+ { -55, 200, -1337, 65485, -52, 4294967243, -54, 4294967241 },
+ { -56, 199, -51, 65484, -53, 4294967242, -55, 4294967240 },
+ { -57, 205, -52, 65483, -54, 4294967241, -56, 4294967239 },
+ { -51, 204, -53, 65482, -55, 4294967240, -57, 4294967245 },
+ };
-// TODO: This doesn't work in interpreted mode:
-// const QMetaObject *meta = qvariant_cast<const QMetaObject *>(o->property("metaobject"));
-// QVERIFY(meta != nullptr);
-// QCOMPARE(meta->className(), "DerivedFromInvisible");
-}
+ QStringList asStrings(8);
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(zeroes));
-class MyCppType : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(bool useListDelegate
- READ useListDelegate
- WRITE setUseListDelegate
- NOTIFY useListDelegateChanged)
-public:
- explicit MyCppType(QObject * parent = nullptr) : QObject(parent) {}
+ QMetaObject::invokeMethod(o.data(), "writeValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(written));
- bool useListDelegate() const { return m_useListDelegate; }
- void setUseListDelegate(bool useListDelegate)
- {
- if (useListDelegate != m_useListDelegate) {
- m_useListDelegate = useListDelegate;
- emit useListDelegateChanged();
+ QMetaObject::invokeMethod(o.data(), "negateValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(writtenNegative));
+
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(o.data(), "shuffleValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
}
+ QCOMPARE(asStrings, convertToStrings(writtenShuffled[i]));
}
-signals:
- void useListDelegateChanged();
+ QMetaObject::invokeMethod(o.data(), "storeValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(stored));
-private:
- bool m_useListDelegate = false;
-};
+ QMetaObject::invokeMethod(o.data(), "negateValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(storedNegative));
+
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(o.data(), "shuffleValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(storedShuffled[i]));
+ }
+}
-void tst_QmlCppCodegen::invalidPropertyType()
+void tst_QmlCppCodegen::objectInVar()
{
- // Invisible on purpose
- qmlRegisterType<MyCppType>("App", 1, 0, "MyCppType");
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectInVar.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data());
+
+ bool result = false;
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
+ QVERIFY(result);
+ o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr));
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
+ QVERIFY(!result);
+}
+
+void tst_QmlCppCodegen::objectLookupOnListElement()
+{
QQmlEngine engine;
- QQmlComponent okComponent(&engine, QUrl(u"qrc:/qt/qml/TestTypes/OkType.qml"_s));
- QVERIFY2(okComponent.isReady(), qPrintable(okComponent.errorString()));
- QScopedPointer<QObject> picker(okComponent.create());
- QVERIFY2(!picker.isNull(), qPrintable(okComponent.errorString()));
- QObject *inner = qmlContext(picker.data())->objectForName(u"inner"_s);
- QVERIFY(inner);
- MyCppType *myCppType = qobject_cast<MyCppType *>(inner);
- QVERIFY(myCppType);
- QVERIFY(!myCppType->useListDelegate());
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BadType.qml"_s));
+ const QUrl url(u"qrc:/qt/qml/TestTypes/objectLookupOnListElement.qml"_s);
+ QQmlComponent c1(&engine, url);
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull());
+
+ QList<int> zOrders;
+ QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders));
+ QCOMPARE(zOrders, (QList<int>{1, 0, 0}));
+ object->setProperty("current", 1);
+ QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders));
+ QCOMPARE(zOrders, (QList<int>{0, 1, 0}));
+
+ QMetaObject::invokeMethod(object.data(), "clearChildren");
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString()
+ + u":21: TypeError: Cannot read property 'z' of undefined"_s));
+ QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders));
+ QCOMPARE(zOrders, (QList<int>()));
+}
+
+void tst_QmlCppCodegen::objectToString()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/toString.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.createWithInitialProperties(
- QVariantMap {{u"picker"_s, QVariant::fromValue(picker.data())}}));
- QVERIFY2(!o.isNull(), qPrintable(c.errorString()));
- QVERIFY(!myCppType->useListDelegate());
- o->setProperty("useListDelegate", QVariant::fromValue<bool>(true));
- QVERIFY(myCppType->useListDelegate());
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/toString.qml:6: no");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("yes").toString(), u"yes yes"_s);
+ QCOMPARE(o->property("no").toString(), u" no"_s); // throws, but that is ignored
}
-void tst_QmlCppCodegen::valueTypeLists()
+void tst_QmlCppCodegen::objectWithStringListMethod()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeLists.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectWithStringListMethod.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtDebugMsg, "2");
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
- QCOMPARE(qvariant_cast<QRectF>(o->property("rectInBounds")), QRectF(1, 2, 3, 4));
- QVERIFY(o->metaObject()->indexOfProperty("rectOutOfBounds") > 0);
- QVERIFY(!o->property("rectOutOfBounds").isValid());
+void tst_QmlCppCodegen::onAssignment()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/pressAndHoldButton.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- QCOMPARE(qvariant_cast<QString>(o->property("stringInBounds")), QStringLiteral("bbb"));
- QVERIFY(o->metaObject()->indexOfProperty("stringOutOfBounds") > 0);
- QVERIFY(!o->property("stringOutOfBounds").isValid());
+ QCOMPARE(object->property("pressed").toBool(), false);
+ QCOMPARE(object->property("scale").toDouble(), 1.0);
- QCOMPARE(qvariant_cast<int>(o->property("intInBounds")), 7);
- QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0);
- QVERIFY(!o->property("intOutOfBounds").isValid());
+ object->metaObject()->invokeMethod(object.data(), "press");
+ QTRY_COMPARE(object->property("pressed").toBool(), true);
+ QCOMPARE(object->property("scale").toDouble(), 0.9);
- QCOMPARE(qvariant_cast<QString>(o->property("charInBounds")), QStringLiteral("d"));
- QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0);
- QVERIFY(!o->property("charOutOfBounds").isValid());
+ object->metaObject()->invokeMethod(object.data(), "release");
+ QCOMPARE(object->property("pressed").toBool(), false);
+ QCOMPARE(object->property("scale").toDouble(), 1.0);
}
-void tst_QmlCppCodegen::boundComponents()
+void tst_QmlCppCodegen::optionalComparison()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/boundComponents.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/optionalComparison.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QObject *c1o = o->property("o").value<QObject *>();
- QVERIFY(c1o != nullptr);
- QCOMPARE(c1o->objectName(), u"bar"_s);
+ QCOMPARE(object->property("found").toInt(), 1);
+ QCOMPARE(object->property("foundStrict").toInt(), 1);
+ QCOMPARE(object->property("foundNot").toInt(), 2);
+ QCOMPARE(object->property("foundStrictNot").toInt(), 2);
- QObject *c2o = c1o->property("o").value<QObject *>();
- QVERIFY(c2o != nullptr);
- QCOMPARE(c2o->objectName(), u"bar12"_s);
+ // this === this, null === null (x4), undefined === undefined
+ QCOMPARE(object->property("undefinedEqualsUndefined").toInt(), 6);
+
+ QCOMPARE(object->property("optionalNull").toBool(), true);
+ object->setObjectName("foo"_L1);
+ QCOMPARE(object->property("optionalNull").toBool(), false);
}
-class InvisibleListElementType : public QObject
+void tst_QmlCppCodegen::outOfBoundsArray()
{
- Q_OBJECT
-public:
- InvisibleListElementType(QObject *parent = nullptr) : QObject(parent) {}
-};
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/outOfBounds.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
-void tst_QmlCppCodegen::invisibleListElementType()
+ QTest::ignoreMessage(QtDebugMsg, "oob undefined");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QVERIFY(object->metaObject()->indexOfProperty("oob") > 0);
+ QVERIFY(!object->property("oob").isValid());
+ const QVariant oob2 = object->property("oob2");
+ QCOMPARE(oob2.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(oob2.value<QObject *>(), nullptr);
+}
+
+void tst_QmlCppCodegen::overriddenProperty()
{
- qmlRegisterType<InvisibleListElementType>("Invisible", 1, 0, "InvisibleListElement");
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleListElementType.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/childobject.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QObject *a = o->property("a").value<QObject *>();
- QVERIFY(a);
+ QObject *child = object->property("child").value<QObject *>();
+ QVERIFY(child);
- const QVariant x = a->property("x");
- QCOMPARE(x.metaType(), QMetaType::fromType<QQmlListReference>());
- const QQmlListReference ref = x.value<QQmlListReference>();
- QVERIFY(ref.isValid());
- QCOMPARE(ref.size(), 0);
+ QCOMPARE(object->objectName(), u"kraut"_s);
+ QCOMPARE(object->property("doneThing").toInt(), 5);
+ QCOMPARE(object->property("usingFinal").toInt(), 5);
+
+ auto checkAssignment = [&]() {
+ const QString newName = u"worscht"_s;
+ QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName));
+ QCOMPARE(object->objectName(), newName);
+ };
+ checkAssignment();
+
+ QMetaObject::invokeMethod(child, "doString");
+ QCOMPARE(child->objectName(), u"string"_s);
+ QMetaObject::invokeMethod(child, "doNumber");
+ QCOMPARE(child->objectName(), u"double"_s);
+ QMetaObject::invokeMethod(child, "doArray");
+ QCOMPARE(child->objectName(), u"javaScript"_s);
+
+ QMetaObject::invokeMethod(child, "doString2");
+ QCOMPARE(child->objectName(), u"string"_s);
+ QMetaObject::invokeMethod(child, "doNumber2");
+ QCOMPARE(child->objectName(), u"double"_s);
+ QMetaObject::invokeMethod(child, "doArray2");
+ QCOMPARE(child->objectName(), u"javaScript"_s);
+
+ QMetaObject::invokeMethod(child, "doFoo");
+ QCOMPARE(child->objectName(), u"ObjectWithMethod"_s);
+
+ ObjectWithMethod *benign = new ObjectWithMethod(object.data());
+ benign->theThing = 10;
+ benign->setObjectName(u"cabbage"_s);
+ object->setProperty("child", QVariant::fromValue(benign));
+ QCOMPARE(object->objectName(), u"cabbage"_s);
+ checkAssignment();
+ QCOMPARE(object->property("doneThing").toInt(), 10);
+ QCOMPARE(object->property("usingFinal").toInt(), 10);
+
+ OverriddenObjectName *evil = new OverriddenObjectName(object.data());
+ QTest::ignoreMessage(QtWarningMsg,
+ "Final member fff is overridden in class OverriddenObjectName. "
+ "The override won't be used.");
+ object->setProperty("child", QVariant::fromValue(evil));
+
+ QCOMPARE(object->objectName(), u"borschtsch"_s);
+
+ checkAssignment();
+ QCOMPARE(object->property("doneThing").toInt(), 7);
+ QCOMPARE(object->property("usingFinal").toInt(), 5);
}
-void tst_QmlCppCodegen::typePropertyClash()
+void tst_QmlCppCodegen::ownPropertiesNonShadowable()
{
QQmlEngine engine;
- engine.rootContext()->setContextProperty(u"size"_s, 5);
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropertyClash.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"Size: 5"_s);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/overriddenMember.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->property("ppp").toInt(), 16);
+ QCOMPARE(rootObject->property("ppp2").toInt(), 9);
+ QCOMPARE(rootObject->property("ppp3").toInt(), 12);
}
-void tst_QmlCppCodegen::objectToString()
+void tst_QmlCppCodegen::parentProperty()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/toString.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/parentProp.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("c").toInt(), 11);
+ QCOMPARE(object->property("i").toInt(), 22);
+ object->setProperty("a", QVariant::fromValue(22));
+ QCOMPARE(object->property("c").toInt(), 28);
+ object->setProperty("implicitWidth", QVariant::fromValue(14));
+ QCOMPARE(object->property("i").toInt(), 26);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/toString.qml:6: no");
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
+ QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_s);
+ QObject *evil = qmlContext(object.data())->objectForName(u"evil"_s);
- QCOMPARE(o->property("yes").toString(), u"yes yes"_s);
- QCOMPARE(o->property("no").toString(), u" no"_s); // throws, but that is ignored
+ child->setProperty("parent", QVariant::fromValue(sibling));
+
+ QCOMPARE(child->property("b").toInt(), 0);
+ QCOMPARE(child->property("i").toInt(), 28);
+ QCOMPARE(object->property("i").toInt(), 56);
+
+ child->setProperty("parent", QVariant::fromValue(evil));
+
+ QCOMPARE(child->property("b").toInt(), 5994);
+ QCOMPARE(object->property("c").toInt(), 5996);
+
+ QCOMPARE(child->property("i").toInt(), 443);
+ QCOMPARE(object->property("i").toInt(), 886);
+
+ {
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/specificParent.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->property("a").toReal(), 77.0);
+ }
}
-void tst_QmlCppCodegen::throwObjectName()
+void tst_QmlCppCodegen::popContextAfterRet()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/throwObjectName.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/popContextAfterRet.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
-
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/throwObjectName.qml:5:5: ouch");
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QVERIFY(o->objectName().isEmpty());
+ QVERIFY(o);
+
+ QCOMPARE(o->objectName(), QString());
+ o->setProperty("stackViewDepth", 1);
+ QCOMPARE(o->objectName(), u"backgroundImage"_s);
+ o->setProperty("stackViewDepth", 2);
+ QCOMPARE(o->objectName(), u"backgroundBlur"_s);
+ o->setProperty("stackViewDepth", 1);
+ QCOMPARE(o->objectName(), u"backgroundImage"_s);
}
-void tst_QmlCppCodegen::javaScriptArgument()
+void tst_QmlCppCodegen::prefixedType()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/javaScriptArgument.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ // We need to add an import path here because we cannot namespace the implicit import.
+ // The implicit import is what we use for all the other tests, even if we explicitly
+ // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
+ engine.addImportPath(u":/"_s);
+
+ const QUrl document(u"qrc:/qt/qml/TestTypes/prefixedMetaType.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QVERIFY(o);
- QCOMPARE(o->property("a").toDouble(), 4.0);
- QCOMPARE(o->property("b").toDouble(), 9.0);
- QCOMPARE(o->property("c").toString(), u"5t-1"_s);
- QCOMPARE(o->property("d").toString(), u"9"_s);
- QCOMPARE(o->property("e").toString(), u"10"_s);
- QCOMPARE(o->property("f").toString(), u"-10"_s);
+ QCOMPARE(o->property("state").toInt(), 2);
+ QVERIFY(qvariant_cast<QObject *>(o->property("a")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("b")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("c")) == nullptr);
- const QStringList scales {
- "0 ", "1 ", "10 ", "100 ", "1000 ", "9.77k", "97.7k", "977k", "9.54M", "95.4M", "954M",
- "9.31G", "93.1G", "931G", "9.09T", "-1 ", "-10 ", "-100 ", "-1000 ", "-9.77k", "-97.7k",
- "-977k", "-9.54M", "-95.4M", "-954M", "-9.31G", "-93.1G", "-931G", "-9.09T"
- };
+ QVERIFY(qvariant_cast<QObject *>(o->property("d")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("e")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("f")) == nullptr);
- QCOMPARE(o->property("scales").value<QStringList>(), scales);
+ QVERIFY(qvariant_cast<QObject *>(o->property("g")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("h")) != nullptr);
- double thing = 12.0;
- QString result;
- QMetaObject::invokeMethod(
- o.data(), "forwardArg", Q_RETURN_ARG(QString, result), Q_ARG(double, thing));
- QCOMPARE(result, u"12 ");
+ QCOMPARE(o->property("countG").toInt(), 11);
+ QCOMPARE(o->property("countH").toInt(), 11);
}
-void tst_QmlCppCodegen::translation()
+void tst_QmlCppCodegen::propertyOfParent()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/translation.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/RootWithoutId.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- QCOMPARE(o->property("translate2"), u"s"_s);
- QCOMPARE(o->property("translate3"), u"s"_s);
- QCOMPARE(o->property("translate4"), u"s"_s);
+ QObject *child = qmlContext(object.data())->objectForName(u"item"_s);
- QCOMPARE(o->property("translateNoop2"), u"s"_s);
- QCOMPARE(o->property("translateNoop3"), u"s"_s);
+ bool expected = false;
- QCOMPARE(o->property("tr1"), u"s"_s);
- QCOMPARE(o->property("tr2"), u"s"_s);
- QCOMPARE(o->property("tr3"), u"s"_s);
+ for (int i = 0; i < 3; ++i) {
+ const QVariant foo = object->property("foo");
+ QCOMPARE(foo.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(foo.toBool(), expected);
- QCOMPARE(o->property("trNoop1"), u"s"_s);
- QCOMPARE(o->property("trNoop2"), u"s"_s);
+ const QVariant bar = object->property("bar");
+ QCOMPARE(bar.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(bar.toBool(), expected);
- QCOMPARE(o->property("trId1"), u"s"_s);
- QCOMPARE(o->property("trId2"), u"s"_s);
+ const QVariant visible = child->property("visible");
+ QCOMPARE(visible.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(visible.toBool(), expected);
- QCOMPARE(o->property("trIdNoop1"), u"s"_s);
+ expected = !expected;
+ object->setProperty("foo", expected);
+ }
}
-void tst_QmlCppCodegen::stringArg()
+void tst_QmlCppCodegen::reduceWithNullThis()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringArg.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/reduceWithNullThis.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QCOMPARE(o->property("stringArg"), u"a foozly thing"_s);
- QCOMPARE(o->property("falseArg"), u"a 0 thing"_s);
- QCOMPARE(o->property("trueArg"), u"a 1 thing"_s);
- QCOMPARE(o->property("zeroArg"), u"a 0 thing"_s);
- QCOMPARE(o->property("intArg"), u"a 11 thing"_s);
- QCOMPARE(o->property("realArg"), u"a 12.25 thing"_s);
+ QCOMPARE(object->property("preferredHeight").toDouble(), 28.0);
+ QCOMPARE(object->property("preferredHeight2").toDouble(), 28.0);
}
-void tst_QmlCppCodegen::conversionDecrement()
+void tst_QmlCppCodegen::readEnumFromInstance()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionDecrement.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ const QString url = u"qrc:/qt/qml/TestTypes/readEnumFromInstance.qml"_s;
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QQmlComponent component(&engine, QUrl(url));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QCOMPARE(o->property("currentPageIndex").toInt(), 0);
- o->setProperty("pages", 5);
- QCOMPARE(o->property("currentPageIndex").toInt(), 3);
- o->setProperty("pages", 4);
- QCOMPARE(o->property("currentPageIndex").toInt(), 0);
- o->setProperty("pages", 6);
- QCOMPARE(o->property("currentPageIndex").toInt(), 4);
- o->setProperty("pages", 60);
- QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + ":7:5: Unable to assign [undefined] to int"_L1));
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("priority"), QVariant::fromValue<int>(0));
+ QCOMPARE(object->property("prop2"), QVariant::fromValue<int>(1));
+ QCOMPARE(object->property("priorityIsVeryHigh"), QVariant::fromValue<bool>(false));
+
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + ":13: Error: Cannot assign [undefined] to int"_L1));
+
+ int result = 0;
+ QMetaObject::invokeMethod(object.data(), "cyclePriority", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 0);
}
-void tst_QmlCppCodegen::unstoredUndefined()
+void tst_QmlCppCodegen::readonlyListProperty()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unstoredUndefined.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->objectName(), u"NaN"_s);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/readonlyListProperty.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("l").toInt(), 4);
+}
+
+void tst_QmlCppCodegen::registerElimination()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerelimination.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ // Increment of 23 hits both 0 and 460
+ for (int input = -23; input < 700; input += 23) {
+ object->setProperty("input", input);
+ if (input <= 0 || input >= 460)
+ QCOMPARE(object->property("output").toInt(), 459);
+ else
+ QCOMPARE(object->property("output").toInt(), input);
+ }
}
void tst_QmlCppCodegen::registerPropagation()
@@ -2431,784 +4139,989 @@ void tst_QmlCppCodegen::registerPropagation()
QCOMPARE(undefined, u"undefined"_s);
}
-void tst_QmlCppCodegen::argumentConversion()
+void tst_QmlCppCodegen::renameAdjust()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/argumentConversion.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/renameAdjust.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- auto checkNaN = [&](const char *propName) {
- const QVariant prop = o->property(propName);
- QCOMPARE(prop.metaType(), QMetaType::fromType<double>());
- QVERIFY(qIsNaN(prop.toDouble()));
- };
+ QTest::ignoreMessage(QtDebugMsg, "success");
+ QTest::ignoreMessage(QtCriticalMsg, "failed 10 11");
- checkNaN("a");
- checkNaN("b");
- checkNaN("e");
-
- QCOMPARE(o->property("c").toDouble(), 3.0);
- QCOMPARE(o->property("d").toDouble(), -1.0);
- QCOMPARE(o->property("f").toDouble(), 10.0);
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
}
-void tst_QmlCppCodegen::badSequence()
+void tst_QmlCppCodegen::resettableProperty()
{
+ QFETCH(QString, url);
+
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/badSequence.qml"_s));
+ QQmlComponent c(&engine, QUrl(url));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
-
- Person *self = qobject_cast<Person *>(o.data());
- QVERIFY(self);
- QVERIFY(self->barzles().isEmpty());
- QVERIFY(self->cousins().isEmpty());
-
- Person *other = o->property("other").value<Person *>();
- QVERIFY(other);
- QVERIFY(other->barzles().isEmpty());
- QVERIFY(other->cousins().isEmpty());
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":10:5: Unable to assign [undefined] to double"_s));
- Barzle f1;
- Barzle f2;
- const QList<Barzle *> barzles { &f1, &f2 };
- const QList<Person *> cousins { self, other };
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- other->setBarzles(barzles);
- QCOMPARE(self->barzles(), barzles);
- QCOMPARE(self->property("l").toInt(), 2);
+ QCOMPARE(o->property("value").toDouble(), 999);
+ QMetaObject::invokeMethod(o.data(), "doReset");
+ QCOMPARE(o->property("value").toDouble(), 0);
- other->setCousins(cousins);
- QCOMPARE(self->cousins(), cousins);
- QCOMPARE(self->property("m").toInt(), 2);
+ o->setProperty("value", double(82));
+ QCOMPARE(o->property("value").toDouble(), 82);
+ QMetaObject::invokeMethod(o.data(), "doReset2");
+ QCOMPARE(o->property("value").toDouble(), 0);
- QQmlListProperty<Person> others
- = self->property("others").value<QQmlListProperty<Person>>();
- QCOMPARE(others.count(&others), 2);
- QCOMPARE(others.at(&others, 0), cousins[0]);
- QCOMPARE(others.at(&others, 1), cousins[1]);
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":18: Error: Cannot assign [undefined] to double"_s));
+ QCOMPARE(o->property("notResettable").toDouble(), 10);
+ QMetaObject::invokeMethod(o.data(), "doNotReset");
+ QCOMPARE(o->property("notResettable").toDouble(), 10);
+ QCOMPARE(o->property("notResettable2").toDouble(), 0); // not NaN
- QQmlListProperty<Person> momsCousins
- = self->property("momsCousins").value<QQmlListProperty<Person>>();
- QCOMPARE(momsCousins.count(&momsCousins), 2);
- QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]);
- QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]);
+ o->setObjectName(u"namename"_s);
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":22: Error: Cannot assign [undefined] to QString"_s));
+ QMetaObject::invokeMethod(o.data(), "aaa");
+ QCOMPARE(o->objectName(), u"namename"_s);
+}
- QQmlListProperty<Person> dadsCousins
- = self->property("dadsCousins").value<QQmlListProperty<Person>>();
- QCOMPARE(dadsCousins.count(&dadsCousins), 1);
- QCOMPARE(dadsCousins.at(&dadsCousins, 0), other);
+void tst_QmlCppCodegen::resettableProperty_data()
+{
+ QTest::addColumn<QString>("url");
+ QTest::addRow("object lookups") << u"qrc:/qt/qml/TestTypes/resettable.qml"_s;
+ QTest::addRow("fallback lookups") << u"qrc:/qt/qml/TestTypes/fallbackresettable.qml"_s;
}
-void tst_QmlCppCodegen::enumLookup()
+void tst_QmlCppCodegen::returnAfterReject()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumLookup.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/returnAfterReject.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
-
- QCOMPARE(o->property("ready").toBool(), true);
+ QVERIFY(o);
+ QCOMPARE(o->property("bar").toInt(), 123);
}
-void tst_QmlCppCodegen::trivialSignalHandler()
+void tst_QmlCppCodegen::revisions()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/trivialSignalHandler.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/revisions.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(o->property("a").toString(), u"no"_s);
- QCOMPARE(o->property("b").toInt(), -1);
- QCOMPARE(o->property("b").toDouble(), -1.0);
+ QCOMPARE(o->property("delayed").toBool(), true);
+ QCOMPARE(o->property("gotten").toInt(), 5);
+}
- o->setObjectName(u"yes"_s);
- QCOMPARE(o->property("a").toString(), u"yes"_s);
- QCOMPARE(o->property("b").toInt(), 5);
- QCOMPARE(o->property("c").toDouble(), 2.5);
+void tst_QmlCppCodegen::scopeIdLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeIdLookup.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("objectName").toString(), u"outer"_s);
}
-void tst_QmlCppCodegen::stringToByteArray()
+void tst_QmlCppCodegen::scopeObjectDestruction()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringToByteArray.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fileDialog.qml"_s));
- Person *person = qobject_cast<Person *>(o.data());
- QVERIFY(person);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- QCOMPARE(person->dataBindable().value(), QByteArray("some data"));
- QCOMPARE(person->name(), u"some data"_s);
+ QObject *dialog = rootObject->property("dialog").value<QObject *>();
+ QVERIFY(dialog);
+
+ // We cannot check the warning messages. The AOT compiled code complains about reading the
+ // "parent" property of an object scheduled for deletion. The runtime silently returns undefined
+ // at that point and then complains about not being able to read a property on undefined.
+
+ // Doesn't crash, even though it triggers bindings on scope objects scheduled for deletion.
+ QMetaObject::invokeMethod(dialog, "open");
}
-void tst_QmlCppCodegen::listPropertyAsModel()
+void tst_QmlCppCodegen::scopeVsObject()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listPropertyAsModel.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
-
- QQmlListReference children(o.data(), "children");
- QCOMPARE(children.count(), 5);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeVsObject.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("objectName").toString(), u"foobar"_s);
}
-void tst_QmlCppCodegen::notNotString()
+void tst_QmlCppCodegen::scopedEnum()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notNotString.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ const QString url = u"qrc:/qt/qml/TestTypes/scopedEnum.qml"_s;
+ QQmlComponent component(&engine, QUrl(url));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QCOMPARE(o->property("notNotString").value<bool>(), false);
- o->setObjectName(u"a"_s);
- QCOMPARE(o->property("notNotString").value<bool>(), true);
-}
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url + u":6:5: Unable to assign [undefined] to int"_s));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url + u":8: TypeError: Cannot read property 'C' of undefined"_s));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url + u":14: TypeError: Cannot read property 'C' of undefined"_s));
-template<typename T>
-QString toOperand(double arg);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("good").toInt(), 27);
+ QCOMPARE(object->property("bad").toInt(), 0);
+ QCOMPARE(object->property("wrong").toInt(), 0);
+ QCOMPARE(object->property("right").toInt(), 7);
-template<>
-QString toOperand<double>(double arg)
-{
- if (qIsNull(arg))
- return std::signbit(arg) ? QStringLiteral("(-0)") : QStringLiteral("(0)");
+ QCOMPARE(object->property("notgood").toInt(), 26);
+ QCOMPARE(object->property("notbad").toInt(), 26);
+ QCOMPARE(object->property("notwrong").toInt(), 0);
+ QCOMPARE(object->property("notright").toInt(), 6);
- return u'(' + QJSPrimitiveValue(arg).toString() + u')';
+ QCOMPARE(object->property("passable").toInt(), 2);
+ QCOMPARE(object->property("wild").toInt(), 1);
}
-template<>
-QString toOperand<int>(double arg)
+void tst_QmlCppCodegen::sequenceToIterable()
{
- const int iArg = QJSPrimitiveValue(arg).toInteger();
- return u'(' + QJSPrimitiveValue(iArg).toString() + u')';
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/sequenceToIterable.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("c").toInt(), 11);
+
+ QQmlListReference children(object.data(), "children");
+ QCOMPARE(children.count(), 11);
+ static const QRegularExpression name("Entry\\(0x[0-9a-f]+, \"Item ([0-9])\"\\): ([0-9])");
+ for (int i = 0; i < 10; ++i) {
+ const auto match = name.match(children.at(i)->objectName());
+ QVERIFY(match.hasMatch());
+ QCOMPARE(match.captured(1), QString::number(i));
+ }
}
-template<>
-QString toOperand<bool>(double arg)
+void tst_QmlCppCodegen::setLookupConversion()
{
- const bool bArg = QJSPrimitiveValue(arg).toBoolean();
- return u'(' + QJSPrimitiveValue(bArg).toString() + u')';
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/setLookupConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->objectName().isEmpty());
+ QMetaObject::invokeMethod(o.data(), "t");
+ QCOMPARE(o->objectName(), u"a"_s);
+ QCOMPARE(o->property("value").toInt(), 9);
}
-template<typename T1, typename T2>
-double jsEval(double arg1, double arg2, const QString &op, QJSEngine *engine)
+void tst_QmlCppCodegen::setLookupOriginalScope()
{
- auto evalBinary = [&](const QString &jsOp) {
- return engine->evaluate(toOperand<T1>(arg1) + jsOp + toOperand<T2>(arg2)).toNumber();
- };
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/setLookupOriginalScope.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- auto evalBinaryConst = [&](const QString &jsOp) {
- return engine->evaluate(toOperand<T1>(arg1) + jsOp + u'9').toNumber();
- };
+ QObject *v = o->property("variable").value<QObject *>();
+ QCOMPARE(v->property("value").toInt(), 0);
- auto evalUnary = [&](const QString &jsOp) {
- return engine->evaluate(jsOp + toOperand<T1>(arg1)).toNumber();
- };
+ QMetaObject::invokeMethod(o.data(), "trigger");
+ QObject *edit = o->property("edit").value<QObject *>();
+ QVERIFY(edit);
+ QCOMPARE(edit->property("myOutput").value<QObject *>(), v);
+ QCOMPARE(v->property("value").toInt(), 55);
+}
- auto evalInPlace = [&](const QString &jsOp) {
- return engine->evaluate(
- u"(function() {var a = "_s + toOperand<T1>(arg1)+ u"; return "_s
- + jsOp + u"a;})()"_s).toNumber();
- };
+void tst_QmlCppCodegen::shadowedAsCasts()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedAsCasts.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+
+ QObject *shadowed1 = obj->property("shadowed1").value<QObject *>();
+ QVERIFY(shadowed1);
+ QVERIFY(shadowed1->objectName().isEmpty());
+ const QVariant name1 = shadowed1->property("objectName");
+ QCOMPARE(name1.metaType(), QMetaType::fromType<int>());
+ QCOMPARE(name1.toInt(), 43);
+
+ QObject *shadowed2 = obj->property("shadowed2").value<QObject *>();
+ QVERIFY(shadowed2);
+ QVERIFY(shadowed2->objectName().isEmpty());
+ const QVariant name2 = shadowed2->property("objectName");
+ QCOMPARE(name2.metaType(), QMetaType::fromType<int>());
+ QCOMPARE(name2.toInt(), 42);
+
+ QObject *shadowed3 = obj->property("shadowed3").value<QObject *>();
+ QVERIFY(shadowed3);
+ QVERIFY(shadowed3->objectName().isEmpty());
+ const QVariant name3 = shadowed3->property("objectName");
+ QCOMPARE(name3.metaType(), QMetaType::fromType<double>());
+ QCOMPARE(name3.toDouble(), 41.0);
+}
+
+void tst_QmlCppCodegen::shadowedMethod()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedMethod.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("athing"), QVariant::fromValue<bool>(false));
+ QCOMPARE(o->property("bthing"), QVariant::fromValue(u"b"_s));
+ QCOMPARE(o->property("cthing"), QVariant::fromValue(u"c"_s));
+}
- if (op == u"unot")
- return evalUnary(u"!"_s);
- if (op == u"uplus")
- return evalUnary(u"+"_s);
- if (op == u"uminus")
- return evalUnary(u"-"_s);
- if (op == u"ucompl")
- return evalUnary(u"~"_s);
+void tst_QmlCppCodegen::shadowedPrimitiveCmpEqNull()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedPrimitiveCmpEqNull.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
- if (op == u"increment")
- return evalInPlace(u"++"_s);
- if (op == u"decrement")
- return evalInPlace(u"--"_s);
+void tst_QmlCppCodegen::shifts()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/shifts.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- if (op == u"add")
- return evalBinary(u"+"_s);
- if (op == u"sub")
- return evalBinary(u"-"_s);
- if (op == u"mul")
- return evalBinary(u"*"_s);
- if (op == u"div")
- return evalBinary(u"/"_s);
- if (op == u"exp")
- return evalBinary(u"**"_s);
- if (op == u"mod")
- return evalBinary(u"%"_s);
+ QCOMPARE(object->property("a").toInt(), 9728);
+ QCOMPARE(object->property("b").toInt(), 4864);
+ QCOMPARE(object->property("c").toInt(), 19448);
+ QCOMPARE(object->property("d").toInt(), 9731);
+ QCOMPARE(object->property("e").toInt(), 0);
+}
- if (op == u"bitAnd")
- return evalBinary(u"&"_s);
- if (op == u"bitOr")
- return evalBinary(u"|"_s);
- if (op == u"bitXor")
- return evalBinary(u"^"_s);
+void tst_QmlCppCodegen::signalHandler()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signal.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->objectName(), QString());
+ QCOMPARE(object->property("ff").toInt(), 4);
- if (op == u"bitAndConst")
- return evalBinaryConst(u"&"_s);
- if (op == u"bitOrConst")
- return evalBinaryConst(u"|"_s);
- if (op == u"bitXorConst")
- return evalBinaryConst(u"^"_s);
+ object->setObjectName(u"foo"_s);
+ QCOMPARE(object->property("ff").toInt(), 12);
+}
- if (op == u"ushr")
- return evalBinary(u">>>"_s);
- if (op == u"shr")
- return evalBinary(u">>"_s);
- if (op == u"shl")
- return evalBinary(u"<<"_s);
+void tst_QmlCppCodegen::signalIndexMismatch()
+{
+ QQmlEngine engine;
- if (op == u"ushrConst")
- return evalBinaryConst(u">>>"_s);
- if (op == u"shrConst")
- return evalBinaryConst(u">>"_s);
- if (op == u"shlConst")
- return evalBinaryConst(u"<<"_s);
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalIndexMismatch.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- qDebug() << op;
- Q_UNREACHABLE_RETURN(0);
+ QScopedPointer<QObject> item(c1.create());
+ const auto visualIndexBeforeMoveList = item->property("visualIndexBeforeMove").toList();
+ const auto visualIndexAfterMoveList = item->property("visualIndexAfterMove").toList();
+
+ QCOMPARE(visualIndexBeforeMoveList, QList<QVariant>({ 0, 1, 2 }));
+ QCOMPARE(visualIndexAfterMoveList, QList<QVariant>({ 0, 1, 2 }));
}
-void tst_QmlCppCodegen::mathOperations()
+void tst_QmlCppCodegen::signalsWithLists()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathOperations.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalsWithLists.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- const QMetaObject *metaObject = o->metaObject();
+ QVariantList varlist = o->property("varlist").toList();
+ QCOMPARE(varlist.size(), 5);
+ QCOMPARE(varlist[0], QVariant::fromValue(1));
+ QCOMPARE(varlist[1], QVariant::fromValue(u"foo"_s));
+ QCOMPARE(varlist[2], QVariant::fromValue(o.data()));
+ QCOMPARE(varlist[3], QVariant());
+ QCOMPARE(varlist[4], QVariant::fromValue(true));
- char t1;
- char t2;
- QString name;
- const auto guard = qScopeGuard([&]() {
- if (QTest::currentTestFailed()) {
- qDebug() << t1 << t2 << name << "failed on:";
- qDebug() << "doubles" << o->property("a").toDouble() << o->property("b").toDouble();
- qDebug() << "integers" << o->property("ia").toInt() << o->property("ib").toInt();
- qDebug() << "booleans" << o->property("ba").toBool() << o->property("bb").toBool();
- }
- });
+ QQmlListProperty<QObject> objlist = o->property("objlist").value<QQmlListProperty<QObject>>();
+ QCOMPARE(objlist.count(&objlist), 3);
+ QCOMPARE(objlist.at(&objlist, 0), o.data());
+ QCOMPARE(objlist.at(&objlist, 1), nullptr);
+ QCOMPARE(objlist.at(&objlist, 2), o.data());
- for (double a : numbers) {
- for (double b : numbers) {
- o->setProperty("a", a);
- o->setProperty("b", b);
- for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) {
- const QMetaProperty prop = metaObject->property(i);
- const QByteArray propName = prop.name();
+ QCOMPARE(o->property("happening").toInt(), 0);
+ o->metaObject()->invokeMethod(o.data(), "sendSignals");
+ QCOMPARE(o->property("happening").toInt(), 8);
+}
- if (propName.size() < 3 || propName == "objectName")
- continue;
+void tst_QmlCppCodegen::signatureIgnored()
+{
+ QQmlEngine engine;
- t1 = propName[0];
- t2 = propName[1];
- name = QString::fromUtf8(propName.mid(2));
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signatureIgnored.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- double expected;
+ QScopedPointer<QObject> ignored(c1.create());
+ QCOMPARE(ignored->property("l").toInt(), 5);
+ QCOMPARE(ignored->property("m").toInt(), 77);
+ QCOMPARE(ignored->property("n").toInt(), 67);
+}
- switch (t2) {
- case 'd':
- case '_':
- switch (t1) {
- case 'd':
- expected = jsEval<double, double>(a, b, name, &engine);
- break;
- case 'i':
- expected = jsEval<int, double>(a, b, name, &engine);
- break;
- case 'b':
- expected = jsEval<bool, double>(a, b, name, &engine);
- break;
- }
- break;
- case 'i':
- switch (t1) {
- case 'd':
- expected = jsEval<double, int>(a, b, name, &engine);
- break;
- case 'i':
- expected = jsEval<int, int>(a, b, name, &engine);
- break;
- case 'b':
- expected = jsEval<bool, int>(a, b, name, &engine);
- break;
- }
- break;
- case 'b':
- switch (t1) {
- case 'd':
- expected = jsEval<double, bool>(a, b, name, &engine);
- break;
- case 'i':
- expected = jsEval<int, bool>(a, b, name, &engine);
- break;
- case 'b':
- expected = jsEval<bool, bool>(a, b, name, &engine);
- break;
- }
- break;
- }
+void tst_QmlCppCodegen::simpleBinding()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
+ QCOMPARE(object->property("foo").toInt(), int(3));
- const double result = prop.read(o.data()).toDouble();
- QCOMPARE(result, expected);
- }
- }
+ {
+ CppBaseClass *base = qobject_cast<CppBaseClass *>(object.data());
+ Q_ASSERT(base);
+ QVERIFY(!base->cppProp.hasBinding());
+ QCOMPARE(base->cppProp.value(), 7);
+ QVERIFY(base->cppProp2.hasBinding());
+ QCOMPARE(base->cppProp2.value(), 14);
+ base->cppProp.setValue(9);
+ QCOMPARE(base->cppProp.value(), 9);
+ QCOMPARE(base->cppProp2.value(), 18);
}
}
-void tst_QmlCppCodegen::inaccessibleProperty()
+void tst_QmlCppCodegen::storeElementSideEffects()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/versionmismatch.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/storeElementSideEffects.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(o->property("c").toInt(), 5);
+ const QJSValue prop = o->property("myItem").value<QJSValue>();
+ QVERIFY(prop.isArray());
+ QCOMPARE(prop.property(0).toInt(), 10);
}
-void tst_QmlCppCodegen::typePropagationLoop()
+void tst_QmlCppCodegen::storeMetaEnum()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropagationLoop.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/StoreMetaEnum.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(o->property("j").toInt(), 3);
+ QCOMPARE(o->property("bar").toInt(), 0);
+ QCOMPARE(o->property("baz").toInt(), 1);
}
-void tst_QmlCppCodegen::signatureIgnored()
+void tst_QmlCppCodegen::stringArg()
{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringArg.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signatureIgnored.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
-
- QScopedPointer<QObject> ignored(c1.create());
- QCOMPARE(ignored->property("l").toInt(), 5);
- QCOMPARE(ignored->property("m").toInt(), 77);
- QCOMPARE(ignored->property("n").toInt(), 67);
+ QCOMPARE(o->property("stringArg"), u"a foozly thing"_s);
+ QCOMPARE(o->property("falseArg"), u"a 0 thing"_s);
+ QCOMPARE(o->property("trueArg"), u"a 1 thing"_s);
+ QCOMPARE(o->property("zeroArg"), u"a 0 thing"_s);
+ QCOMPARE(o->property("intArg"), u"a 11 thing"_s);
+ QCOMPARE(o->property("realArg"), u"a 12.25 thing"_s);
}
-void tst_QmlCppCodegen::listAsArgument()
+void tst_QmlCppCodegen::stringLength()
{
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringLength.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("stringLength").toInt(), 8);
+}
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listAsArgument.qml"_s));
+void tst_QmlCppCodegen::stringToByteArray()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringToByteArray.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
-
QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->property("i").toInt(), 4);
- QCOMPARE(o->property("j").toInt(), 2);
- QCOMPARE(o->property("i1").toInt(), 2);
- QCOMPARE(o->property("i2").toInt(), 4);
- QCOMPARE(o->property("d").value<QObject *>()->objectName(), u"this one"_s);
- int singleInt = 0;
- QList<int> moreInts;
- QMetaObject::invokeMethod(o.data(), "returnInts1", Q_RETURN_ARG(QList<int>, moreInts));
- QCOMPARE(moreInts, QList<int>({5, 4, 3, 2, 1}));
- QMetaObject::invokeMethod(o.data(), "selectSecondInt", Q_RETURN_ARG(int, singleInt), Q_ARG(QList<int>, moreInts));
- QCOMPARE(singleInt, 4);
+ Person *person = qobject_cast<Person *>(o.data());
+ QVERIFY(person);
+
+ QCOMPARE(person->dataBindable().value(), QByteArray("some data"));
+ QCOMPARE(person->name(), u"some data"_s);
}
-void tst_QmlCppCodegen::letAndConst()
+void tst_QmlCppCodegen::structuredValueType()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/letAndConst.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/structuredValueType.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"ab"_s);
+
+ QCOMPARE(o->property("r").value<QRectF>(), QRectF(1, 2, 3, 4));
+ QCOMPARE(o->property("r2").value<QRectF>(), QRectF(42, 0, 0, 0));
+
+ WeatherModelUrl w;
+ w.setStrings(QStringList({"one", "two", "three"}));
+
+ QCOMPARE(o->property("w").value<WeatherModelUrl>(), w);
}
-void tst_QmlCppCodegen::signalIndexMismatch()
+void tst_QmlCppCodegen::testIsnan()
{
QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/isnan.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalIndexMismatch.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QCOMPARE(o->property("good").toDouble(), 10.1);
+ QVERIFY(qIsNaN(o->property("bad").toDouble()));
- QScopedPointer<QObject> item(c1.create());
- const auto visualIndexBeforeMoveList = item->property("visualIndexBeforeMove").toList();
- const auto visualIndexAfterMoveList = item->property("visualIndexAfterMove").toList();
+ const QVariant a = o->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(!a.toBool());
- QCOMPARE(visualIndexBeforeMoveList, QList<QVariant>({ 0, 1, 2 }));
- QCOMPARE(visualIndexAfterMoveList, QList<QVariant>({ 0, 1, 2 }));
+ const QVariant b = o->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(b.toBool());
}
-void tst_QmlCppCodegen::callWithSpread()
+void tst_QmlCppCodegen::thisObject()
{
- QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callWithSpread.qml"_s));
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/thisObject.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QTest::ignoreMessage(QtCriticalMsg, "That is great!");
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
+ QCOMPARE(o->property("warned").value<QObject *>(), o.data());
}
-void tst_QmlCppCodegen::nullComparison()
+void tst_QmlCppCodegen::throwObjectName()
{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/throwObjectName.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullComparison.qml"_s));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/throwObjectName.qml:5:5: ouch");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->objectName().isEmpty());
+}
+
+void tst_QmlCppCodegen::topLevelComponent()
+{
+ // TODO: Once we stop accepting top level Component elements, this test can be removed.
+
+ QQmlEngine e;
+
+ const QUrl url(u"qrc:/qt/qml/TestTypes/topLevelComponent.qml"_s);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + u":4:1: Using a Component as the root of a QML document "
+ "is deprecated: types defined in qml documents are "
+ "automatically wrapped into Components when needed."_s));
+
+ QQmlComponent c(&e, url);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->property("v").toInt(), 1);
- QCOMPARE(o->property("w").toInt(), 3);
- QCOMPARE(o->property("x").toInt(), 1);
- QCOMPARE(o->property("y").toInt(), 5);
+ QQmlComponent *inner = qobject_cast<QQmlComponent *>(o.data());
+ QVERIFY(inner);
+
+ QScopedPointer<QObject> o2(inner->create());
+ QCOMPARE(o2->objectName(), u"foo"_s);
}
-void tst_QmlCppCodegen::consoleObject()
+void tst_QmlCppCodegen::translation()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleObject.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/translation.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtInfoMsg, "b 4.55");
- QTest::ignoreMessage(QtWarningMsg, "b 4.55");
- QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
+ QCOMPARE(o->property("translate2"), u"s"_s);
+ QCOMPARE(o->property("translate3"), u"s"_s);
+ QCOMPARE(o->property("translate4"), u"s"_s);
- // Unfortunately we cannot check the logging category with QTest::ignoreMessage
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtInfoMsg, "b 4.55");
- QTest::ignoreMessage(QtWarningMsg, "b 4.55");
- QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
+ QCOMPARE(o->property("translateNoop2"), u"s"_s);
+ QCOMPARE(o->property("translateNoop3"), u"s"_s);
- const QRegularExpression re(u"QQmlComponentAttached\\(0x[0-9a-f]+\\) b 4\\.55"_s);
- QTest::ignoreMessage(QtDebugMsg, re);
- QTest::ignoreMessage(QtDebugMsg, re);
- QTest::ignoreMessage(QtInfoMsg, re);
- QTest::ignoreMessage(QtWarningMsg, re);
- QTest::ignoreMessage(QtCriticalMsg, re);
+ QCOMPARE(o->property("tr1"), u"s"_s);
+ QCOMPARE(o->property("tr2"), u"s"_s);
+ QCOMPARE(o->property("tr3"), u"s"_s);
- QTest::ignoreMessage(QtDebugMsg, "a undefined b false null 7");
- QTest::ignoreMessage(QtDebugMsg, "");
- QTest::ignoreMessage(QtDebugMsg, "4");
- QTest::ignoreMessage(QtDebugMsg, "");
+ QCOMPARE(o->property("trNoop1"), u"s"_s);
+ QCOMPARE(o->property("trNoop2"), u"s"_s);
- const QRegularExpression re2(u"QQmlComponentAttached\\(0x[0-9a-f]+\\)"_s);
- QTest::ignoreMessage(QtDebugMsg, re2);
+ QCOMPARE(o->property("trId1"), u"s"_s);
+ QCOMPARE(o->property("trId2"), u"s"_s);
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QCOMPARE(o->property("trIdNoop1"), u"s"_s);
}
-void tst_QmlCppCodegen::multiForeign()
+void tst_QmlCppCodegen::trigraphs()
{
- QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiforeign.qml"_s));
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/trigraphs.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"not here and not there"_s);
+ QCOMPARE(o->objectName(), u"?""?= ?""?/ ?""?' ?""?( ?""?) ?""?! ?""?< ?""?> ?""?-"_s);
}
-void tst_QmlCppCodegen::namespaceWithEnum()
+void tst_QmlCppCodegen::trivialSignalHandler()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/namespaceWithEnum.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/trivialSignalHandler.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->property("i").toInt(), 2);
+
+ QCOMPARE(o->property("a").toString(), u"no"_s);
+ QCOMPARE(o->property("b").toInt(), -1);
+ QCOMPARE(o->property("b").toDouble(), -1.0);
+
+ o->setObjectName(u"yes"_s);
+ QCOMPARE(o->property("a").toString(), u"yes"_s);
+ QCOMPARE(o->property("b").toInt(), 5);
+ QCOMPARE(o->property("c").toDouble(), 2.5);
}
-void tst_QmlCppCodegen::enumProblems()
+void tst_QmlCppCodegen::typePropagationLoop()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumProblems.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> outer(c.create());
- QVERIFY(!outer.isNull());
- QObject *inner = outer->property("o").value<QObject *>();
- QVERIFY(inner);
- Foo *bar = inner->property("bar").value<Foo *>();
- QVERIFY(bar);
- QCOMPARE(bar->type(), Foo::Component);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropagationLoop.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- Foo *fighter = inner->property("fighter").value<Foo *>();
- QVERIFY(fighter);
- QCOMPARE(fighter->type(), Foo::Fighter);
+ QCOMPARE(o->property("j").toInt(), 3);
}
-void tst_QmlCppCodegen::enumConversion()
+void tst_QmlCppCodegen::typePropertyClash()
{
QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumConversion.qml"_s));
+ engine.rootContext()->setContextProperty(u"size"_s, 5);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropertyClash.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"Size: 5"_s);
+}
+void tst_QmlCppCodegen::typedArray()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/typedArray.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(o);
- QCOMPARE(o->property("test").toInt(), 0x04);
- QCOMPARE(o->property("test_1").toBool(), true);
+ QDateTime date;
+ QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty());
+ QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")),
+ QList<int>({1, 2, 3, 4}));
+ QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
+ QList<QDateTime>({date, date, date}));
+ {
+ const QList<double> actual
+ = qvariant_cast<QList<double>>(o->property("values5"));
+ const QList<double> expected
+ = QList<double>({1, 2, 3.4, 30, std::numeric_limits<double>::quiet_NaN(), 0});
+ QCOMPARE(actual.size(), expected.size());
+ for (qsizetype i = 0, end = actual.size(); i != end; ++i) {
+ if (std::isnan(expected[i]))
+ QVERIFY(std::isnan(actual[i]));
+ else
+ QCOMPARE(actual[i], expected[i]);
+ }
+ }
+ date = QDateTime::currentDateTime();
+ o->setProperty("aDate", date);
+ QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
+ QList<QDateTime>({date, date, date}));
+
+ QQmlListProperty<QObject> values6
+ = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6"));
+ QCOMPARE(values6.count(&values6), 3);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values6.at(&values6, i), o.data());
+
+ QCOMPARE(o->property("inIntList").toInt(), 2);
+ QCOMPARE(qvariant_cast<QDateTime>(o->property("inDateList")), date);
+ QCOMPARE(o->property("inRealList").toDouble(), 30.0);
+ QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f"));
+
+ const QMetaObject *metaObject = o->metaObject();
+ QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)"));
+ QVERIFY(method.isValid());
+
+ // If LoadElement threw an exception the function would certainly return neither 10 nor 20.
+ int result = 0;
+ method.invoke(
+ o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a")));
+ QCOMPARE(result, 10);
+ method.invoke(
+ o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa")));
+ QCOMPARE(result, 20);
}
-void tst_QmlCppCodegen::storeElementSideEffects()
+void tst_QmlCppCodegen::undefinedResets()
{
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedResets.qml"_s));
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/storeElementSideEffects.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ Person *person = qobject_cast<Person *>(rootObject.data());
+ QVERIFY(person);
+ QCOMPARE(person->shoeSize(), 0);
+ QCOMPARE(person->name(), u"Marge"_s);
- const QJSValue prop = o->property("myItem").value<QJSValue>();
- QVERIFY(prop.isArray());
- QCOMPARE(prop.property(0).toInt(), 10);
-};
+ person->setShoeSize(11);
-void tst_QmlCppCodegen::ambiguousSignals()
+ QCOMPARE(person->shoeSize(), 11);
+ QCOMPARE(person->name(), u"Bart"_s);
+
+ person->setShoeSize(10);
+ QCOMPARE(person->shoeSize(), 10);
+ QCOMPARE(person->name(), u"Marge"_s);
+
+ person->setName(u"no one"_s);
+ QCOMPARE(person->name(), u"no one"_s);
+
+ person->setObjectName(u"the one"_s);
+ QCOMPARE(person->name(), u"Bart"_s);
+}
+
+void tst_QmlCppCodegen::undefinedToDouble()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguousSignals.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedToDouble.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"tomorrow"_s);
- Person *p = qobject_cast<Person *>(o.data());
- QVERIFY(p);
- emit p->ambiguous(12);
- QCOMPARE(o->objectName(), u"12foo"_s);
- emit p->ambiguous();
- QCOMPARE(o->objectName(), u"9foo"_s);
+ const QVariant d = o->property("d");
+ QCOMPARE(d.metaType(), QMetaType::fromType<double>());
+ QVERIFY(std::isnan(d.toDouble()));
}
-void tst_QmlCppCodegen::fileImportsContainCxxTypes()
+void tst_QmlCppCodegen::unknownAttached()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/usingCxxTypesFromFileImports.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"horst guenther"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownAttached.qml"_s));
+ QVERIFY(c.isError());
}
-void tst_QmlCppCodegen::lengthAccessArraySequenceCompat()
+void tst_QmlCppCodegen::unknownParameter()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ArraySequenceLengthInterop.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownParameter.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("cppProp").toInt(), 18);
+}
+
+void tst_QmlCppCodegen::unstoredUndefined()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unstoredUndefined.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->property("length").toInt(), 100);
+ QCOMPARE(o->objectName(), u"NaN"_s);
}
-static QList<QString> convertToStrings(const QList<int> &ints)
+void tst_QmlCppCodegen::unusedAttached()
{
- QList<QString> strings;
- for (int i : ints)
- strings.append(QString::number(i));
- return strings;
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unusedAttached.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ const auto func = qmlAttachedPropertiesFunction(
+ object.data(), QMetaType::fromName("QQuickKeyNavigationAttached*").metaObject());
+ QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
+ const QVariant prop = attached->property("priority");
+ QVERIFY(prop.isValid());
+ QCOMPARE(QByteArray(prop.metaType().name()), "QQuickKeyNavigationAttached::Priority");
+ bool ok = false;
+ QCOMPARE(prop.toInt(&ok), 0);
+ QVERIFY(ok);
}
-void tst_QmlCppCodegen::numbersInJsPrimitive()
+void tst_QmlCppCodegen::urlString()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/numbersInJsPrimitive.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/urlString.qml"_s));
- const QList<int> zeroes = {0, 0, 0, 0};
- const QList<int> written = {39, 40, 41, 42};
- const QList<int> stored = {1334, 1335, 1336, 1337};
- QStringList asStrings(4);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- for (int i = 0; i < 4; ++i) {
- QMetaObject::invokeMethod(
- o.data(), "readValueAsString",
- Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s));
+ QCOMPARE(rootObject->objectName(), QLatin1String("http://dddddd.com"));
+}
+
+void tst_QmlCppCodegen::valueTypeBehavior()
+{
+ QQmlEngine engine;
+
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeCopy.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e'));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f'));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("e").toDouble(), 45.0);
+ QCOMPARE(o->property("f").toDouble(), 1.0);
}
- QCOMPARE(asStrings, convertToStrings(zeroes));
- QMetaObject::invokeMethod(o.data(), "writeValues");
- for (int i = 0; i < 4; ++i) {
- QMetaObject::invokeMethod(
- o.data(), "readValueAsString",
- Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeReference.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e'));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f'));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(qIsNaN(o->property("e").toDouble()));
+ QCOMPARE(o->property("f").toDouble(), 5.0);
}
- QCOMPARE(asStrings, convertToStrings(written));
- QMetaObject::invokeMethod(o.data(), "storeValues");
- for (int i = 0; i < 4; ++i) {
- QMetaObject::invokeMethod(
- o.data(), "readValueAsString",
- Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeDefault.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e'));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f'));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(qIsNaN(o->property("e").toDouble()));
+ QCOMPARE(o->property("f").toDouble(), 5.0);
+ }
+
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeCast.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("x"), 10);
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString()
+ + u":8: TypeError: Cannot read property 'x' of undefined"_s));
+ o->setProperty("v", QLatin1String("not a rect"));
+
+ // If the binding throws an exception, the value doesn't change.
+ QCOMPARE(o->property("x"), 10);
+
+ QCOMPARE(o->property("tv3"), 5);
+ QCOMPARE(o->property("tc3"), 5);
+ QCOMPARE(o->property("tc6"), QVariant());
+ QCOMPARE(o->property("tc7"), QVariant());
+ QCOMPARE(o->property("tc8"), 2);
+
+ // The default greeting is never applied because undefined can be coerced to string
+ QCOMPARE(o->property("greeting1"), QLatin1String("undefined"));
+ QCOMPARE(o->property("greeting2"), QLatin1String("Custom Greeting"));
}
- QCOMPARE(asStrings, convertToStrings(stored));
}
-void tst_QmlCppCodegen::infinitiesToInt()
+void tst_QmlCppCodegen::valueTypeLists()
{
QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinitiesToInt.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeLists.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- const char *props[] = {"a", "b", "c"};
- for (const char *prop : props) {
- const QVariant i = o->property(prop);
- QCOMPARE(i.metaType(), QMetaType::fromType<int>());
- bool ok = false;
- QCOMPARE(i.toInt(&ok), 0);
- QVERIFY(ok);
- }
+ QCOMPARE(qvariant_cast<QRectF>(o->property("rectInBounds")), QRectF(1, 2, 3, 4));
+ QVERIFY(o->metaObject()->indexOfProperty("rectOutOfBounds") > 0);
+ QVERIFY(!o->property("rectOutOfBounds").isValid());
+
+ QCOMPARE(qvariant_cast<QString>(o->property("stringInBounds")), QStringLiteral("bbb"));
+ QVERIFY(o->metaObject()->indexOfProperty("stringOutOfBounds") > 0);
+ QVERIFY(!o->property("stringOutOfBounds").isValid());
+
+ QCOMPARE(qvariant_cast<int>(o->property("intInBounds")), 7);
+ QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0);
+ QVERIFY(!o->property("intOutOfBounds").isValid());
+
+ QCOMPARE(qvariant_cast<QString>(o->property("charInBounds")), QStringLiteral("d"));
+ QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0);
+ QVERIFY(!o->property("charOutOfBounds").isValid());
}
-void tst_QmlCppCodegen::equalityVarAndNonStorable()
+void tst_QmlCppCodegen::valueTypeProperty()
{
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeProperty.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityVarAndNonStorable.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QFont font = qvariant_cast<QFont>(object->property("font"));
+ QCOMPARE(object->property("foo").toString(), font.family());
+ font.setFamily(u"Bar"_s);
+ object->setProperty("font", QVariant::fromValue(font));
+ QCOMPARE(object->property("foo").toString(), u"Bar"_s);
+}
- QScopedPointer<QObject> object(c1.create());
- QVERIFY(!object.isNull() && !c1.isError());
- QVERIFY(!object->property("aIsNull").toBool());
- QVERIFY(object->property("aIsNotNull").toBool());
- QVERIFY(object->property("aIsNotUndefined").toBool());
- QVERIFY(object->property("objectIsNotNull").toBool());
- QVERIFY(!object->property("typedArrayIsNull").toBool());
- QVERIFY(object->property("isUndefined").toBool());
- QVERIFY(!object->property("derivedIsNull").toBool());
+void tst_QmlCppCodegen::variantMapLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantMapLookup.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("i"), 42);
+}
- QVERIFY(object->property("primitiveIsNull").toBool());
- QVERIFY(object->property("primitiveIsDefined").toBool());
- QVERIFY(object->property("primitiveIsUndefined").toBool());
+void tst_QmlCppCodegen::variantReturn()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/variantReturn.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QVERIFY(object->property("jsValueIsNull").toBool());
- QVERIFY(object->property("jsValueIsDefined").toBool());
- QVERIFY(object->property("jsValueIsUndefined").toBool());
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QVERIFY(object->property("nullVarIsUndefined").toBool());
- QVERIFY(object->property("nullIsUndefined").toBool());
- QVERIFY(object->property("nullVarIsNull").toBool());
- QVERIFY(object->property("nullIsNotUndefined").toBool());
-};
+ QObject *a = o->property("a").value<QObject *>();
+ QVERIFY(a);
+ const QVariant x = a->property("x");
+ const QMetaObject *meta = x.metaType().metaObject();
+ QVERIFY(meta);
+ const QMetaProperty property = meta->property(meta->indexOfProperty("timeIndex"));
+ QVERIFY(property.isValid());
+ const QVariant timeIndex = property.readOnGadget(x.data());
+ QCOMPARE(timeIndex.metaType(), QMetaType::fromType<qsizetype>());
+ QCOMPARE(timeIndex.value<qsizetype>(), qsizetype(1));
-void tst_QmlCppCodegen::equalityQObjects()
+ QObject *b = o->property("b").value<QObject *>();
+ QVERIFY(b);
+ QCOMPARE(b->property("z").toInt(), 2);
+}
+
+void tst_QmlCppCodegen::variantlist()
{
QQmlEngine engine;
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQObjects.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QScopedPointer<QObject> object(c1.create());
- QVERIFY(!object.isNull() && !c1.isError());
-
- QVERIFY(object->property("derivedIsNotNull").toBool());
- QVERIFY(object->property("nullObjectIsNull").toBool());
- QVERIFY(object->property("nonNullObjectIsNotNull").toBool());
- QVERIFY(object->property("compareSameObjects").toBool());
- QVERIFY(object->property("compareDifferentObjects").toBool());
- QVERIFY(object->property("compareObjectWithNullObject").toBool());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantlist.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QVERIFY(object->property("nonStrict_derivedIsNotNull").toBool());
- QVERIFY(object->property("nonStrict_nullObjectIsNull").toBool());
- QVERIFY(object->property("nonStrict_nonNullObjectIsNotNull").toBool());
- QVERIFY(object->property("nonStrict_compareSameObjects").toBool());
- QVERIFY(object->property("nonStrict_compareDifferentObjects").toBool());
- QVERIFY(object->property("nonStrict_compareObjectWithNullObject").toBool());
+ const QVariantList things = qvariant_cast<QVariantList>(o->property("things"));
+ QCOMPARE(things.size(), 2);
+ QCOMPARE(things[0].toString(), u"thing"_s);
+ QCOMPARE(things[1].toInt(), 30);
}
-void tst_QmlCppCodegen::dateConversions()
+void tst_QmlCppCodegen::variantMap()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantMap.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug");
-
- const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate());
- const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime());
-
- QCOMPARE(o->property("date").value<QDateTime>(), refDate);
- QCOMPARE(o->property("time").value<QDateTime>(), refTime);
-
- QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDateTime, QString>(refDate)));
- QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QDateTime, QString>(refTime)));
+ QCOMPARE(o->objectName(), "a b"_L1);
+ QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(12, 13, 14, 15)));
- QMetaObject::invokeMethod(o.data(), "shuffle");
+ const QVariantMap expected = QVariantMap {
+ { u"1"_s, QVariant::fromValue<std::nullptr_t>(nullptr) },
+ { u"19"_s, QVariant::fromValue(u"19"_s) },
+ { u"25"_s, QVariant() }
+ };
- QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate)));
- QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime)));
+ QCOMPARE(o->property("v").toMap(), expected);
+}
- const QDate date = ref->myDate();
- const QTime time = ref->myTime();
+void tst_QmlCppCodegen::voidConversion()
+{
+ QQmlEngine engine;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/voidConversion.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date)));
- QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time)));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + u":8: Error: Cannot assign [undefined] to QPointF"_s));
- QMetaObject::invokeMethod(o.data(), "fool");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time)));
- QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date)));
+ QCOMPARE(o->property("p"), QPointF(20, 10));
}
-static QRegularExpression bindingLoopMessage(const QUrl &url, char var)
+void tst_QmlCppCodegen::voidFunction()
{
- // The actual string depends on how many times QObject* was registered with what parameters.
- return QRegularExpression(
- "%1:4:1: QML [^:]+: Binding loop detected for property \"%2\""_L1
- .arg(url.toString()).arg(QLatin1Char(var)));
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/voidfunction.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QVERIFY(object->objectName().isEmpty());
+ object->metaObject()->invokeMethod(object.data(), "doesNotReturnValue");
+ QCOMPARE(object->objectName(), u"barbar"_s);
}
-void tst_QmlCppCodegen::valueTypeBehavior()
+void tst_QmlCppCodegen::writeBack()
{
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/writeback.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- const QUrl copy(u"qrc:/qt/qml/TestTypes/valueTypeCopy.qml"_s);
+ Person *person = qobject_cast<Person *>(object.data());
+ QVERIFY(person);
+ QCOMPARE(person->area(), QRectF(4, 5, 16, 17));
+ QCOMPARE(person->property("inner").toInt(), 99);
- QQmlComponent c1(&engine, copy);
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(copy, 'e'));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(copy, 'f'));
- QScopedPointer<QObject> o1(c1.create());
- QVERIFY(!o1.isNull());
- QCOMPARE(o1->property("e").toDouble(), 45.0);
- QCOMPARE(o1->property("f").toDouble(), 1.0);
+ Person *shadowable = person->property("shadowable").value<Person *>();
+ QVERIFY(shadowable);
+ QCOMPARE(shadowable->area(), QRectF(40, 50, 16, 17));
- const QUrl reference(u"qrc:/qt/qml/TestTypes/valueTypeReference.qml"_s);
- QQmlComponent c2(&engine, reference);
- QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(reference, 'e'));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(reference, 'f'));
- QScopedPointer<QObject> o2(c2.create());
- QVERIFY(!o2.isNull());
- QVERIFY(qIsNaN(o2->property("e").toDouble()));
- QCOMPARE(o2->property("f").toDouble(), 5.0);
+ QCOMPARE(person->property("ints"), QVariant::fromValue(QList<int>({12, 22, 2, 1, 0, 0, 33})));
}
-void tst_QmlCppCodegen::invisibleSingleton()
+void tst_QmlCppCodegen::writeVariantMap()
{
QQmlEngine engine;
- const QUrl copy(u"qrc:/qt/qml/TestTypes/hidden/Main.qml"_s);
- QQmlComponent c(&engine, copy);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/StringBuilderTestTypes/writeVariantMap.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ const QVariantMap v = object->property("data").toMap();
+ QCOMPARE(v.size(), 1);
+ const QVariant textPlain = v[u"text/plain"_s];
+ QCOMPARE(textPlain.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(textPlain.toString(), u"%Drag Me%"_s);
- QTest::ignoreMessage(
- QtWarningMsg,
- "qrc:/qt/qml/TestTypes/hidden/Main.qml:4:5: "
- "Unable to assign [undefined] to QColor");
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->property("c"), QVariant(QMetaType::fromName("QColor")));
}
QTEST_MAIN(tst_QmlCppCodegen)