/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "testtypes.h" #include "testhttpserver.h" #include "../../shared/util.h" #include #if defined(Q_OS_MAC) #include #endif DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) static inline bool isCaseSensitiveFileSystem(const QString &path) { Q_UNUSED(path); #if defined(Q_OS_MAC) return pathconf(path.toLatin1().constData(), _PC_CASE_SENSITIVE); #elif defined(Q_OS_WIN) return false; #else return true; #endif } /* This test case covers QML language issues. This covers everything that does not involve evaluating ECMAScript expressions and bindings. Evaluation of expressions and bindings is covered in qmlecmascript */ class tst_qqmllanguage : public QQmlDataTest { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void errors_data(); void errors(); void insertedSemicolon_data(); void insertedSemicolon(); void simpleObject(); void simpleContainer(); void interfaceProperty(); void interfaceQList(); void assignObjectToSignal(); void assignObjectToVariant(); void assignLiteralSignalProperty(); void assignQmlComponent(); void assignBasicTypes(); void assignTypeExtremes(); void assignCompositeToType(); void assignLiteralToVar(); void assignLiteralToJSValue(); void assignNullStrings(); void bindJSValueToVar(); void bindJSValueToVariant(); void bindJSValueToType(); void bindTypeToJSValue(); void customParserTypes(); void customParserTypeInInlineComponent(); void rootAsQmlComponent(); void rootItemIsComponent(); void inlineQmlComponents(); void idProperty(); void autoNotifyConnection(); void assignSignal(); void assignSignalFunctionExpression(); void overrideSignal_data(); void overrideSignal(); void dynamicProperties(); void dynamicPropertiesNested(); void listProperties(); void listPropertiesInheritanceNoCrash(); void badListItemType(); void dynamicObjectProperties(); void dynamicSignalsAndSlots(); void simpleBindings(); void noDoubleEvaluationForFlushedBindings_data(); void noDoubleEvaluationForFlushedBindings(); void autoComponentCreation(); void autoComponentCreationInGroupProperty(); void propertyValueSource(); void requiredProperty(); void requiredPropertyFromCpp_data(); void requiredPropertyFromCpp(); void attachedProperties(); void dynamicObjects(); void customVariantTypes(); void valueTypes(); void cppnamespace(); void aliasProperties(); void aliasPropertiesAndSignals(); void aliasPropertyChangeSignals(); void qtbug_89822(); void componentCompositeType(); void i18n(); void i18n_data(); void onCompleted(); void onDestruction(); void scriptString(); void scriptStringJs(); void scriptStringWithoutSourceCode(); void scriptStringComparison(); void defaultPropertyListOrder(); void declaredPropertyValues(); void dontDoubleCallClassBegin(); void reservedWords_data(); void reservedWords(); void inlineAssignmentsOverrideBindings(); void nestedComponentRoots(); void registrationOrder(); void readonly(); void readonlyObjectProperties(); void receivers(); void registeredCompositeType(); void registeredCompositeTypeWithEnum(); void registeredCompositeTypeWithAttachedProperty(); void implicitImportsLast(); void basicRemote_data(); void basicRemote(); void importsBuiltin_data(); void importsBuiltin(); void importsLocal_data(); void importsLocal(); void importsRemote_data(); void importsRemote(); void importsInstalled_data(); void importsInstalled(); void importsInstalledRemote_data(); void importsInstalledRemote(); void importsPath_data(); void importsPath(); void importsOrder_data(); void importsOrder(); void importIncorrectCase(); void importJs_data(); void importJs(); void importJsModule_data(); void importJsModule(); void explicitSelfImport(); void importInternalType(); void qmlAttachedPropertiesObjectMethod(); void customOnProperty(); void variantNotify(); void revisions(); void revisionOverloads(); void subclassedUncreateableRevision_data(); void subclassedUncreateableRevision(); void subclassedExtendedUncreateableRevision_data(); void subclassedExtendedUncreateableRevision(); void uncreatableTypesAsProperties(); void propertyInit(); void remoteLoadCrash(); void signalWithDefaultArg(); void signalParameterTypes(); void functionParameterTypes(); // regression tests for crashes void crash1(); void crash2(); void globalEnums(); void lowercaseEnumRuntime_data(); void lowercaseEnumRuntime(); void lowercaseEnumCompileTime_data(); void lowercaseEnumCompileTime(); void scopedEnum(); void scopedEnumsWithNameClash(); void scopedEnumsWithResolvedNameClash(); void enumNoScopeLeak(); void qmlEnums(); void literals_data(); void literals(); void objectDeletionNotify_data(); void objectDeletionNotify(); void scopedProperties(); void deepProperty(); void compositeSingletonProperties(); void compositeSingletonSameEngine(); void compositeSingletonDifferentEngine(); void compositeSingletonNonTypeError(); void compositeSingletonQualifiedNamespace(); void compositeSingletonModule(); void compositeSingletonModuleVersioned(); void compositeSingletonModuleQualified(); void compositeSingletonInstantiateError(); void compositeSingletonDynamicPropertyError(); void compositeSingletonDynamicSignalAndJavaScriptPragma(); void compositeSingletonQmlRegisterTypeError(); void compositeSingletonQmldirNoPragmaError(); void compositeSingletonQmlDirError(); void compositeSingletonRemote(); void compositeSingletonSelectors(); void compositeSingletonRegistered(); void compositeSingletonCircular(); void singletonsHaveContextAndEngine(); void customParserBindingScopes(); void customParserEvaluateEnum(); void customParserProperties(); void customParserWithExtendedObject(); void nestedCustomParsers(); void preservePropertyCacheOnGroupObjects(); void propertyCacheInSync(); void rootObjectInCreationNotForSubObjects(); void lazyDeferredSubObject(); void deferredProperties(); void executeDeferredPropertiesOnce(); void noChildEvents(); void earlyIdObjectAccess(); void deleteSingletons(); void arrayBuffer_data(); void arrayBuffer(); void defaultListProperty(); void namespacedPropertyTypes(); void qmlTypeCanBeResolvedByName_data(); void qmlTypeCanBeResolvedByName(); void instanceof_data(); void instanceof(); void concurrentLoadQmlDir(); void accessDeletedObject(); void lowercaseTypeNames(); void thisInQmlScope(); void valueTypeGroupPropertiesInBehavior(); void retrieveQmlTypeId(); void polymorphicFunctionLookup(); void anchorsToParentInPropertyChanges(); void typeWrapperToVariant(); void extendedForeignTypes(); void foreignTypeSingletons(); void inlineComponent(); void inlineComponent_data(); void inlineComponentReferenceCycle_data(); void inlineComponentReferenceCycle(); void nestedInlineComponentNotAllowed(); void inlineComponentStaticTypeResolution(); void inlineComponentInSingleton(); void nonExistingInlineComponent_data(); void nonExistingInlineComponent(); void inlineComponentFoundBeforeOtherImports(); void inlineComponentDuplicateNameError(); void selfReference(); void selfReferencingSingleton(); void listContainingDeletedObject(); void overrideSingleton(); void revisionedPropertyOfAttachedObjectProperty(); void arrayToContainer(); void qualifiedScopeInCustomParser(); void accessNullPointerPropertyCache(); void checkUncreatableNoReason(); void checkURLtoURLObject(); void registerValueTypes(); void extendedNamespace(); void factorySingleton(); void extendedSingleton(); void qtbug_85932(); void qtbug_86482(); void multiExtension(); void invalidInlineComponent(); private: QQmlEngine engine; QStringList defaultImportPathList; void testType(const QString& qml, const QString& type, const QString& error, bool partialMatch = false); // When calling into JavaScript, the specific type of the return value can differ if that return // value is a number. This is not only the case for non-integral numbers, or numbers that do not // fit into the (signed) integer range, but it also depends on which optimizations are run. So, // to check if the return value is of a number type, use this method instead of checking against // a specific userType. static bool isJSNumberType(int userType) { return userType == QMetaType::Int || userType == QMetaType::UInt || userType == QMetaType::Double; } void getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */); void getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */); }; #define DETERMINE_ERRORS(errorfile,expected,actual)\ QList expected; \ QList actual; \ do { \ QFile file(testFile(errorfile)); \ QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); \ QByteArray data = file.readAll(); \ file.close(); \ expected = data.split('\n'); \ expected.removeAll(QByteArray("")); \ QList errors = component.errors(); \ for (int ii = 0; ii < errors.count(); ++ii) { \ const QQmlError &error = errors.at(ii); \ QByteArray errorStr = QByteArray::number(error.line()) + ':' + \ QByteArray::number(error.column()) + ':' + \ error.description().toUtf8(); \ actual << errorStr; \ } \ } while (false); #define VERIFY_ERRORS(errorfile) \ if (!errorfile) { \ if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \ qWarning() << "Unexpected Errors:" << component.errors(); \ QVERIFY2(!component.isError(), qPrintable(component.errorString())); \ QVERIFY(component.errors().isEmpty()); \ } else { \ DETERMINE_ERRORS(errorfile,expected,actual);\ if (qgetenv("DEBUG") != "" && expected != actual) \ qWarning() << "Expected:" << expected << "Actual:" << actual; \ if (qgetenv("QDECLARATIVELANGUAGE_UPDATEERRORS") != "" && expected != actual) {\ QFile file(testFile(errorfile)); \ QVERIFY(file.open(QIODevice::WriteOnly)); \ for (int ii = 0; ii < actual.count(); ++ii) { \ file.write(actual.at(ii)); file.write("\n"); \ } \ file.close(); \ } else { \ QCOMPARE(actual, expected); \ } \ } void tst_qqmllanguage::cleanupTestCase() { if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) QVERIFY(QFile::remove(testFile(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")))); } void tst_qqmllanguage::insertedSemicolon_data() { QTest::addColumn("file"); QTest::addColumn("errorFile"); QTest::addColumn("create"); QTest::newRow("insertedSemicolon.1") << "insertedSemicolon.1.qml" << "insertedSemicolon.1.errors.txt" << false; } void tst_qqmllanguage::insertedSemicolon() { QFETCH(QString, file); QFETCH(QString, errorFile); QFETCH(bool, create); QQmlComponent component(&engine, testFileUrl(file)); QScopedPointer object; if(create) { object.reset(component.create()); QVERIFY(object.isNull()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); } void tst_qqmllanguage::errors_data() { QTest::addColumn("file"); QTest::addColumn("errorFile"); QTest::addColumn("create"); QTest::newRow("nonexistantProperty.1") << "nonexistantProperty.1.qml" << "nonexistantProperty.1.errors.txt" << false; QTest::newRow("nonexistantProperty.2") << "nonexistantProperty.2.qml" << "nonexistantProperty.2.errors.txt" << false; QTest::newRow("nonexistantProperty.3") << "nonexistantProperty.3.qml" << "nonexistantProperty.3.errors.txt" << false; QTest::newRow("nonexistantProperty.4") << "nonexistantProperty.4.qml" << "nonexistantProperty.4.errors.txt" << false; QTest::newRow("nonexistantProperty.5") << "nonexistantProperty.5.qml" << "nonexistantProperty.5.errors.txt" << false; QTest::newRow("nonexistantProperty.6") << "nonexistantProperty.6.qml" << "nonexistantProperty.6.errors.txt" << false; QTest::newRow("nonexistantProperty.7") << "nonexistantProperty.7.qml" << "nonexistantProperty.7.errors.txt" << false; QTest::newRow("nonexistantProperty.8") << "nonexistantProperty.8.qml" << "nonexistantProperty.8.errors.txt" << false; QTest::newRow("wrongType (string for int)") << "wrongType.1.qml" << "wrongType.1.errors.txt" << false; QTest::newRow("wrongType (int for bool)") << "wrongType.2.qml" << "wrongType.2.errors.txt" << false; QTest::newRow("wrongType (bad rect)") << "wrongType.3.qml" << "wrongType.3.errors.txt" << false; QTest::newRow("wrongType (invalid enum)") << "wrongType.4.qml" << "wrongType.4.errors.txt" << false; QTest::newRow("wrongType (int for uint)") << "wrongType.5.qml" << "wrongType.5.errors.txt" << false; QTest::newRow("wrongType (string for real)") << "wrongType.6.qml" << "wrongType.6.errors.txt" << false; QTest::newRow("wrongType (int for color)") << "wrongType.7.qml" << "wrongType.7.errors.txt" << false; QTest::newRow("wrongType (int for date)") << "wrongType.8.qml" << "wrongType.8.errors.txt" << false; QTest::newRow("wrongType (int for time)") << "wrongType.9.qml" << "wrongType.9.errors.txt" << false; QTest::newRow("wrongType (int for datetime)") << "wrongType.10.qml" << "wrongType.10.errors.txt" << false; QTest::newRow("wrongType (string for point)") << "wrongType.11.qml" << "wrongType.11.errors.txt" << false; QTest::newRow("wrongType (color for size)") << "wrongType.12.qml" << "wrongType.12.errors.txt" << false; QTest::newRow("wrongType (number string for int)") << "wrongType.13.qml" << "wrongType.13.errors.txt" << false; QTest::newRow("wrongType (int for string)") << "wrongType.14.qml" << "wrongType.14.errors.txt" << false; QTest::newRow("wrongType (int for url)") << "wrongType.15.qml" << "wrongType.15.errors.txt" << false; QTest::newRow("wrongType (invalid object)") << "wrongType.16.qml" << "wrongType.16.errors.txt" << false; QTest::newRow("wrongType (int for enum)") << "wrongType.17.qml" << "wrongType.17.errors.txt" << false; QTest::newRow("readOnly.1") << "readOnly.1.qml" << "readOnly.1.errors.txt" << false; QTest::newRow("readOnly.2") << "readOnly.2.qml" << "readOnly.2.errors.txt" << false; QTest::newRow("readOnly.3") << "readOnly.3.qml" << "readOnly.3.errors.txt" << false; QTest::newRow("readOnly.4") << "readOnly.4.qml" << "readOnly.4.errors.txt" << false; QTest::newRow("readOnly.5") << "readOnly.5.qml" << "readOnly.5.errors.txt" << false; QTest::newRow("listAssignment.1") << "listAssignment.1.qml" << "listAssignment.1.errors.txt" << false; QTest::newRow("listAssignment.2") << "listAssignment.2.qml" << "listAssignment.2.errors.txt" << false; QTest::newRow("listAssignment.3") << "listAssignment.3.qml" << "listAssignment.3.errors.txt" << false; QTest::newRow("invalidID.1") << "invalidID.qml" << "invalidID.errors.txt" << false; QTest::newRow("invalidID.2") << "invalidID.2.qml" << "invalidID.2.errors.txt" << false; QTest::newRow("invalidID.3") << "invalidID.3.qml" << "invalidID.3.errors.txt" << false; QTest::newRow("invalidID.4") << "invalidID.4.qml" << "invalidID.4.errors.txt" << false; QTest::newRow("invalidID.5") << "invalidID.5.qml" << "invalidID.5.errors.txt" << false; QTest::newRow("invalidID.6") << "invalidID.6.qml" << "invalidID.6.errors.txt" << false; QTest::newRow("invalidID.7") << "invalidID.7.qml" << "invalidID.7.errors.txt" << false; QTest::newRow("invalidID.8") << "invalidID.8.qml" << "invalidID.8.errors.txt" << false; QTest::newRow("invalidID.9") << "invalidID.9.qml" << "invalidID.9.errors.txt" << false; QTest::newRow("invalidID.10") << "invalidID.10.qml" << "invalidID.10.errors.txt" << false; QTest::newRow("scriptString.1") << "scriptString.1.qml" << "scriptString.1.errors.txt" << false; QTest::newRow("scriptString.2") << "scriptString.2.qml" << "scriptString.2.errors.txt" << false; QTest::newRow("unsupportedProperty") << "unsupportedProperty.qml" << "unsupportedProperty.errors.txt" << false; QTest::newRow("nullDotProperty") << "nullDotProperty.qml" << "nullDotProperty.errors.txt" << true; QTest::newRow("fakeDotProperty") << "fakeDotProperty.qml" << "fakeDotProperty.errors.txt" << false; QTest::newRow("duplicateIDs") << "duplicateIDs.qml" << "duplicateIDs.errors.txt" << false; QTest::newRow("unregisteredObject") << "unregisteredObject.qml" << "unregisteredObject.errors.txt" << false; QTest::newRow("empty") << "empty.qml" << "empty.errors.txt" << false; QTest::newRow("missingObject") << "missingObject.qml" << "missingObject.errors.txt" << false; QTest::newRow("failingComponent") << "failingComponentTest.qml" << "failingComponent.errors.txt" << false; QTest::newRow("missingSignal") << "missingSignal.qml" << "missingSignal.errors.txt" << false; QTest::newRow("missingSignal2") << "missingSignal.2.qml" << "missingSignal.2.errors.txt" << false; QTest::newRow("finalOverride") << "finalOverride.qml" << "finalOverride.errors.txt" << false; QTest::newRow("customParserIdNotAllowed") << "customParserIdNotAllowed.qml" << "customParserIdNotAllowed.errors.txt" << false; QTest::newRow("nullishCoalescing_LHS_Or") << "nullishCoalescing_LHS_Or.qml" << "nullishCoalescing_LHS_Or.errors.txt" << false; QTest::newRow("nullishCoalescing_LHS_And") << "nullishCoalescing_LHS_And.qml" << "nullishCoalescing_LHS_And.errors.txt" << false; QTest::newRow("nullishCoalescing_RHS_Or") << "nullishCoalescing_RHS_Or.qml" << "nullishCoalescing_RHS_Or.errors.txt" << false; QTest::newRow("nullishCoalescing_RHS_And") << "nullishCoalescing_RHS_And.qml" << "nullishCoalescing_RHS_And.errors.txt" << false; QTest::newRow("invalidGroupedProperty.1") << "invalidGroupedProperty.1.qml" << "invalidGroupedProperty.1.errors.txt" << false; QTest::newRow("invalidGroupedProperty.2") << "invalidGroupedProperty.2.qml" << "invalidGroupedProperty.2.errors.txt" << false; QTest::newRow("invalidGroupedProperty.3") << "invalidGroupedProperty.3.qml" << "invalidGroupedProperty.3.errors.txt" << false; QTest::newRow("invalidGroupedProperty.4") << "invalidGroupedProperty.4.qml" << "invalidGroupedProperty.4.errors.txt" << false; QTest::newRow("invalidGroupedProperty.5") << "invalidGroupedProperty.5.qml" << "invalidGroupedProperty.5.errors.txt" << false; QTest::newRow("invalidGroupedProperty.6") << "invalidGroupedProperty.6.qml" << "invalidGroupedProperty.6.errors.txt" << false; QTest::newRow("invalidGroupedProperty.7") << "invalidGroupedProperty.7.qml" << "invalidGroupedProperty.7.errors.txt" << true; QTest::newRow("invalidGroupedProperty.8") << "invalidGroupedProperty.8.qml" << "invalidGroupedProperty.8.errors.txt" << false; QTest::newRow("invalidGroupedProperty.9") << "invalidGroupedProperty.9.qml" << "invalidGroupedProperty.9.errors.txt" << false; QTest::newRow("invalidGroupedProperty.10") << "invalidGroupedProperty.10.qml" << "invalidGroupedProperty.10.errors.txt" << false; QTest::newRow("importNamespaceConflict") << "importNamespaceConflict.qml" << "importNamespaceConflict.errors.txt" << false; QTest::newRow("importVersionMissing (builtin)") << "importVersionMissingBuiltIn.qml" << "importVersionMissingBuiltIn.errors.txt" << false; QTest::newRow("importVersionMissing (installed)") << "importVersionMissingInstalled.qml" << "importVersionMissingInstalled.errors.txt" << false; QTest::newRow("importNonExist (installed)") << "importNonExist.qml" << "importNonExist.errors.txt" << false; QTest::newRow("importNonExistOlder (installed)") << "importNonExistOlder.qml" << "importNonExistOlder.errors.txt" << false; QTest::newRow("importNewerVersion (installed)") << "importNewerVersion.qml" << "importNewerVersion.errors.txt" << false; QTest::newRow("invalidImportID") << "invalidImportID.qml" << "invalidImportID.errors.txt" << false; QTest::newRow("importFile") << "importFile.qml" << "importFile.errors.txt" << false; QTest::newRow("signal.1") << "signal.1.qml" << "signal.1.errors.txt" << false; QTest::newRow("signal.2") << "signal.2.qml" << "signal.2.errors.txt" << false; QTest::newRow("signal.3") << "signal.3.qml" << "signal.3.errors.txt" << false; QTest::newRow("signal.4") << "signal.4.qml" << "signal.4.errors.txt" << false; QTest::newRow("signal.5") << "signal.5.qml" << "signal.5.errors.txt" << false; QTest::newRow("signal.6") << "signal.6.qml" << "signal.6.errors.txt" << false; QTest::newRow("method.1") << "method.1.qml" << "method.1.errors.txt" << false; QTest::newRow("property.1") << "property.1.qml" << "property.1.errors.txt" << false; QTest::newRow("property.2") << "property.2.qml" << "property.2.errors.txt" << false; QTest::newRow("property.3") << "property.3.qml" << "property.3.errors.txt" << false; QTest::newRow("property.4") << "property.4.qml" << "property.4.errors.txt" << false; QTest::newRow("property.6") << "property.6.qml" << "property.6.errors.txt" << false; QTest::newRow("property.7") << "property.7.qml" << "property.7.errors.txt" << false; QTest::newRow("importScript.1") << "importscript.1.qml" << "importscript.1.errors.txt" << false; QTest::newRow("Component.1") << "component.1.qml" << "component.1.errors.txt" << false; QTest::newRow("Component.2") << "component.2.qml" << "component.2.errors.txt" << false; QTest::newRow("Component.3") << "component.3.qml" << "component.3.errors.txt" << false; QTest::newRow("Component.4") << "component.4.qml" << "component.4.errors.txt" << false; QTest::newRow("Component.5") << "component.5.qml" << "component.5.errors.txt" << false; QTest::newRow("Component.6") << "component.6.qml" << "component.6.errors.txt" << false; QTest::newRow("Component.7") << "component.7.qml" << "component.7.errors.txt" << false; QTest::newRow("Component.8") << "component.8.qml" << "component.8.errors.txt" << false; QTest::newRow("Component.9") << "component.9.qml" << "component.9.errors.txt" << false; QTest::newRow("MultiSet.1") << "multiSet.1.qml" << "multiSet.1.errors.txt" << false; QTest::newRow("MultiSet.2") << "multiSet.2.qml" << "multiSet.2.errors.txt" << false; QTest::newRow("MultiSet.3") << "multiSet.3.qml" << "multiSet.3.errors.txt" << false; QTest::newRow("MultiSet.4") << "multiSet.4.qml" << "multiSet.4.errors.txt" << false; QTest::newRow("MultiSet.5") << "multiSet.5.qml" << "multiSet.5.errors.txt" << false; QTest::newRow("MultiSet.6") << "multiSet.6.qml" << "multiSet.6.errors.txt" << false; QTest::newRow("MultiSet.7") << "multiSet.7.qml" << "multiSet.7.errors.txt" << false; QTest::newRow("MultiSet.8") << "multiSet.8.qml" << "multiSet.8.errors.txt" << false; QTest::newRow("MultiSet.9") << "multiSet.9.qml" << "multiSet.9.errors.txt" << false; QTest::newRow("MultiSet.10") << "multiSet.10.qml" << "multiSet.10.errors.txt" << false; QTest::newRow("MultiSet.11") << "multiSet.11.qml" << "multiSet.11.errors.txt" << false; QTest::newRow("dynamicMeta.1") << "dynamicMeta.1.qml" << "dynamicMeta.1.errors.txt" << false; QTest::newRow("dynamicMeta.2") << "dynamicMeta.2.qml" << "dynamicMeta.2.errors.txt" << false; QTest::newRow("dynamicMeta.3") << "dynamicMeta.3.qml" << "dynamicMeta.3.errors.txt" << false; QTest::newRow("dynamicMeta.4") << "dynamicMeta.4.qml" << "dynamicMeta.4.errors.txt" << false; QTest::newRow("dynamicMeta.5") << "dynamicMeta.5.qml" << "dynamicMeta.5.errors.txt" << false; QTest::newRow("invalidAlias.1") << "invalidAlias.1.qml" << "invalidAlias.1.errors.txt" << false; QTest::newRow("invalidAlias.2") << "invalidAlias.2.qml" << "invalidAlias.2.errors.txt" << false; QTest::newRow("invalidAlias.3") << "invalidAlias.3.qml" << "invalidAlias.3.errors.txt" << false; QTest::newRow("invalidAlias.4") << "invalidAlias.4.qml" << "invalidAlias.4.errors.txt" << false; QTest::newRow("invalidAlias.5") << "invalidAlias.5.qml" << "invalidAlias.5.errors.txt" << false; QTest::newRow("invalidAlias.6") << "invalidAlias.6.qml" << "invalidAlias.6.errors.txt" << false; QTest::newRow("invalidAlias.7") << "invalidAlias.7.qml" << "invalidAlias.7.errors.txt" << false; QTest::newRow("invalidAlias.8") << "invalidAlias.8.qml" << "invalidAlias.8.errors.txt" << false; QTest::newRow("invalidAlias.9") << "invalidAlias.9.qml" << "invalidAlias.9.errors.txt" << false; QTest::newRow("invalidAlias.10") << "invalidAlias.10.qml" << "invalidAlias.10.errors.txt" << false; QTest::newRow("invalidAlias.11") << "invalidAlias.11.qml" << "invalidAlias.11.errors.txt" << false; QTest::newRow("invalidAlias.12") << "invalidAlias.12.qml" << "invalidAlias.12.errors.txt" << false; QTest::newRow("invalidAlias.13") << "invalidAlias.13.qml" << "invalidAlias.13.errors.txt" << false; QTest::newRow("invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml" << "invalidAttachedProperty.1.errors.txt" << false; QTest::newRow("invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml" << "invalidAttachedProperty.2.errors.txt" << false; QTest::newRow("invalidAttachedProperty.3") << "invalidAttachedProperty.3.qml" << "invalidAttachedProperty.3.errors.txt" << false; QTest::newRow("invalidAttachedProperty.4") << "invalidAttachedProperty.4.qml" << "invalidAttachedProperty.4.errors.txt" << false; QTest::newRow("invalidAttachedProperty.5") << "invalidAttachedProperty.5.qml" << "invalidAttachedProperty.5.errors.txt" << false; QTest::newRow("invalidAttachedProperty.6") << "invalidAttachedProperty.6.qml" << "invalidAttachedProperty.6.errors.txt" << false; QTest::newRow("invalidAttachedProperty.7") << "invalidAttachedProperty.7.qml" << "invalidAttachedProperty.7.errors.txt" << false; QTest::newRow("invalidAttachedProperty.8") << "invalidAttachedProperty.8.qml" << "invalidAttachedProperty.8.errors.txt" << false; QTest::newRow("invalidAttachedProperty.9") << "invalidAttachedProperty.9.qml" << "invalidAttachedProperty.9.errors.txt" << false; QTest::newRow("invalidAttachedProperty.10") << "invalidAttachedProperty.10.qml" << "invalidAttachedProperty.10.errors.txt" << false; QTest::newRow("invalidAttachedProperty.11") << "invalidAttachedProperty.11.qml" << "invalidAttachedProperty.11.errors.txt" << false; QTest::newRow("invalidAttachedProperty.12") << "invalidAttachedProperty.12.qml" << "invalidAttachedProperty.12.errors.txt" << false; QTest::newRow("invalidAttachedProperty.13") << "invalidAttachedProperty.13.qml" << "invalidAttachedProperty.13.errors.txt" << false; QTest::newRow("assignValueToSignal") << "assignValueToSignal.qml" << "assignValueToSignal.errors.txt" << false; QTest::newRow("emptySignal") << "emptySignal.qml" << "emptySignal.errors.txt" << false; QTest::newRow("nestedErrors") << "nestedErrors.qml" << "nestedErrors.errors.txt" << false; QTest::newRow("defaultGrouped") << "defaultGrouped.qml" << "defaultGrouped.errors.txt" << false; QTest::newRow("doubleSignal") << "doubleSignal.qml" << "doubleSignal.errors.txt" << false; QTest::newRow("missingValueTypeProperty") << "missingValueTypeProperty.qml" << "missingValueTypeProperty.errors.txt" << false; QTest::newRow("objectValueTypeProperty") << "objectValueTypeProperty.qml" << "objectValueTypeProperty.errors.txt" << false; QTest::newRow("enumTypes") << "enumTypes.qml" << "enumTypes.errors.txt" << false; QTest::newRow("noCreation") << "noCreation.qml" << "noCreation.errors.txt" << false; QTest::newRow("destroyedSignal") << "destroyedSignal.qml" << "destroyedSignal.errors.txt" << false; QTest::newRow("assignToNamespace") << "assignToNamespace.qml" << "assignToNamespace.errors.txt" << false; QTest::newRow("invalidOn") << "invalidOn.qml" << "invalidOn.errors.txt" << false; QTest::newRow("invalidProperty") << "invalidProperty.qml" << "invalidProperty.errors.txt" << false; QTest::newRow("nonScriptableProperty") << "nonScriptableProperty.qml" << "nonScriptableProperty.errors.txt" << false; QTest::newRow("notAvailable") << "notAvailable.qml" << "notAvailable.errors.txt" << false; QTest::newRow("singularProperty") << "singularProperty.qml" << "singularProperty.errors.txt" << false; QTest::newRow("singularProperty.2") << "singularProperty.2.qml" << "singularProperty.2.errors.txt" << false; QTest::newRow("scopedEnumList") << "scopedEnumList.qml" << "scopedEnumList.errors.txt" << false; QTest::newRow("lowercase enum value") << "lowercaseQmlEnum.1.qml" << "lowercaseQmlEnum.1.errors.txt" << false; QTest::newRow("lowercase enum type") << "lowercaseQmlEnum.2.qml" << "lowercaseQmlEnum.2.errors.txt" << false; QTest::newRow("string enum value") << "invalidQmlEnumValue.1.qml" << "invalidQmlEnumValue.1.errors.txt" << false; QTest::newRow("identifier enum type") << "invalidQmlEnumValue.2.qml" << "invalidQmlEnumValue.2.errors.txt" << false; QTest::newRow("enum value too large") << "invalidQmlEnumValue.3.qml" << "invalidQmlEnumValue.3.errors.txt" << false; QTest::newRow("non-integer enum value") << "invalidQmlEnumValue.4.qml" << "invalidQmlEnumValue.4.errors.txt" << false; const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ? QStringLiteral("incorrectCase.errors.sensitive.txt") : QStringLiteral("incorrectCase.errors.insensitive.txt"); QTest::newRow("incorrectCase") << "incorrectCase.qml" << expectedError << false; QTest::newRow("metaobjectRevision.1") << "metaobjectRevision.1.qml" << "metaobjectRevision.1.errors.txt" << false; QTest::newRow("metaobjectRevision.2") << "metaobjectRevision.2.qml" << "metaobjectRevision.2.errors.txt" << false; QTest::newRow("metaobjectRevision.3") << "metaobjectRevision.3.qml" << "metaobjectRevision.3.errors.txt" << false; QTest::newRow("invalidRoot.1") << "invalidRoot.1.qml" << "invalidRoot.1.errors.txt" << false; QTest::newRow("invalidRoot.2") << "invalidRoot.2.qml" << "invalidRoot.2.errors.txt" << false; QTest::newRow("invalidRoot.3") << "invalidRoot.3.qml" << "invalidRoot.3.errors.txt" << false; QTest::newRow("invalidRoot.4") << "invalidRoot.4.qml" << "invalidRoot.4.errors.txt" << false; QTest::newRow("invalidTypeName.1") << "invalidTypeName.1.qml" << "invalidTypeName.1.errors.txt" << false; QTest::newRow("invalidTypeName.2") << "invalidTypeName.2.qml" << "invalidTypeName.2.errors.txt" << false; QTest::newRow("invalidTypeName.3") << "invalidTypeName.3.qml" << "invalidTypeName.3.errors.txt" << false; QTest::newRow("invalidTypeName.4") << "invalidTypeName.4.qml" << "invalidTypeName.4.errors.txt" << false; QTest::newRow("Major version isolation") << "majorVersionIsolation.qml" << "majorVersionIsolation.errors.txt" << false; QTest::newRow("badCompositeRegistration.1") << "badCompositeRegistration.1.qml" << "badCompositeRegistration.1.errors.txt" << false; QTest::newRow("badCompositeRegistration.2") << "badCompositeRegistration.2.qml" << "badCompositeRegistration.2.errors.txt" << false; QTest::newRow("assignComponentToWrongType") << "assignComponentToWrongType.qml" << "assignComponentToWrongType.errors.txt" << false; QTest::newRow("cyclicAlias") << "cyclicAlias.qml" << "cyclicAlias.errors.txt" << false; QTest::newRow("fuzzed.1") << "fuzzed.1.qml" << "fuzzed.1.errors.txt" << false; QTest::newRow("fuzzed.2") << "fuzzed.2.qml" << "fuzzed.2.errors.txt" << false; QTest::newRow("fuzzed.3") << "fuzzed.3.qml" << "fuzzed.3.errors.txt" << false; QTest::newRow("bareQmlImport") << "bareQmlImport.qml" << "bareQmlImport.errors.txt" << false; QTest::newRow("typeAnnotations.2") << "typeAnnotations.2.qml" << "typeAnnotations.2.errors.txt" << false; QTest::newRow("propertyUnknownType") << "propertyUnknownType.qml" << "propertyUnknownType.errors.txt" << false; QTest::newRow("selfInstantiation") << "SelfInstantiation.qml" << "SelfInstantiation.errors.txt" << false; } void tst_qqmllanguage::errors() { #ifdef Q_OS_ANDROID if (qstrcmp(QTest::currentDataTag(), "fuzzed.2") == 0) { QSKIP("Gives different errors on Android"); /* Only gives one error on Android: qrc:/data/fuzzed.2.qml:1:1: " import" ^ So, it seems to complain about the first import (which is understandable) */ } #endif QFETCH(QString, file); QFETCH(QString, errorFile); QFETCH(bool, create); QQmlComponent component(&engine, testFileUrl(file)); QTRY_VERIFY(!component.isLoading()); QScopedPointer object; if (create) { object.reset(component.create()); QVERIFY(object.isNull()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); } void tst_qqmllanguage::simpleObject() { QQmlComponent component(&engine, testFileUrl("simpleObject.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); } void tst_qqmllanguage::simpleContainer() { QQmlComponent component(&engine, testFileUrl("simpleContainer.qml")); VERIFY_ERRORS(0); QScopedPointer container(qobject_cast(component.create())); QVERIFY(container != nullptr); QCOMPARE(container->getChildren()->count(),2); } void tst_qqmllanguage::interfaceProperty() { QQmlComponent component(&engine, testFileUrl("interfaceProperty.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->interface()); QCOMPARE(object->interface()->id, 913); } void tst_qqmllanguage::interfaceQList() { QQmlComponent component(&engine, testFileUrl("interfaceQList.qml")); VERIFY_ERRORS(0); QScopedPointer container(qobject_cast(component.create())); QVERIFY(container != nullptr); QCOMPARE(container->getQListInterfaces()->count(), 2); for(int ii = 0; ii < 2; ++ii) QCOMPARE(container->getQListInterfaces()->at(ii)->id, 913); } void tst_qqmllanguage::assignObjectToSignal() { QQmlComponent component(&engine, testFileUrl("assignObjectToSignal.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); } void tst_qqmllanguage::assignObjectToVariant() { QQmlComponent component(&engine, testFileUrl("assignObjectToVariant.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVariant v = object->property("a"); QVERIFY(v.typeId() == qMetaTypeId()); } void tst_qqmllanguage::assignLiteralSignalProperty() { QQmlComponent component(&engine, testFileUrl("assignLiteralSignalProperty.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->onLiteralSignal(), 10); } // Test is an external component can be loaded and assigned (to a qlist) void tst_qqmllanguage::assignQmlComponent() { QQmlComponent component(&engine, testFileUrl("assignQmlComponent.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 1); QObject *child = object->getChildren()->at(0); QCOMPARE(child->property("x"), QVariant(10)); QCOMPARE(child->property("y"), QVariant(11)); } // Test literal assignment to all the basic types void tst_qqmllanguage::assignBasicTypes() { QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); QCOMPARE(object->qtEnumProperty(), Qt::RichText); QCOMPARE(object->mirroredEnumProperty(), MyTypeObject::MirroredEnumVal3); QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue); QCOMPARE(object->stringProperty(), QString("Hello World!")); QCOMPARE(object->uintProperty(), uint(10)); QCOMPARE(object->intProperty(), -19); QCOMPARE((float)object->realProperty(), float(23.2)); QCOMPARE((float)object->doubleProperty(), float(-19.7)); QCOMPARE((float)object->floatProperty(), float(8.5)); QCOMPARE(object->colorProperty(), QColor("red")); QCOMPARE(object->dateProperty(), QDate(1982, 11, 25)); QCOMPARE(object->timeProperty(), QTime(11, 11, 32)); QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1))); QCOMPARE(object->pointProperty(), QPoint(99,13)); QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3)); QCOMPARE(object->sizeProperty(), QSize(99, 13)); QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2)); QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200)); QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99)); QCOMPARE(object->boolProperty(), true); QCOMPARE(object->variantProperty(), QVariant("Hello World!")); QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2f)); QCOMPARE(object->vector2Property(), QVector2D(2, 3)); QCOMPARE(object->vector4Property(), QVector4D(10, 1, 2.2f, 2.3f)); const QUrl encoded = QUrl::fromEncoded("main.qml?with%3cencoded%3edata", QUrl::TolerantMode); QCOMPARE(object->urlProperty(), encoded); QVERIFY(object->objectProperty() != nullptr); MyTypeObject *child = qobject_cast(object->objectProperty()); QVERIFY(child != nullptr); QCOMPARE(child->intProperty(), 8); //these used to go via script. Ensure they no longer do QCOMPARE(object->property("qtEnumTriggeredChange").toBool(), false); QCOMPARE(object->property("mirroredEnumTriggeredChange").toBool(), false); } // Test edge case type assignments void tst_qqmllanguage::assignTypeExtremes() { QQmlComponent component(&engine, testFileUrl("assignTypeExtremes.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->uintProperty(), 0xEE6B2800); QCOMPARE(object->intProperty(), -0x77359400); } // Test that a composite type can assign to a property of its base type void tst_qqmllanguage::assignCompositeToType() { QQmlComponent component(&engine, testFileUrl("assignCompositeToType.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); } // Test that literals are stored correctly in "var" properties // Note that behaviour differs from "variant" properties in that // no conversion from "special strings" to QVariants is performed. void tst_qqmllanguage::assignLiteralToVar() { QQmlComponent component(&engine, testFileUrl("assignLiteralToVar.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(isJSNumberType(object->property("test1").typeId())); QCOMPARE(object->property("test2").typeId(), (int)QMetaType::Double); QCOMPARE(object->property("test3").typeId(), QMetaType::QString); QCOMPARE(object->property("test4").typeId(), QMetaType::QString); QCOMPARE(object->property("test5").typeId(), QMetaType::QString); QCOMPARE(object->property("test6").typeId(), QMetaType::QString); QCOMPARE(object->property("test7").typeId(), QMetaType::QString); QCOMPARE(object->property("test8").typeId(), QMetaType::QString); QCOMPARE(object->property("test9").typeId(), QMetaType::QString); QCOMPARE(object->property("test10").typeId(), QMetaType::Bool); QCOMPARE(object->property("test11").typeId(), QMetaType::Bool); QCOMPARE(object->property("test12").typeId(), QMetaType::QColor); QCOMPARE(object->property("test13").typeId(), QMetaType::QRectF); QCOMPARE(object->property("test14").typeId(), QMetaType::QPointF); QCOMPARE(object->property("test15").typeId(), QMetaType::QSizeF); QCOMPARE(object->property("test16").typeId(), QMetaType::QVector3D); QVERIFY(isJSNumberType(object->property("variantTest1Bound").typeId())); QVERIFY(isJSNumberType(object->property("test1Bound").typeId())); QCOMPARE(object->property("test1"), QVariant(5)); QCOMPARE(object->property("test2"), QVariant((double)1.7)); QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String("Hello world!")))); QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String("#FF008800")))); QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String("10,10,10x10")))); QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String("10,10")))); QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String("10x10")))); QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String("100,100,100")))); QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String("#FF008800")))); QCOMPARE(object->property("test10"), QVariant(bool(true))); QCOMPARE(object->property("test11"), QVariant(bool(false))); QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5))); QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10))); QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10))); QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10))); QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); QCOMPARE(object->property("variantTest1Bound"), QVariant(9)); QCOMPARE(object->property("test1Bound"), QVariant(11)); } void tst_qqmllanguage::assignLiteralToJSValue() { QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(root != nullptr); { MyQmlObject *object = root->findChild("test1"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(5)); } { MyQmlObject *object = root->findChild("test2"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(1.7)); } { MyQmlObject *object = root->findChild("test3"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("Hello world!"))); }{ MyQmlObject *object = root->findChild("test4"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("#FF008800"))); } { MyQmlObject *object = root->findChild("test5"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("10,10,10x10"))); } { MyQmlObject *object = root->findChild("test6"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("10,10"))); } { MyQmlObject *object = root->findChild("test7"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("10x10"))); } { MyQmlObject *object = root->findChild("test8"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("100,100,100"))); } { MyQmlObject *object = root->findChild("test9"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("#FF008800"))); } { MyQmlObject *object = root->findChild("test10"); QJSValue value = object->qjsvalue(); QVERIFY(value.isBool()); QCOMPARE(value.toBool(), true); } { MyQmlObject *object = root->findChild("test11"); QJSValue value = object->qjsvalue(); QVERIFY(value.isBool()); QCOMPARE(value.toBool(), false); } { MyQmlObject *object = root->findChild("test20"); QJSValue value = object->qjsvalue(); QVERIFY(value.isCallable()); QCOMPARE(value.call(QList () << QJSValue(4)).toInt(), 12); } { MyQmlObject *object = root->findChild("test21"); QJSValue value = object->qjsvalue(); QVERIFY(value.isUndefined()); } { MyQmlObject *object = root->findChild("test22"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNull()); } { MyQmlObject *object = root->findChild("test1Bound"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(9)); } { MyQmlObject *object = root->findChild("test20Bound"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(27)); } { MyQmlObject *object = root->findChild("test23"); QJSValue value = object->qjsvalue(); QVERIFY(value.isQObject()); QCOMPARE(value.toQObject()->objectName(), "blah"); } } void tst_qqmllanguage::assignNullStrings() { QQmlComponent component(&engine, testFileUrl("assignNullStrings.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->stringProperty().isNull()); QVERIFY(object->byteArrayProperty().isNull()); QMetaObject::invokeMethod(object.data(), "assignNullStringsFromJs", Qt::DirectConnection); QVERIFY(object->stringProperty().isNull()); QVERIFY(object->byteArrayProperty().isNull()); } void tst_qqmllanguage::bindJSValueToVar() { QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(root != nullptr); QObject *object = root->findChild("varProperties"); QVERIFY(isJSNumberType(object->property("test1").typeId())); QVERIFY(isJSNumberType(object->property("test2").typeId())); QCOMPARE(object->property("test3").typeId(), QMetaType::QString); QCOMPARE(object->property("test4").typeId(), QMetaType::QString); QCOMPARE(object->property("test5").typeId(), QMetaType::QString); QCOMPARE(object->property("test6").typeId(), QMetaType::QString); QCOMPARE(object->property("test7").typeId(), QMetaType::QString); QCOMPARE(object->property("test8").typeId(), QMetaType::QString); QCOMPARE(object->property("test9").typeId(), QMetaType::QString); QCOMPARE(object->property("test10").typeId(), QMetaType::Bool); QCOMPARE(object->property("test11").typeId(), QMetaType::Bool); QCOMPARE(object->property("test12").typeId(), QMetaType::QColor); QCOMPARE(object->property("test13").typeId(), QMetaType::QRectF); QCOMPARE(object->property("test14").typeId(), QMetaType::QPointF); QCOMPARE(object->property("test15").typeId(), QMetaType::QSizeF); QCOMPARE(object->property("test16").typeId(), QMetaType::QVector3D); QVERIFY(isJSNumberType(object->property("test1Bound").typeId())); QVERIFY(isJSNumberType(object->property("test20Bound").typeId())); QCOMPARE(object->property("test1"), QVariant(5)); QCOMPARE(object->property("test2"), QVariant((double)1.7)); QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String("Hello world!")))); QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String("#FF008800")))); QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String("10,10,10x10")))); QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String("10,10")))); QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String("10x10")))); QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String("100,100,100")))); QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String("#FF008800")))); QCOMPARE(object->property("test10"), QVariant(bool(true))); QCOMPARE(object->property("test11"), QVariant(bool(false))); QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5))); QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10))); QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10))); QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10))); QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); QCOMPARE(object->property("test1Bound"), QVariant(9)); QCOMPARE(object->property("test20Bound"), QVariant(27)); } void tst_qqmllanguage::bindJSValueToVariant() { QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(root != nullptr); QObject *object = root->findChild("variantProperties"); QVERIFY(isJSNumberType(object->property("test1").typeId())); QVERIFY(isJSNumberType(object->property("test2").typeId())); QCOMPARE(object->property("test3").typeId(), QMetaType::QString); QCOMPARE(object->property("test4").typeId(), QMetaType::QString); QCOMPARE(object->property("test5").typeId(), QMetaType::QString); QCOMPARE(object->property("test6").typeId(), QMetaType::QString); QCOMPARE(object->property("test7").typeId(), QMetaType::QString); QCOMPARE(object->property("test8").typeId(), QMetaType::QString); QCOMPARE(object->property("test9").typeId(), QMetaType::QString); QCOMPARE(object->property("test10").typeId(), QMetaType::Bool); QCOMPARE(object->property("test11").typeId(), QMetaType::Bool); QCOMPARE(object->property("test12").typeId(), QMetaType::QColor); QCOMPARE(object->property("test13").typeId(), QMetaType::QRectF); QCOMPARE(object->property("test14").typeId(), QMetaType::QPointF); QCOMPARE(object->property("test15").typeId(), QMetaType::QSizeF); QCOMPARE(object->property("test16").typeId(), QMetaType::QVector3D); QVERIFY(isJSNumberType(object->property("test1Bound").typeId())); QVERIFY(isJSNumberType(object->property("test20Bound").typeId())); QCOMPARE(object->property("test1"), QVariant(5)); QCOMPARE(object->property("test2"), QVariant((double)1.7)); QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String("Hello world!")))); QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String("#FF008800")))); QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String("10,10,10x10")))); QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String("10,10")))); QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String("10x10")))); QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String("100,100,100")))); QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String("#FF008800")))); QCOMPARE(object->property("test10"), QVariant(bool(true))); QCOMPARE(object->property("test11"), QVariant(bool(false))); QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5))); QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10))); QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10))); QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10))); QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); QCOMPARE(object->property("test1Bound"), QVariant(9)); QCOMPARE(object->property("test20Bound"), QVariant(27)); } void tst_qqmllanguage::bindJSValueToType() { QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(root != nullptr); { MyTypeObject *object = root->findChild("typedProperties"); QCOMPARE(object->intProperty(), 5); QCOMPARE(object->doubleProperty(), double(1.7)); QCOMPARE(object->stringProperty(), QString(QLatin1String("Hello world!"))); QCOMPARE(object->boolProperty(), true); QCOMPARE(object->colorProperty(), QColor::fromRgbF(0.2, 0.3, 0.4, 0.5)); QCOMPARE(object->rectFProperty(), QRectF(10, 10, 10, 10)); QCOMPARE(object->pointFProperty(), QPointF(10, 10)); QCOMPARE(object->sizeFProperty(), QSizeF(10, 10)); QCOMPARE(object->vectorProperty(), QVector3D(100, 100, 100)); } { MyTypeObject *object = root->findChild("stringProperties"); QCOMPARE(object->intProperty(), 1); QCOMPARE(object->doubleProperty(), double(1.7)); QCOMPARE(object->stringProperty(), QString(QLatin1String("Hello world!"))); QCOMPARE(object->boolProperty(), true); QCOMPARE(object->colorProperty(), QColor::fromRgb(0x00, 0x88, 0x00, 0xFF)); QCOMPARE(object->rectFProperty(), QRectF(10, 10, 10, 10)); QCOMPARE(object->pointFProperty(), QPointF(10, 10)); QCOMPARE(object->sizeFProperty(), QSizeF(10, 10)); QCOMPARE(object->vectorProperty(), QVector3D(100, 100, 100)); } } void tst_qqmllanguage::bindTypeToJSValue() { QQmlComponent component(&engine, testFileUrl("bindTypeToJSValue.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(root != nullptr); { MyQmlObject *object = root->findChild("flagProperty"); QVERIFY(object); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3)); } { MyQmlObject *object = root->findChild("enumProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(MyTypeObject::EnumVal2)); } { MyQmlObject *object = root->findChild("stringProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("Hello World!"))); } { MyQmlObject *object = root->findChild("uintProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(10)); } { MyQmlObject *object = root->findChild("intProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(-19)); } { MyQmlObject *object = root->findChild("realProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(23.2)); } { MyQmlObject *object = root->findChild("doubleProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(-19.7)); } { MyQmlObject *object = root->findChild("floatProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(8.5)); } { MyQmlObject *object = root->findChild("colorProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("r")).toNumber(), qreal(1.0)); QCOMPARE(value.property(QLatin1String("g")).toNumber(), qreal(0.0)); QCOMPARE(value.property(QLatin1String("b")).toNumber(), qreal(0.0)); } { MyQmlObject *object = root->findChild("dateProperty"); QJSValue value = object->qjsvalue(); QCOMPARE(value.toDateTime().isValid(), true); } { MyQmlObject *object = root->findChild("timeProperty"); QJSValue value = object->qjsvalue(); QCOMPARE(value.toDateTime().isValid(), true); } { MyQmlObject *object = root->findChild("dateTimeProperty"); QJSValue value = object->qjsvalue(); QCOMPARE(value.toDateTime().isValid(), true); } { MyQmlObject *object = root->findChild("pointProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(99)); QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(13)); } { MyQmlObject *object = root->findChild("pointFProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(-10.1)); QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(12.3)); } { MyQmlObject *object = root->findChild("rectProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(9)); QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(7)); QCOMPARE(value.property(QLatin1String("width")).toNumber(), qreal(100)); QCOMPARE(value.property(QLatin1String("height")).toNumber(), qreal(200)); } { MyQmlObject *object = root->findChild("rectFProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(1000.1)); QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(-10.9)); QCOMPARE(value.property(QLatin1String("width")).toNumber(), qreal(400)); QCOMPARE(value.property(QLatin1String("height")).toNumber(), qreal(90.99)); } { MyQmlObject *object = root->findChild("boolProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isBool()); QCOMPARE(value.toBool(), true); } { MyQmlObject *object = root->findChild("variantProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("Hello World!"))); } { MyQmlObject *object = root->findChild("vectorProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(10.0f)); QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(1.0f)); QCOMPARE(value.property(QLatin1String("z")).toNumber(), qreal(2.2f)); } { MyQmlObject *object = root->findChild("vector4Property"); QJSValue value = object->qjsvalue(); QVERIFY(value.isObject()); QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(10.0f)); QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(1.0f)); QCOMPARE(value.property(QLatin1String("z")).toNumber(), qreal(2.2f)); QCOMPARE(value.property(QLatin1String("w")).toNumber(), qreal(2.3f)); } { MyQmlObject *object = root->findChild("urlProperty"); QJSValue value = object->qjsvalue(); const QUrl encoded = QUrl::fromEncoded("main.qml?with%3cencoded%3edata", QUrl::TolerantMode); QCOMPARE(value.toString(), encoded.toString()); } { MyQmlObject *object = root->findChild("objectProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isQObject()); QVERIFY(qobject_cast(value.toQObject())); } { MyQmlObject *object = root->findChild("varProperty"); QJSValue value = object->qjsvalue(); QVERIFY(value.isString()); QCOMPARE(value.toString(), QString(QLatin1String("Hello World!"))); } } // Tests that custom parser types can be instantiated void tst_qqmllanguage::customParserTypes() { QQmlComponent component(&engine, testFileUrl("customParserTypes.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("count"), QVariant(2)); } // Tests that custom pursor types can be instantiated in ICs void tst_qqmllanguage::customParserTypeInInlineComponent() { QQmlComponent component(&engine, testFileUrl("customParserTypeInIC.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("count"), 2); } // Tests that the root item can be a custom component void tst_qqmllanguage::rootAsQmlComponent() { QQmlComponent component(&engine, testFileUrl("rootAsQmlComponent.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->property("x"), QVariant(11)); QCOMPARE(object->getChildren()->count(), 2); } void tst_qqmllanguage::rootItemIsComponent() { QQmlComponent component(&engine, testFileUrl("rootItemIsComponent.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(qobject_cast(root.data())); QScopedPointer other(qobject_cast(root.data())->create()); QVERIFY(!other.isNull()); QQmlContext *context = qmlContext(other.data()); QVERIFY(context); QCOMPARE(context->nameForObject(other.data()), QStringLiteral("blah")); } // Tests that components can be specified inline void tst_qqmllanguage::inlineQmlComponents() { QQmlComponent component(&engine, testFileUrl("inlineQmlComponents.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 1); QQmlComponent *comp = qobject_cast(object->getChildren()->at(0)); QVERIFY(comp != nullptr); QScopedPointer compObject(qobject_cast(comp->create())); QVERIFY(compObject != nullptr); QCOMPARE(compObject->value(), 11); } // Tests that types that have an id property have it set void tst_qqmllanguage::idProperty() { { QQmlComponent component(&engine, testFileUrl("idProperty.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 2); MyTypeObject *child = qobject_cast(object->getChildren()->at(0)); QVERIFY(child != nullptr); QCOMPARE(child->id(), QString("myObjectId")); QCOMPARE(object->property("object"), QVariant::fromValue((QObject *)child)); child = qobject_cast(object->getChildren()->at(1)); QVERIFY(child != nullptr); QCOMPARE(child->id(), QString("name.with.dots")); } { QQmlComponent component(&engine, testFileUrl("idPropertyMismatch.qml")); VERIFY_ERRORS(0); QScopedPointer root(component.create()); QVERIFY(!root.isNull()); QQmlContext *ctx = qmlContext(root.data()); QVERIFY(ctx); QCOMPARE(ctx->nameForObject(root.data()), QString("root")); } } // Tests automatic connection to notify signals if "onBlahChanged" syntax is used // even if the notify signal for "blah" is not called "blahChanged" void tst_qqmllanguage::autoNotifyConnection() { QQmlComponent component(&engine, testFileUrl("autoNotifyConnection.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QMetaProperty prop = object->metaObject()->property(object->metaObject()->indexOfProperty("receivedNotify")); QVERIFY(prop.isValid()); QCOMPARE(prop.read(object.data()), QVariant::fromValue(false)); object->setPropertyWithNotify(1); QCOMPARE(prop.read(object.data()), QVariant::fromValue(true)); } // Tests that signals can be assigned to void tst_qqmllanguage::assignSignal() { QQmlComponent component(&engine, testFileUrl("assignSignal.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlotWithArgs(9)"); emit object->basicParameterizedSignal(9); } void tst_qqmllanguage::assignSignalFunctionExpression() { QQmlComponent component(&engine, testFileUrl("assignSignalFunctionExpression.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlotWithArgs(9)"); emit object->basicParameterizedSignal(9); } void tst_qqmllanguage::overrideSignal_data() { QTest::addColumn("file"); QTest::addColumn("errorFile"); QTest::newRow("override signal with signal") << "overrideSignal.1.qml" << "overrideSignal.1.errors.txt"; QTest::newRow("override signal with method") << "overrideSignal.2.qml" << "overrideSignal.2.errors.txt"; QTest::newRow("override signal with property") << "overrideSignal.3.qml" << ""; QTest::newRow("override signal of alias property with signal") << "overrideSignal.4.qml" << "overrideSignal.4.errors.txt"; QTest::newRow("override signal of superclass with signal") << "overrideSignal.5.qml" << "overrideSignal.5.errors.txt"; QTest::newRow("override builtin signal with signal") << "overrideSignal.6.qml" << "overrideSignal.6.errors.txt"; } void tst_qqmllanguage::overrideSignal() { QFETCH(QString, file); QFETCH(QString, errorFile); QQmlComponent component(&engine, testFileUrl(file)); if (errorFile.isEmpty()) { VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); } else { VERIFY_ERRORS(errorFile.toLatin1().constData()); } } // Tests the creation and assignment of dynamic properties void tst_qqmllanguage::dynamicProperties() { QQmlComponent component(&engine, testFileUrl("dynamicProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QCOMPARE(object->property("intProperty"), QVariant(10)); QCOMPARE(object->property("boolProperty"), QVariant(false)); QCOMPARE(object->property("doubleProperty"), QVariant(-10.1)); QCOMPARE(object->property("realProperty"), QVariant((qreal)-19.9)); QCOMPARE(object->property("stringProperty"), QVariant("Hello World!")); QCOMPARE(object->property("urlProperty"), QVariant(QUrl("main.qml"))); QCOMPARE(object->property("colorProperty"), QVariant(QColor("red"))); QVariant date = object->property("dateProperty"); if (!date.convert(QMetaType(QMetaType::QDate))) QFAIL("could not convert to date"); QCOMPARE(date, QVariant(QDate(1945, 9, 2))); QCOMPARE(object->property("varProperty"), QVariant("Hello World!")); } // Test that nested types can use dynamic properties void tst_qqmllanguage::dynamicPropertiesNested() { QQmlComponent component(&engine, testFileUrl("dynamicPropertiesNested.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("super_a").toInt(), 11); // Overridden QCOMPARE(object->property("super_c").toInt(), 14); // Inherited QCOMPARE(object->property("a").toInt(), 13); // New QCOMPARE(object->property("b").toInt(), 12); // New } // Tests the creation and assignment to dynamic list properties void tst_qqmllanguage::listProperties() { QQmlComponent component(&engine, testFileUrl("listProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 2); } // Tests that initializing list properties of a base class does not crash // (QTBUG-82171) void tst_qqmllanguage::listPropertiesInheritanceNoCrash() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("listPropertiesChild.qml")); QScopedPointer object(component.create()); // should not crash QVERIFY(object != nullptr); } void tst_qqmllanguage::badListItemType() { QQmlComponent component(&engine, testFileUrl("badListItemType.qml")); QVERIFY(component.isError()); VERIFY_ERRORS("badListItemType.errors.txt"); } // Tests the creation and assignment of dynamic object properties // ### Not complete void tst_qqmllanguage::dynamicObjectProperties() { { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("objectProperty"), QVariant::fromValue((QObject*)nullptr)); QVERIFY(object->property("objectProperty2") != QVariant::fromValue((QObject*)nullptr)); } { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.2.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(object->property("objectProperty") != QVariant::fromValue((QObject*)nullptr)); } } // Tests the declaration of dynamic signals and slots void tst_qqmllanguage::dynamicSignalsAndSlots() { QTest::ignoreMessage(QtDebugMsg, "1921"); QQmlComponent component(&engine, testFileUrl("dynamicSignalsAndSlots.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(object->metaObject()->indexOfMethod("signal1()") != -1); QVERIFY(object->metaObject()->indexOfMethod("signal2()") != -1); QVERIFY(object->metaObject()->indexOfMethod("slot1()") != -1); QVERIFY(object->metaObject()->indexOfMethod("slot2()") != -1); QCOMPARE(object->property("test").toInt(), 0); QMetaObject::invokeMethod(object.data(), "slot3", Qt::DirectConnection, Q_ARG(QVariant, QVariant(10))); QCOMPARE(object->property("test").toInt(), 10); } void tst_qqmllanguage::simpleBindings() { QQmlComponent component(&engine, testFileUrl("simpleBindings.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value1"), QVariant(10)); QCOMPARE(object->property("value2"), QVariant(10)); QCOMPARE(object->property("value3"), QVariant(21)); QCOMPARE(object->property("value4"), QVariant(10)); QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object.data())); } class EvaluationCounter : public QObject { Q_OBJECT public: int counter = 0; Q_INVOKABLE void increaseEvaluationCounter() { ++counter; } }; void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings_data() { QTest::addColumn("fileName"); QTest::newRow("order1") << QString("noDoubleEvaluationForFlushedBindings.qml"); QTest::newRow("order2") << QString("noDoubleEvaluationForFlushedBindings.2.qml"); } void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings() { QFETCH(QString, fileName); QQmlEngine engine; EvaluationCounter stats; engine.rootContext()->setContextProperty("stats", &stats); QQmlComponent component(&engine, testFileUrl(fileName)); VERIFY_ERRORS(0); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QCOMPARE(stats.counter, 1); } void tst_qqmllanguage::autoComponentCreation() { { QQmlComponent component(&engine, testFileUrl("autoComponentCreation.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->componentProperty() != nullptr); QScopedPointer child(qobject_cast(object->componentProperty()->create())); QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } { QQmlComponent component(&engine, testFileUrl("autoComponentCreation.2.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->componentProperty() != nullptr); QScopedPointer child(qobject_cast(object->componentProperty()->create())); QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } } void tst_qqmllanguage::autoComponentCreationInGroupProperty() { QQmlComponent component(&engine, testFileUrl("autoComponentCreationInGroupProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->componentProperty() != nullptr); QScopedPointer child(qobject_cast(object->componentProperty()->create())); QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } void tst_qqmllanguage::propertyValueSource() { { QQmlComponent component(&engine, testFileUrl("propertyValueSource.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QList valueSources; QObjectList allChildren = object->findChildren(); foreach (QObject *child, allChildren) { if (qobject_cast(child)) valueSources.append(child); } QCOMPARE(valueSources.count(), 1); MyPropertyValueSource *valueSource = qobject_cast(valueSources.at(0)); QVERIFY(valueSource != nullptr); QCOMPARE(valueSource->prop.object(), qobject_cast(object.data())); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); } { QQmlComponent component(&engine, testFileUrl("propertyValueSource.2.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QList valueSources; QObjectList allChildren = object->findChildren(); foreach (QObject *child, allChildren) { if (qobject_cast(child)) valueSources.append(child); } QCOMPARE(valueSources.count(), 1); MyPropertyValueSource *valueSource = qobject_cast(valueSources.at(0)); QVERIFY(valueSource != nullptr); QCOMPARE(valueSource->prop.object(), qobject_cast(object.data())); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); } } void tst_qqmllanguage::requiredProperty() { QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("requiredProperties.1.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object); } { QQmlComponent component(&engine, testFileUrl("requiredProperties.2.qml")); QVERIFY(!component.errors().empty()); } { QQmlComponent component(&engine, testFileUrl("requiredProperties.4.qml")); QScopedPointer object(component.create()); QVERIFY(!component.errors().empty()); QVERIFY(component.errorString().contains("Required property objectName was not initialized")); } { QQmlComponent component(&engine, testFileUrl("requiredProperties.3.qml")); QScopedPointer object(component.create()); QVERIFY(!component.errors().empty()); QVERIFY(component.errorString().contains("Required property i was not initialized")); } { QQmlComponent component(&engine, testFileUrl("requiredProperties.5.qml")); QScopedPointer object(component.create()); QVERIFY(!component.errors().empty()); QVERIFY(component.errorString().contains("Required property i was not initialized")); } { QQmlComponent component(&engine, testFileUrl("requiredProperties.6.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object); } { QQmlComponent component(&engine, testFileUrl("requiredProperties.7.qml")); QScopedPointer object(component.create()); QVERIFY(!component.errors().empty()); QVERIFY(component.errorString().contains("Property blub was marked as required but does not exist")); } { QQmlComponent component(&engine, testFileUrl("RequiredListPropertiesUser.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object); } } class MyClassWithRequiredProperty : public QObject { public: Q_OBJECT Q_PROPERTY(int test MEMBER m_test REQUIRED NOTIFY testChanged) Q_SIGNAL void testChanged(); private: int m_test; }; class ChildClassWithoutOwnRequired : public MyClassWithRequiredProperty { public: Q_OBJECT Q_PROPERTY(int test2 MEMBER m_test2 NOTIFY test2Changed) Q_SIGNAL void test2Changed(); private: int m_test2; }; class ChildClassWithOwnRequired : public MyClassWithRequiredProperty { public: Q_OBJECT Q_PROPERTY(int test2 MEMBER m_test2 REQUIRED NOTIFY test2Changed) Q_SIGNAL void test2Changed(); private: int m_test2; }; void tst_qqmllanguage::requiredPropertyFromCpp_data() { qmlRegisterType("example.org", 1, 0, "MyClass"); qmlRegisterType("example.org", 1, 0, "Child"); qmlRegisterType("example.org", 1, 0, "Child2"); QTest::addColumn("setFile"); QTest::addColumn("notSetFile"); QTest::addColumn("errorMessage"); QTest::addColumn("expectedValue"); QTest::addRow("direct") << testFileUrl("cppRequiredProperty.qml") << testFileUrl("cppRequiredPropertyNotSet.qml") << QString(":4 Required property test was not initialized\n") << 42; QTest::addRow("in parent") << testFileUrl("cppRequiredPropertyInParent.qml") << testFileUrl("cppRequiredPropertyInParentNotSet.qml") << QString(":4 Required property test was not initialized\n") << 42; QTest::addRow("in child and parent") << testFileUrl("cppRequiredPropertyInChildAndParent.qml") << testFileUrl("cppRequiredPropertyInChildAndParentNotSet.qml") << QString(":4 Required property test2 was not initialized\n") << 18; } void tst_qqmllanguage::requiredPropertyFromCpp() { QQmlEngine engine; QFETCH(QUrl, setFile); QFETCH(QUrl, notSetFile); QFETCH(QString, errorMessage); QFETCH(int, expectedValue); { QQmlComponent comp(&engine, notSetFile); QScopedPointer o { comp.create() }; QVERIFY(o.isNull()); QVERIFY(comp.isError()); QCOMPARE(comp.errorString(), notSetFile.toString() + errorMessage); } { QQmlComponent comp(&engine, setFile); QScopedPointer o { comp.create() }; QVERIFY(!o.isNull()); QCOMPARE(o->property("test").toInt(), expectedValue); } } void tst_qqmllanguage::attachedProperties() { QQmlComponent component(&engine, testFileUrl("attachedProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QObject *attached = qmlAttachedPropertiesObject(object.data()); QVERIFY(attached != nullptr); QCOMPARE(attached->property("value"), QVariant(10)); QCOMPARE(attached->property("value2"), QVariant(13)); } // Tests non-static object properties void tst_qqmllanguage::dynamicObjects() { QQmlComponent component(&engine, testFileUrl("dynamicObject.1.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); } // Tests the registration of custom variant string converters void tst_qqmllanguage::customVariantTypes() { QQmlComponent component(&engine, testFileUrl("customVariantTypes.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->customType().a, 10); } void tst_qqmllanguage::valueTypes() { QQmlComponent component(&engine, testFileUrl("valueTypes.qml")); VERIFY_ERRORS(0); QString message = component.url().toString() + ":2:1: QML MyTypeObject: Binding loop detected for property \"rectProperty.width\""; QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->rectProperty(), QRect(10, 11, 12, 13)); QCOMPARE(object->rectProperty2(), QRect(10, 11, 12, 13)); QCOMPARE(object->intProperty(), 10); object->doAction(); QCOMPARE(object->rectProperty(), QRect(12, 11, 14, 13)); QCOMPARE(object->rectProperty2(), QRect(12, 11, 14, 13)); QCOMPARE(object->intProperty(), 12); // ### #if 0 QQmlProperty p(object, "rectProperty.x"); QCOMPARE(p.read(), QVariant(12)); p.write(13); QCOMPARE(p.read(), QVariant(13)); quint32 r = QQmlPropertyPrivate::saveValueType(p.coreIndex(), p.valueTypeCoreIndex()); QQmlProperty p2; QQmlPropertyPrivate::restore(p2, r, object); QCOMPARE(p2.read(), QVariant(13)); #endif } void tst_qqmllanguage::cppnamespace() { QScopedPointer object; auto create = [&](const char *file) { QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); object.reset(component.create()); QVERIFY(object != nullptr); }; auto createAndCheck = [&](const char *file) { create(file); return !QTest::currentTestFailed(); }; QVERIFY(createAndCheck("cppnamespace.qml")); QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2); QVERIFY(createAndCheck("cppstaticnamespace.qml")); QCOMPARE(object->property("intProperty").toInt(), (int)MyStaticNamespace::MyOtherNSEnum::OtherKey2); QVERIFY(createAndCheck("cppnamespace.2.qml")); QVERIFY(createAndCheck("cppstaticnamespace.2.qml")); } void tst_qqmllanguage::aliasProperties() { // Simple "int" alias { QQmlComponent component(&engine, testFileUrl("alias.1.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias QCOMPARE(object->property("valueAlias").toInt(), 10); object->setProperty("value", QVariant(13)); QCOMPARE(object->property("valueAlias").toInt(), 13); // Write through alias object->setProperty("valueAlias", QVariant(19)); QCOMPARE(object->property("valueAlias").toInt(), 19); QCOMPARE(object->property("value").toInt(), 19); } // Complex object alias { QQmlComponent component(&engine, testFileUrl("alias.2.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias MyQmlObject *v = qvariant_cast(object->property("aliasObject")); QVERIFY(v != nullptr); QCOMPARE(v->value(), 10); // Write through alias MyQmlObject *v2 = new MyQmlObject(); v2->setParent(object.data()); object->setProperty("aliasObject", QVariant::fromValue(v2)); MyQmlObject *v3 = qvariant_cast(object->property("aliasObject")); QVERIFY(v3 != nullptr); QCOMPARE(v3, v2); } // Nested aliases { QQmlComponent component(&engine, testFileUrl("alias.3.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 1892); QCOMPARE(object->property("value2").toInt(), 1892); object->setProperty("value", QVariant(1313)); QCOMPARE(object->property("value").toInt(), 1313); QCOMPARE(object->property("value2").toInt(), 1313); object->setProperty("value2", QVariant(8080)); QCOMPARE(object->property("value").toInt(), 8080); QCOMPARE(object->property("value2").toInt(), 8080); } // Enum aliases { QQmlComponent component(&engine, testFileUrl("alias.4.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("enumAlias").toInt(), 1); } // Id aliases { QQmlComponent component(&engine, testFileUrl("alias.5.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVariant v = object->property("otherAlias"); QCOMPARE(v.typeId(), qMetaTypeId()); MyQmlObject *o = qvariant_cast(v); QCOMPARE(o->value(), 10); delete o; v = object->property("otherAlias"); QCOMPARE(v.typeId(), qMetaTypeId()); o = qvariant_cast(v); QVERIFY(!o); } // Nested aliases - this used to cause a crash { QQmlComponent component(&engine, testFileUrl("alias.6.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 1923); } // Ptr Alias Cleanup - check that aliases to ptr types return 0 // if the object aliased to is removed { QQmlComponent component(&engine, testFileUrl("alias.7.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QObject *object1 = qvariant_cast(object->property("object")); QVERIFY(object1 != nullptr); QObject *object2 = qvariant_cast(object1->property("object")); QVERIFY(object2 != nullptr); QObject *alias = qvariant_cast(object->property("aliasedObject")); QCOMPARE(alias, object2); delete object1; QObject *alias2 = object.data(); // "Random" start value int status = -1; void *a[] = { &alias2, nullptr, &status }; QMetaObject::metacall(object.data(), QMetaObject::ReadProperty, object->metaObject()->indexOfProperty("aliasedObject"), a); QVERIFY(!alias2); } // Simple composite type { QQmlComponent component(&engine, testFileUrl("alias.8.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 10); } // Complex composite type { QQmlComponent component(&engine, testFileUrl("alias.9.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 10); } // Valuetype alias // Simple "int" alias { QQmlComponent component(&engine, testFileUrl("alias.10.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias QCOMPARE(object->property("valueAlias").toRect(), QRect(10, 11, 9, 8)); object->setProperty("rectProperty", QVariant(QRect(33, 12, 99, 100))); QCOMPARE(object->property("valueAlias").toRect(), QRect(33, 12, 99, 100)); // Write through alias object->setProperty("valueAlias", QVariant(QRect(3, 3, 4, 9))); QCOMPARE(object->property("valueAlias").toRect(), QRect(3, 3, 4, 9)); QCOMPARE(object->property("rectProperty").toRect(), QRect(3, 3, 4, 9)); } // Valuetype sub-alias { QQmlComponent component(&engine, testFileUrl("alias.11.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias QCOMPARE(object->property("aliasProperty").toInt(), 19); object->setProperty("rectProperty", QVariant(QRect(33, 8, 102, 111))); QCOMPARE(object->property("aliasProperty").toInt(), 33); // Write through alias object->setProperty("aliasProperty", QVariant(4)); QCOMPARE(object->property("aliasProperty").toInt(), 4); QCOMPARE(object->property("rectProperty").toRect(), QRect(4, 8, 102, 111)); } // Nested aliases with a qml file { QQmlComponent component(&engine, testFileUrl("alias.12.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QPointer subObject = qvariant_cast(object->property("referencingSubObject")); QVERIFY(!subObject.isNull()); QVERIFY(subObject->property("success").toBool()); } // Nested aliases with a qml file with reverse ordering { // This is known to fail at the moment. QQmlComponent component(&engine, testFileUrl("alias.13.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QPointer subObject = qvariant_cast(object->property("referencingSubObject")); QVERIFY(!subObject.isNull()); QVERIFY(subObject->property("success").toBool()); } // "Nested" aliases within an object that require iterative resolution { QQmlComponent component(&engine, testFileUrl("alias.14.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QPointer subObject = qvariant_cast(object->property("referencingSubObject")); QVERIFY(!subObject.isNull()); QVERIFY(subObject->property("success").toBool()); } // Property bindings on group properties that are actually aliases (QTBUG-51043) { QQmlComponent component(&engine, testFileUrl("alias.15.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QPointer subItem = qvariant_cast(object->property("symbol")); QVERIFY(!subItem.isNull()); QCOMPARE(subItem->property("y").toInt(), 1); } // Alias to sub-object with binding (QTBUG-57041) { // This is shold *not* crash. QQmlComponent component(&engine, testFileUrl("alias.16.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); } // Alias to grouped property { QQmlComponent component(&engine, testFileUrl("alias.17.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QVERIFY(object->property("success").toBool()); } // Alias to grouped property updates { QQmlComponent component(&engine, testFileUrl("alias.17.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QObject *aliasUser = object->findChild(QLatin1String("aliasUser")); QVERIFY(aliasUser); QQmlProperty checkValueProp(object.get(), "checkValue"); QVERIFY(checkValueProp.isValid()); checkValueProp.write(777); QCOMPARE(object->property("checkValue").toInt(), 777); QCOMPARE(aliasUser->property("topMargin").toInt(), 777); } // Write to alias to grouped property { QQmlComponent component(&engine, testFileUrl("alias.17.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QObject *aliasUser = object->findChild(QLatin1String("aliasUser")); QVERIFY(aliasUser); QQmlProperty topMarginProp {aliasUser, "topMargin"}; QVERIFY(topMarginProp.isValid()); topMarginProp.write(777); QObject *myItem = object->findChild(QLatin1String("myItem")); QVERIFY(myItem); auto anchors = myItem->property("anchors").value(); QVERIFY(anchors); QCOMPARE(anchors->property("topMargin").toInt(), 777); } // Binding to alias to grouped property gets updated { QQmlComponent component(&engine, testFileUrl("alias.17.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QObject *aliasUser = object->findChild(QLatin1String("aliasUser")); QVERIFY(aliasUser); QQmlProperty topMarginProp {aliasUser, "topMargin"}; QVERIFY(topMarginProp.isValid()); topMarginProp.write(20); QObject *myText = object->findChild(QLatin1String("myText")); QVERIFY(myText); auto text = myText->property("text").toString(); QCOMPARE(text, "alias:\n20"); } { QQmlComponent component(&engine, testFileUrl("alias.18.qml")); VERIFY_ERRORS("alias.18.errors.txt"); } } // QTBUG-13374 Test that alias properties and signals can coexist void tst_qqmllanguage::aliasPropertiesAndSignals() { QQmlComponent component(&engine, testFileUrl("aliasPropertiesAndSignals.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("test").toBool(), true); } void tst_qqmllanguage::qtbug_89822() { QQmlComponent component(&engine, testFileUrl("qtbug_89822.qml")); VERIFY_ERRORS("qtbug_89822.errors.txt"); } // Test that the root element in a composite type can be a Component void tst_qqmllanguage::componentCompositeType() { QQmlComponent component(&engine, testFileUrl("componentCompositeType.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); } class TestType : public QObject { Q_OBJECT public: TestType(QObject *p=nullptr) : QObject(p) {} }; class TestType2 : public QObject { Q_OBJECT public: TestType2(QObject *p=nullptr) : QObject(p) {} }; void tst_qqmllanguage::i18n_data() { QTest::addColumn("file"); QTest::addColumn("stringProperty"); QTest::newRow("i18nStrings") << "i18nStrings.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245 (5 accented 'a' letters)"); QTest::newRow("i18nDeclaredPropertyNames") << "i18nDeclaredPropertyNames.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 10"); QTest::newRow("i18nDeclaredPropertyUse") << "i18nDeclaredPropertyUse.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 15"); QTest::newRow("i18nScript") << "i18nScript.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 20"); QTest::newRow("i18nType") << "i18nType.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 30"); QTest::newRow("i18nNameSpace") << "i18nNameSpace.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 40"); } void tst_qqmllanguage::i18n() { QFETCH(QString, file); QFETCH(QString, stringProperty); QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->stringProperty(), stringProperty); } // Check that the Component::onCompleted attached property works void tst_qqmllanguage::onCompleted() { QQmlComponent component(&engine, testFileUrl("onCompleted.qml")); VERIFY_ERRORS(0); QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); QTest::ignoreMessage(QtDebugMsg, "Completed 10 11"); QScopedPointer object(component.create()); QVERIFY(object != nullptr); } // Check that the Component::onDestruction attached property works void tst_qqmllanguage::onDestruction() { QQmlComponent component(&engine, testFileUrl("onDestruction.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QTest::ignoreMessage(QtDebugMsg, "Destruction 6 10"); QTest::ignoreMessage(QtDebugMsg, "Destruction 6 10"); QTest::ignoreMessage(QtDebugMsg, "Destruction 10 11"); } // Check that assignments to QQmlScriptString properties work void tst_qqmllanguage::scriptString() { { QQmlComponent component(&engine, testFileUrl("scriptString.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(!object->scriptProperty().isEmpty()); QCOMPARE(object->scriptProperty().stringLiteral(), QString()); bool ok; QCOMPARE(object->scriptProperty().numberLiteral(&ok), qreal(0.)); QCOMPARE(ok, false); const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(object->scriptProperty()); QVERIFY(scriptPrivate != nullptr); QCOMPARE(scriptPrivate->script, QString("foo + bar")); QCOMPARE(scriptPrivate->scope, qobject_cast(object.data())); QCOMPARE(scriptPrivate->context, qmlContext(object.data())); QVERIFY(object->grouped() != nullptr); const QQmlScriptStringPrivate *groupedPrivate = QQmlScriptStringPrivate::get(object->grouped()->script()); QCOMPARE(groupedPrivate->script, QString("console.log(1921)")); QCOMPARE(groupedPrivate->scope, qobject_cast(object.data())); QCOMPARE(groupedPrivate->context, qmlContext(object.data())); } { QQmlComponent component(&engine, testFileUrl("scriptString2.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().stringLiteral(), QString("hello\\n\\\"world\\\"")); } { QQmlComponent component(&engine, testFileUrl("scriptString3.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); bool ok; QCOMPARE(object->scriptProperty().numberLiteral(&ok), qreal(12.345)); QCOMPARE(ok, true); } { QQmlComponent component(&engine, testFileUrl("scriptString4.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); bool ok; QCOMPARE(object->scriptProperty().booleanLiteral(&ok), true); QCOMPARE(ok, true); } { QQmlComponent component(&engine, testFileUrl("scriptString5.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().isNullLiteral(), true); } { QQmlComponent component(&engine, testFileUrl("scriptString6.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().isUndefinedLiteral(), true); } { QQmlComponent component(&engine, testFileUrl("scriptString7.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QQmlScriptString ss = object->scriptProperty(); { QQmlExpression expr(ss, /*context*/nullptr, object.data()); QCOMPARE(expr.evaluate().toInt(), int(100)); } { SimpleObjectWithCustomParser testScope; QVERIFY(testScope.metaObject()->indexOfProperty("intProperty") != object->metaObject()->indexOfProperty("intProperty")); testScope.setIntProperty(42); QQmlExpression expr(ss, /*context*/nullptr, &testScope); QCOMPARE(expr.evaluate().toInt(), int(42)); } } } // Check that assignments to QQmlScriptString properties works also from within Javascript void tst_qqmllanguage::scriptStringJs() { QQmlComponent component(&engine, testFileUrl("scriptStringJs.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QQmlContext *context = QQmlEngine::contextForObject(object.data()); QVERIFY(context != nullptr); bool ok; QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\" hello \\\" world \"")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(!object->scriptProperty().isUndefinedLiteral()); QVERIFY(!object->scriptProperty().isNullLiteral()); QCOMPARE(object->scriptProperty().stringLiteral(), QString(" hello \\\" world ")); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); QJSValue inst = engine.newQObject(object.data()); QJSValue func = engine.evaluate("(function(value) { this.scriptProperty = value })"); func.callWithInstance(inst, QJSValueList() << "test a \"string "); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\"test a \\\"string \"")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(!object->scriptProperty().isUndefinedLiteral()); QVERIFY(!object->scriptProperty().isNullLiteral()); QCOMPARE(object->scriptProperty().stringLiteral(), QString("test a \\\"string ")); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); func.callWithInstance(inst, QJSValueList() << QJSValue::UndefinedValue); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("undefined")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(object->scriptProperty().isUndefinedLiteral()); QVERIFY(!object->scriptProperty().isNullLiteral()); QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); func.callWithInstance(inst, QJSValueList() << true); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("true")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(!object->scriptProperty().isUndefinedLiteral()); QVERIFY(!object->scriptProperty().isNullLiteral()); QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(object->scriptProperty().booleanLiteral(&ok) && ok); func.callWithInstance(inst, QJSValueList() << false); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("false")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(!object->scriptProperty().isUndefinedLiteral()); QVERIFY(!object->scriptProperty().isNullLiteral()); QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && ok); func.callWithInstance(inst, QJSValueList() << QJSValue::NullValue); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("null")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(!object->scriptProperty().isUndefinedLiteral()); QVERIFY(object->scriptProperty().isNullLiteral()); QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); func.callWithInstance(inst, QJSValueList() << 12.34); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("12.34")); QVERIFY(!object->scriptProperty().isEmpty()); QVERIFY(!object->scriptProperty().isUndefinedLiteral()); QVERIFY(!object->scriptProperty().isNullLiteral()); QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); QVERIFY(object->scriptProperty().numberLiteral(&ok) == 12.34 && ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); } struct FreeUnitData { static void cleanup(const QV4::CompiledData::Unit *readOnlyQmlUnit) { if (readOnlyQmlUnit && !(readOnlyQmlUnit->flags & QV4::CompiledData::Unit::StaticData)) free(const_cast(readOnlyQmlUnit)); } }; void tst_qqmllanguage::scriptStringWithoutSourceCode() { QUrl url = testFileUrl("scriptString7.qml"); QScopedPointer readOnlyQmlUnit; { QQmlEnginePrivate *eng = QQmlEnginePrivate::get(&engine); QQmlRefPointer td = eng->typeLoader.getType(url); Q_ASSERT(td); QQmlRefPointer compilationUnit = td->compilationUnit(); readOnlyQmlUnit.reset(compilationUnit->unitData()); Q_ASSERT(readOnlyQmlUnit); QV4::CompiledData::Unit *qmlUnit = reinterpret_cast(malloc(readOnlyQmlUnit->unitSize)); memcpy(qmlUnit, readOnlyQmlUnit.data(), readOnlyQmlUnit->unitSize); qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; compilationUnit->setUnitData(qmlUnit); const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); QCOMPARE(compilationUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); quint32 i; for (i = 0; i < rootObject->nBindings; ++i) { const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i; if (compilationUnit->stringAt(binding->propertyNameIndex) != QString("scriptProperty")) continue; QCOMPARE(compilationUnit->bindingValueAsScriptString(binding), QString("intProperty")); const_cast(binding)->stringIndex = 0; // empty string index QVERIFY(compilationUnit->bindingValueAsScriptString(binding).isEmpty()); break; } QVERIFY(i < rootObject->nBindings); } QQmlComponent component(&engine, url); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QQmlScriptString ss = object->scriptProperty(); QVERIFY(!ss.isEmpty()); QCOMPARE(ss.stringLiteral(), QString()); bool ok; QCOMPARE(ss.numberLiteral(&ok), qreal(0.)); QCOMPARE(ok, false); const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(ss); QVERIFY(scriptPrivate != nullptr); QVERIFY(scriptPrivate->script.isEmpty()); QCOMPARE(scriptPrivate->scope, qobject_cast(object.data())); QCOMPARE(scriptPrivate->context, qmlContext(object.data())); { QQmlExpression expr(ss, /*context*/nullptr, object.data()); QCOMPARE(expr.evaluate().toInt(), int(100)); } } // Test the QQmlScriptString comparison operators. The script strings are considered // equal if there evaluation would produce the same result. void tst_qqmllanguage::scriptStringComparison() { QQmlComponent component1(&engine, testFileUrl("scriptString.qml")); QVERIFY(!component1.isError() && component1.errors().isEmpty()); QScopedPointer object1(qobject_cast(component1.create())); QVERIFY(object1 != nullptr); QQmlComponent component2(&engine, testFileUrl("scriptString2.qml")); QVERIFY(!component2.isError() && component2.errors().isEmpty()); QScopedPointer object2(qobject_cast(component2.create())); QVERIFY(object2 != nullptr); QQmlComponent component3(&engine, testFileUrl("scriptString3.qml")); QVERIFY(!component3.isError() && component3.errors().isEmpty()); QScopedPointer object3(qobject_cast(component3.create())); QVERIFY(object3 != nullptr); //QJSValue inst1 = engine.newQObject(object1); QJSValue inst2 = engine.newQObject(object2.data()); QJSValue inst3 = engine.newQObject(object3.data()); QJSValue func = engine.evaluate("(function(value) { this.scriptProperty = value })"); const QString s = "hello\\n\\\"world\\\""; const qreal n = 12.345; bool ok; QCOMPARE(object2->scriptProperty().stringLiteral(), s); QVERIFY(object3->scriptProperty().numberLiteral(&ok) == n && ok); QCOMPARE(object1->scriptProperty(), object1->scriptProperty()); QCOMPARE(object2->scriptProperty(), object2->scriptProperty()); QCOMPARE(object3->scriptProperty(), object3->scriptProperty()); QVERIFY(object2->scriptProperty() != object3->scriptProperty()); QVERIFY(object1->scriptProperty() != object2->scriptProperty()); QVERIFY(object1->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst2, QJSValueList() << n); QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); func.callWithInstance(inst2, QJSValueList() << s); QVERIFY(object2->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst3, QJSValueList() << s); QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); func.callWithInstance(inst2, QJSValueList() << QJSValue::UndefinedValue); QVERIFY(object2->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst3, QJSValueList() << QJSValue::UndefinedValue); QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); func.callWithInstance(inst2, QJSValueList() << QJSValue::NullValue); QVERIFY(object2->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst3, QJSValueList() << QJSValue::NullValue); QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); func.callWithInstance(inst2, QJSValueList() << false); QVERIFY(object2->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst3, QJSValueList() << false); QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); func.callWithInstance(inst2, QJSValueList() << true); QVERIFY(object2->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst3, QJSValueList() << true); QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); QVERIFY(object1->scriptProperty() != object2->scriptProperty()); object2->setScriptProperty(object1->scriptProperty()); QCOMPARE(object1->scriptProperty(), object2->scriptProperty()); QVERIFY(object1->scriptProperty() != object3->scriptProperty()); func.callWithInstance(inst3, QJSValueList() << engine.toScriptValue(object1->scriptProperty())); QCOMPARE(object1->scriptProperty(), object3->scriptProperty()); // While this are two instances of the same object they are still considered different // because the (none literal) script string may access variables which have different // values in both instances and hence evaluated to different results. QScopedPointer object1_2(qobject_cast(component1.create())); QVERIFY(object1_2 != nullptr); QVERIFY(object1->scriptProperty() != object1_2->scriptProperty()); } // Check that default property assignments are correctly spliced into explicit // property assignments void tst_qqmllanguage::defaultPropertyListOrder() { QQmlComponent component(&engine, testFileUrl("defaultPropertyListOrder.qml")); VERIFY_ERRORS(0); QScopedPointer container(qobject_cast(component.create())); QVERIFY(container != nullptr); QCOMPARE(container->getChildren()->count(), 6); QCOMPARE(container->getChildren()->at(0)->property("index"), QVariant(0)); QCOMPARE(container->getChildren()->at(1)->property("index"), QVariant(1)); QCOMPARE(container->getChildren()->at(2)->property("index"), QVariant(2)); QCOMPARE(container->getChildren()->at(3)->property("index"), QVariant(3)); QCOMPARE(container->getChildren()->at(4)->property("index"), QVariant(4)); QCOMPARE(container->getChildren()->at(5)->property("index"), QVariant(5)); } void tst_qqmllanguage::declaredPropertyValues() { QQmlComponent component(&engine, testFileUrl("declaredPropertyValues.qml")); VERIFY_ERRORS(0); } void tst_qqmllanguage::dontDoubleCallClassBegin() { QQmlComponent component(&engine, testFileUrl("dontDoubleCallClassBegin.qml")); QScopedPointer o(component.create()); QVERIFY(o); MyParserStatus *o2 = qobject_cast(qvariant_cast(o->property("object"))); QVERIFY(o2); QCOMPARE(o2->classBeginCount(), 1); QCOMPARE(o2->componentCompleteCount(), 1); } void tst_qqmllanguage::reservedWords_data() { QTest::addColumn("word"); QTest::newRow("abstract") << QByteArray("abstract"); QTest::newRow("as") << QByteArray("as"); QTest::newRow("boolean") << QByteArray("boolean"); QTest::newRow("break") << QByteArray("break"); QTest::newRow("byte") << QByteArray("byte"); QTest::newRow("case") << QByteArray("case"); QTest::newRow("catch") << QByteArray("catch"); QTest::newRow("char") << QByteArray("char"); QTest::newRow("class") << QByteArray("class"); QTest::newRow("continue") << QByteArray("continue"); QTest::newRow("const") << QByteArray("const"); QTest::newRow("debugger") << QByteArray("debugger"); QTest::newRow("default") << QByteArray("default"); QTest::newRow("delete") << QByteArray("delete"); QTest::newRow("do") << QByteArray("do"); QTest::newRow("double") << QByteArray("double"); QTest::newRow("else") << QByteArray("else"); QTest::newRow("enum") << QByteArray("enum"); QTest::newRow("export") << QByteArray("export"); QTest::newRow("extends") << QByteArray("extends"); QTest::newRow("false") << QByteArray("false"); QTest::newRow("final") << QByteArray("final"); QTest::newRow("finally") << QByteArray("finally"); QTest::newRow("float") << QByteArray("float"); QTest::newRow("for") << QByteArray("for"); QTest::newRow("function") << QByteArray("function"); QTest::newRow("goto") << QByteArray("goto"); QTest::newRow("if") << QByteArray("if"); QTest::newRow("implements") << QByteArray("implements"); QTest::newRow("import") << QByteArray("import"); QTest::newRow("pragma") << QByteArray("pragma"); QTest::newRow("in") << QByteArray("in"); QTest::newRow("instanceof") << QByteArray("instanceof"); QTest::newRow("int") << QByteArray("int"); QTest::newRow("interface") << QByteArray("interface"); QTest::newRow("long") << QByteArray("long"); QTest::newRow("native") << QByteArray("native"); QTest::newRow("new") << QByteArray("new"); QTest::newRow("null") << QByteArray("null"); QTest::newRow("package") << QByteArray("package"); QTest::newRow("private") << QByteArray("private"); QTest::newRow("protected") << QByteArray("protected"); QTest::newRow("public") << QByteArray("public"); QTest::newRow("return") << QByteArray("return"); QTest::newRow("short") << QByteArray("short"); QTest::newRow("static") << QByteArray("static"); QTest::newRow("super") << QByteArray("super"); QTest::newRow("switch") << QByteArray("switch"); QTest::newRow("synchronized") << QByteArray("synchronized"); QTest::newRow("this") << QByteArray("this"); QTest::newRow("throw") << QByteArray("throw"); QTest::newRow("throws") << QByteArray("throws"); QTest::newRow("transient") << QByteArray("transient"); QTest::newRow("true") << QByteArray("true"); QTest::newRow("try") << QByteArray("try"); QTest::newRow("typeof") << QByteArray("typeof"); QTest::newRow("var") << QByteArray("var"); QTest::newRow("void") << QByteArray("void"); QTest::newRow("volatile") << QByteArray("volatile"); QTest::newRow("while") << QByteArray("while"); QTest::newRow("with") << QByteArray("with"); } void tst_qqmllanguage::reservedWords() { QFETCH(QByteArray, word); QQmlComponent component(&engine); component.setData("import QtQuick 2.0\nQtObject { property string " + word + " }", QUrl()); QCOMPARE(component.errorString(), QLatin1String(":2 Expected token `identifier'\n")); } // Check that first child of qml is of given type. Empty type insists on error. void tst_qqmllanguage::testType(const QString& qml, const QString& type, const QString& expectederror, bool partialMatch) { if (engine.importPathList() == defaultImportPathList) engine.addImportPath(testFile("lib")); QQmlComponent component(&engine); component.setData(qml.toUtf8(), testFileUrl("empty.qml")); // just a file for relative local imports QTRY_VERIFY(!component.isLoading()); if (type.isEmpty()) { QVERIFY(component.isError()); QString actualerror; foreach (const QQmlError e, component.errors()) { if (!actualerror.isEmpty()) actualerror.append("; "); actualerror.append(e.description()); } QCOMPARE(actualerror.left(partialMatch ? expectederror.length(): -1),expectederror); } else { VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); const QMetaObject *meta = object->metaObject(); for (; meta; meta = meta->superClass()) { const QString className(meta->className()); if (!className.contains("_QMLTYPE_") && !className.contains("_QML_")) { QCOMPARE(className, type); break; } } QVERIFY(meta != nullptr); } engine.setImportPathList(defaultImportPathList); } // QTBUG-17276 void tst_qqmllanguage::inlineAssignmentsOverrideBindings() { QQmlComponent component(&engine, testFileUrl("inlineAssignmentsOverrideBindings.qml")); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 11); } // QTBUG-19354 void tst_qqmllanguage::nestedComponentRoots() { QQmlComponent component(&engine, testFileUrl("nestedComponentRoots.qml")); } // Import tests (QT-558) void tst_qqmllanguage::importsBuiltin_data() { // QT-610 QTest::addColumn("qml"); QTest::addColumn("type"); QTest::addColumn("error"); // import built-ins QTest::newRow("missing import") << "Test {}" << "" << "Test is not a type"; QTest::newRow("not in version 0.0") << "import org.qtproject.Test 0.0\n" "Test {}" << "" << "Test is not a type"; QTest::newRow("version not installed") << "import org.qtproject.Test 99.0\n" "Test {}" << "" << "module \"org.qtproject.Test\" version 99.0 is not installed"; QTest::newRow("in version 0.0") << "import org.qtproject.Test 0.0\n" "TestTP {}" << "TestType" << ""; QTest::newRow("qualified in version 0.0") << "import org.qtproject.Test 0.0 as T\n" "T.TestTP {}" << "TestType" << ""; QTest::newRow("in version 1.0") << "import org.qtproject.Test 1.0\n" "Test {}" << "TestType" << ""; QTest::newRow("qualified wrong") << "import org.qtproject.Test 1.0 as T\n" // QT-610 "Test {}" << "" << "Test is not a type"; QTest::newRow("qualified right") << "import org.qtproject.Test 1.0 as T\n" "T.Test {}" << "TestType" << ""; QTest::newRow("qualified right but not in version 0.0") << "import org.qtproject.Test 0.0 as T\n" "T.Test {}" << "" << "T.Test is not a type"; QTest::newRow("in version 1.1") << "import org.qtproject.Test 1.1\n" "Test {}" << "TestType" << ""; QTest::newRow("in version 1.3") << "import org.qtproject.Test 1.3\n" "Test {}" << "TestType" << ""; QTest::newRow("in version 1.5") << "import org.qtproject.Test 1.5\n" "Test {}" << "TestType" << ""; QTest::newRow("changed in version 1.8") << "import org.qtproject.Test 1.8\n" "Test {}" << "TestType2" << ""; QTest::newRow("in version 1.12") << "import org.qtproject.Test 1.12\n" "Test {}" << "TestType2" << ""; QTest::newRow("old in version 1.9") << "import org.qtproject.Test 1.9\n" "OldTest {}" << "TestType" << ""; QTest::newRow("old in version 1.11") << "import org.qtproject.Test 1.11\n" "OldTest {}" << "TestType" << ""; QTest::newRow("multiversion 1") << "import org.qtproject.Test 1.11\n" "import org.qtproject.Test 1.12\n" "Test {}" << (!qmlCheckTypes()?"TestType2":"") << (!qmlCheckTypes()?"":"Test is ambiguous. Found in org/qtproject/Test/ in version 1.12 and 1.11"); QTest::newRow("multiversion 2") << "import org.qtproject.Test 1.11\n" "import org.qtproject.Test 1.12\n" "OldTest {}" << (!qmlCheckTypes()?"TestType":"") << (!qmlCheckTypes()?"":"OldTest is ambiguous. Found in org/qtproject/Test/ in version 1.12 and 1.11"); QTest::newRow("qualified multiversion 3") << "import org.qtproject.Test 1.0 as T0\n" "import org.qtproject.Test 1.8 as T8\n" "T0.Test {}" << "TestType" << ""; QTest::newRow("qualified multiversion 4") << "import org.qtproject.Test 1.0 as T0\n" "import org.qtproject.Test 1.8 as T8\n" "T8.Test {}" << "TestType2" << ""; } void tst_qqmllanguage::importsBuiltin() { QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); testType(qml,type,error); } void tst_qqmllanguage::importsLocal_data() { QTest::addColumn("qml"); QTest::addColumn("type"); QTest::addColumn("error"); // import locals QTest::newRow("local import") << "import \"subdir\"\n" // QT-613 "Test {}" << "QQuickRectangle" << ""; QTest::newRow("local import second") << "import QtQuick 2.0\nimport \"subdir\"\n" "Test {}" << "QQuickRectangle" << ""; QTest::newRow("local import subsubdir") << "import QtQuick 2.0\nimport \"subdir/subsubdir\"\n" "SubTest {}" << "QQuickRectangle" << ""; QTest::newRow("local import QTBUG-7721 A") << "subdir.Test {}" // no longer allowed (QTBUG-7721) << "" << "subdir.Test - subdir is neither a type nor a namespace"; QTest::newRow("local import QTBUG-7721 B") << "import \"subdir\" as X\n" "X.subsubdir.SubTest {}" // no longer allowed (QTBUG-7721) << "" << "X.subsubdir.SubTest - subsubdir is not a type"; QTest::newRow("local import as") << "import \"subdir\" as T\n" "T.Test {}" << "QQuickRectangle" << ""; QTest::newRow("wrong local import as") << "import \"subdir\" as T\n" "Test {}" << "" << "Test is not a type"; QTest::newRow("library precedence over local import") << "import \"subdir\"\n" "import org.qtproject.Test 1.0\n" "Test {}" << (!qmlCheckTypes()?"TestType":"") << (!qmlCheckTypes()?"":"Test is ambiguous. Found in org/qtproject/Test/ and in subdir/"); if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) { // file URL doesn't work with qrc scheme QTest::newRow("file URL survives percent-encoding") << "import \"" + QUrl::fromLocalFile(QDir::currentPath() + "/{subdir}").toString() + "\"\n" "Test {}" << "QQuickRectangle" << ""; } } void tst_qqmllanguage::importsLocal() { QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); testType(qml,type,error); } void tst_qqmllanguage::basicRemote_data() { QTest::addColumn("url"); QTest::addColumn("type"); QTest::addColumn("error"); QString serverdir = "/qtest/qml/qqmllanguage/"; QTest::newRow("no need for qmldir") << QUrl(serverdir+"Test.qml") << "" << ""; QTest::newRow("absent qmldir") << QUrl(serverdir+"/noqmldir/Test.qml") << "" << ""; QTest::newRow("need qmldir") << QUrl(serverdir+"TestNamed.qml") << "" << ""; } void tst_qqmllanguage::basicRemote() { QFETCH(QUrl, url); QFETCH(QString, type); QFETCH(QString, error); ThreadedTestHTTPServer server(dataDirectory()); url = server.baseUrl().resolved(url); QQmlComponent component(&engine, url); QTRY_VERIFY(!component.isLoading()); if (error.isEmpty()) { if (component.isError()) qDebug() << component.errors(); QVERIFY(!component.isError()); } else { QVERIFY(component.isError()); } } void tst_qqmllanguage::importsRemote_data() { QTest::addColumn("qml"); QTest::addColumn("type"); QTest::addColumn("error"); QString serverdir = "{{ServerBaseUrl}}/qtest/qml/qqmllanguage"; QTest::newRow("remote import") << "import \""+serverdir+"\"\nTest {}" << "QQuickRectangle" << ""; QTest::newRow("remote import with subdir") << "import \""+serverdir+"\"\nTestSubDir {}" << "QQuickText" << ""; QTest::newRow("remote import with local") << "import \""+serverdir+"\"\nTestLocal {}" << "QQuickImage" << ""; QTest::newRow("remote import with qualifier") << "import \""+serverdir+"\" as NS\nNS.NamedLocal {}" << "QQuickImage" << ""; QTest::newRow("wrong remote import with undeclared local") << "import \""+serverdir+"\"\nWrongTestLocal {}" << "" << "WrongTestLocal is not a type"; QTest::newRow("wrong remote import of internal local") << "import \""+serverdir+"\"\nLocalInternal {}" << "" << "LocalInternal is not a type"; QTest::newRow("wrong remote import of undeclared local") << "import \""+serverdir+"\"\nUndeclaredLocal {}" << "" << "UndeclaredLocal is not a type"; } void tst_qqmllanguage::importsRemote() { QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); ThreadedTestHTTPServer server(dataDirectory()); qml.replace(QStringLiteral("{{ServerBaseUrl}}"), server.baseUrl().toString()); testType(qml,type,error); } void tst_qqmllanguage::importsInstalled_data() { // QT-610 QTest::addColumn("qml"); QTest::addColumn("type"); QTest::addColumn("error"); // import installed QTest::newRow("installed import 0") << "import org.qtproject.installedtest0 0.0\n" "InstalledTestTP {}" << "QQuickRectangle" << ""; QTest::newRow("installed import 0 as TP") << "import org.qtproject.installedtest0 0.0 as TP\n" "TP.InstalledTestTP {}" << "QQuickRectangle" << ""; QTest::newRow("installed import 1") << "import org.qtproject.installedtest 1.0\n" "InstalledTest {}" << "QQuickRectangle" << ""; QTest::newRow("installed import 2") << "import org.qtproject.installedtest 1.3\n" "InstalledTest {}" << "QQuickRectangle" << ""; QTest::newRow("installed import 3") << "import org.qtproject.installedtest 1.4\n" "InstalledTest {}" << "QQuickText" << ""; QTest::newRow("installed import minor version not available") // QTBUG-11936 << "import org.qtproject.installedtest 0.1\n" "InstalledTest {}" << "" << "module \"org.qtproject.installedtest\" version 0.1 is not installed"; QTest::newRow("installed import minor version not available") // QTBUG-9627 << "import org.qtproject.installedtest 1.10\n" "InstalledTest {}" << "" << "module \"org.qtproject.installedtest\" version 1.10 is not installed"; QTest::newRow("installed import major version not available") // QTBUG-9627 << "import org.qtproject.installedtest 9.0\n" "InstalledTest {}" << "" << "module \"org.qtproject.installedtest\" version 9.0 is not installed"; QTest::newRow("installed import visibility") // QT-614 << "import org.qtproject.installedtest 1.4\n" "PrivateType {}" << "" << "PrivateType is not a type"; QTest::newRow("installed import version QML clash") << "import org.qtproject.installedtest1 1.0\n" "Test {}" << "" << "\"Test\" version 1.0 is defined more than once in module \"org.qtproject.installedtest1\""; QTest::newRow("installed import version JS clash") << "import org.qtproject.installedtest2 1.0\n" "Test {}" << "" << "\"Test\" version 1.0 is defined more than once in module \"org.qtproject.installedtest2\""; } void tst_qqmllanguage::importsInstalled() { QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); testType(qml,type,error); } void tst_qqmllanguage::importsInstalledRemote_data() { // Repeat the tests for local installed data importsInstalled_data(); } void tst_qqmllanguage::importsInstalledRemote() { QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); ThreadedTestHTTPServer server(dataDirectory()); QString serverdir = server.urlString("/lib/"); engine.setImportPathList(QStringList(defaultImportPathList) << serverdir); testType(qml,type,error); engine.setImportPathList(defaultImportPathList); } void tst_qqmllanguage::importsPath_data() { QTest::addColumn("importPath"); QTest::addColumn("qml"); QTest::addColumn("value"); QTest::newRow("local takes priority normal") << (QStringList() << testFile("lib") << "{{ServerBaseUrl}}/lib2/") << "import testModule 1.0\n" "Test {}" << "foo"; QTest::newRow("local takes priority reversed") << (QStringList() << "{{ServerBaseUrl}}/lib/" << testFile("lib2")) << "import testModule 1.0\n" "Test {}" << "bar"; QTest::newRow("earlier takes priority 1") << (QStringList() << "{{ServerBaseUrl}}/lib/" << "{{ServerBaseUrl}}/lib2/") << "import testModule 1.0\n" "Test {}" << "foo"; QTest::newRow("earlier takes priority 2") << (QStringList() << "{{ServerBaseUrl}}/lib2/" << "{{ServerBaseUrl}}/lib/") << "import testModule 1.0\n" "Test {}" << "bar"; QTest::newRow("major version takes priority over unversioned") << (QStringList() << "{{ServerBaseUrl}}/lib/" << "{{ServerBaseUrl}}/lib3/") << "import testModule 1.0\n" "Test {}" << "baz"; QTest::newRow("major version takes priority over minor") << (QStringList() << "{{ServerBaseUrl}}/lib4/" << "{{ServerBaseUrl}}/lib3/") << "import testModule 1.0\n" "Test {}" << "baz"; QTest::newRow("minor version takes priority over unversioned") << (QStringList() << "{{ServerBaseUrl}}/lib/" << "{{ServerBaseUrl}}/lib4/") << "import testModule 1.0\n" "Test {}" << "qux"; } void tst_qqmllanguage::importsPath() { QFETCH(QStringList, importPath); QFETCH(QString, qml); QFETCH(QString, value); ThreadedTestHTTPServer server(dataDirectory()); for (int i = 0; i < importPath.count(); ++i) importPath[i].replace(QStringLiteral("{{ServerBaseUrl}}"), server.baseUrl().toString()); engine.setImportPathList(QStringList(defaultImportPathList) << importPath); QQmlComponent component(&engine); component.setData(qml.toUtf8(), testFileUrl("empty.qml")); QTRY_VERIFY(component.isReady()); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toString(), value); engine.setImportPathList(defaultImportPathList); } void tst_qqmllanguage::importsOrder_data() { QTest::addColumn("qml"); QTest::addColumn("type"); QTest::addColumn("error"); QTest::addColumn("partialMatch"); QTest::newRow("double import") << "import org.qtproject.installedtest 1.4\n" "import org.qtproject.installedtest 1.4\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickText":"") << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.4 and 1.4") << false; QTest::newRow("installed import overrides 1") << "import org.qtproject.installedtest 1.0\n" "import org.qtproject.installedtest 1.4\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickText":"") << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.4 and 1.0") << false; QTest::newRow("installed import overrides 2") << "import org.qtproject.installedtest 1.4\n" "import org.qtproject.installedtest 1.0\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickRectangle":"") << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.0 and 1.4") << false; QTest::newRow("installed import re-overrides 1") << "import org.qtproject.installedtest 1.4\n" "import org.qtproject.installedtest 1.0\n" "import org.qtproject.installedtest 1.4\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickText":"") << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.4 and 1.0") << false; QTest::newRow("installed import re-overrides 2") << "import org.qtproject.installedtest 1.4\n" "import org.qtproject.installedtest 1.0\n" "import org.qtproject.installedtest 1.4\n" "import org.qtproject.installedtest 1.0\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickRectangle":"") << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.0 and 1.4") << false; QTest::newRow("installed import versus builtin 1") << "import org.qtproject.installedtest 1.5\n" "import QtQuick 2.0\n" "Rectangle {}" << (!qmlCheckTypes()?"QQuickRectangle":"") << (!qmlCheckTypes()?"":"Rectangle is ambiguous. Found in file://") << true; QTest::newRow("installed import versus builtin 2") << "import QtQuick 2.0\n" "import org.qtproject.installedtest 1.5\n" "Rectangle {}" << (!qmlCheckTypes()?"QQuickText":"") << (!qmlCheckTypes()?"":"Rectangle is ambiguous. Found in lib/org/qtproject/installedtest/ and in file://") << true; QTest::newRow("namespaces cannot be overridden by types 1") << "import QtQuick 2.0 as Rectangle\n" "import org.qtproject.installedtest 1.5\n" "Rectangle {}" << "" << "Namespace Rectangle cannot be used as a type" << false; QTest::newRow("namespaces cannot be overridden by types 2") << "import QtQuick 2.0 as Rectangle\n" "import org.qtproject.installedtest 1.5\n" "Rectangle.Image {}" << "QQuickImage" << "" << false; QTest::newRow("local last 1") << "LocalLast {}" << "QQuickText" << "" << false; QTest::newRow("local last 2") << "import org.qtproject.installedtest 1.0\n" "LocalLast {}" << (!qmlCheckTypes()?"QQuickRectangle":"")// i.e. from org.qtproject.installedtest, not data/LocalLast.qml << (!qmlCheckTypes()?"":"LocalLast is ambiguous. Found in lib/org/qtproject/installedtest/ and in ") << false; QTest::newRow("local last 3") << //Forces it to load the local qmldir to resolve types, but they shouldn't override anything "import org.qtproject.installedtest 1.0\n" "LocalLast {LocalLast2{}}" << (!qmlCheckTypes()?"QQuickRectangle":"")// i.e. from org.qtproject.installedtest, not data/LocalLast.qml << (!qmlCheckTypes()?"":"LocalLast is ambiguous. Found in lib/org/qtproject/installedtest/ and in ") << false; } void tst_qqmllanguage::importsOrder() { QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); QFETCH(bool, partialMatch); testType(qml,type,error,partialMatch); } void tst_qqmllanguage::importIncorrectCase() { if (engine.importPathList() == defaultImportPathList) engine.addImportPath(testFile("lib")); // Load "importIncorrectCase.qml" using wrong case QQmlComponent component(&engine, testFileUrl("ImportIncorrectCase.qml")); QList errors = component.errors(); QCOMPARE(errors.count(), 1); const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ? QStringLiteral("No such file or directory") : QStringLiteral("File name case mismatch"); QCOMPARE(errors.at(0).description(), expectedError); engine.setImportPathList(defaultImportPathList); } void tst_qqmllanguage::importJs_data() { QTest::addColumn("file"); QTest::addColumn("errorFile"); QTest::addColumn("performTest"); QTest::newRow("defaultVersion") << "importJs.1.qml" << "importJs.1.errors.txt" << true; QTest::newRow("specifiedVersion") << "importJs.2.qml" << "importJs.2.errors.txt" << true; QTest::newRow("excludeExcessiveVersion") << "importJs.3.qml" << "importJs.3.errors.txt" << false; QTest::newRow("includeAppropriateVersion") << "importJs.4.qml" << "importJs.4.errors.txt" << true; QTest::newRow("noDefaultVersion") << "importJs.5.qml" << "importJs.5.errors.txt" << false; QTest::newRow("repeatImportFails") << "importJs.6.qml" << "importJs.6.errors.txt" << false; QTest::newRow("multipleVersionImportFails") << "importJs.7.qml" << "importJs.7.errors.txt" << false; QTest::newRow("namespacedImport") << "importJs.8.qml" << "importJs.8.errors.txt" << true; QTest::newRow("namespacedVersionedImport") << "importJs.9.qml" << "importJs.9.errors.txt" << true; QTest::newRow("namespacedRepeatImport") << "importJs.10.qml" << "importJs.10.errors.txt" << true; QTest::newRow("emptyScript") << "importJs.11.qml" << "importJs.11.errors.txt" << true; } void tst_qqmllanguage::importJs() { QFETCH(QString, file); QFETCH(QString, errorFile); QFETCH(bool, performTest); engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); QQmlComponent component(&engine, testFileUrl(file)); { DETERMINE_ERRORS(errorFile,expected,actual); QCOMPARE(expected.size(), actual.size()); for (int i = 0; i < expected.size(); ++i) { const int compareLen = qMin(expected.at(i).length(), actual.at(i).length()); QCOMPARE(expected.at(i).left(compareLen), actual.at(i).left(compareLen)); } } if (performTest) { QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(),true); } engine.setImportPathList(defaultImportPathList); } void tst_qqmllanguage::importJsModule_data() { QTest::addColumn("file"); QTest::newRow("plainImport") << "importJsModule.1.qml"; QTest::newRow("ImportQmlStyle") << "importJsModule.2.qml"; QTest::newRow("plainImportWithCycle") << "importJsModule.3.qml"; } void tst_qqmllanguage::importJsModule() { QFETCH(QString, file); engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); auto importPathGuard = qScopeGuard([this]{ engine.setImportPathList(defaultImportPathList); }); QQmlComponent component(&engine, testFileUrl(file)); QVERIFY2(!component.isError(), qPrintable(component.errorString())); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(),true); } void tst_qqmllanguage::explicitSelfImport() { engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); QQmlComponent component(&engine, testFileUrl("mixedModuleWithSelfImport.qml")); QVERIFY(component.errors().count() == 0); engine.setImportPathList(defaultImportPathList); } void tst_qqmllanguage::importInternalType() { QQmlEngine engine; engine.addImportPath(dataDirectory()); { QQmlComponent component(&engine); component.setData("import modulewithinternaltypes 1.0\nPublicType{}", QUrl()); VERIFY_ERRORS(0); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QVERIFY(obj->property("myInternalType").value() != 0); } { QQmlComponent component(&engine); component.setData("import modulewithinternaltypes 1.0\nPublicTypeWithExplicitImport{}", QUrl()); VERIFY_ERRORS(0); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QVERIFY(obj->property("myInternalType").value() != 0); } } void tst_qqmllanguage::qmlAttachedPropertiesObjectMethod() { QObject object; QCOMPARE(qmlAttachedPropertiesObject(&object, false), (QObject *)nullptr); QVERIFY(qmlAttachedPropertiesObject(&object, true)); { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.1.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(qmlAttachedPropertiesObject(object.data(), false), (QObject *)nullptr); QVERIFY(qmlAttachedPropertiesObject(object.data(), true) != nullptr); } { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.2.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(qmlAttachedPropertiesObject(object.data(), false) != nullptr); QVERIFY(qmlAttachedPropertiesObject(object.data(), true) != nullptr); } } void tst_qqmllanguage::crash1() { QQmlComponent component(&engine); component.setData("import QtQuick 2.0\nComponent {}", QUrl()); } void tst_qqmllanguage::crash2() { QQmlComponent component(&engine, testFileUrl("crash2.qml")); } // QTBUG-8676 void tst_qqmllanguage::customOnProperty() { QQmlComponent component(&engine, testFileUrl("customOnProperty.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("on").toInt(), 10); } // QTBUG-12601 void tst_qqmllanguage::variantNotify() { QQmlComponent component(&engine, testFileUrl("variantNotify.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QEXPECT_FAIL("", "var properties always trigger notify", Continue); QCOMPARE(object->property("notifyCount").toInt(), 1); } void tst_qqmllanguage::revisions() { { QQmlComponent component(&engine, testFileUrl("revisions11.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->prop2(), 10.0); } { QQmlEngine myEngine; QQmlComponent component(&myEngine, testFileUrl("revisionssub11.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->prop1(), 10.0); QCOMPARE(object->prop2(), 10.0); QCOMPARE(object->prop3(), 10.0); QCOMPARE(object->prop4(), 10.0); } { QQmlComponent component(&engine, testFileUrl("versionedbase.qml")); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->prop1(), 10.0); QCOMPARE(object->prop2(), 10.0); } } void tst_qqmllanguage::revisionOverloads() { { QQmlComponent component(&engine, testFileUrl("allowedRevisionOverloads.qml")); VERIFY_ERRORS(0); } { QQmlComponent component(&engine, testFileUrl("disallowedRevisionOverloads.qml")); QEXPECT_FAIL("", "QTBUG-13849", Abort); QVERIFY(0); VERIFY_ERRORS("disallowedRevisionOverloads.errors.txt"); } } void tst_qqmllanguage::subclassedUncreateableRevision_data() { QTest::addColumn("version"); QTest::addColumn("prop"); QTest::addColumn("shouldWork"); QTest::newRow("prop1 exists in 1.0") << "1.0" << "prop1" << true; QTest::newRow("prop2 does not exist in 1.0") << "1.0" << "prop2" << false; QTest::newRow("prop3 does not exist in 1.0") << "1.0" << "prop3" << false; QTest::newRow("prop1 exists in 1.1") << "1.1" << "prop1" << true; QTest::newRow("prop2 works because it's re-declared in Derived") << "1.1" << "prop2" << true; QTest::newRow("prop3 only works if the Base REVISION 1 is picked up") << "1.1" << "prop3" << true; } void tst_qqmllanguage::subclassedUncreateableRevision() { QFETCH(QString, version); QFETCH(QString, prop); QFETCH(bool, shouldWork); { QQmlEngine engine; QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyUncreateableBaseClass {}").arg(version); QQmlComponent c(&engine); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); QScopedPointer obj(c.create()); QCOMPARE(obj.data(), static_cast(nullptr)); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Cannot create MyUncreateableBaseClass")); } QQmlEngine engine; QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyCreateableDerivedClass {\n%3: true\n}").arg(version).arg(prop); QQmlComponent c(&engine); if (!shouldWork) QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); QScopedPointer obj(c.create()); if (!shouldWork) { QCOMPARE(obj.data(), static_cast(nullptr)); return; } QVERIFY(obj); MyUncreateableBaseClass *base = qobject_cast(obj.data()); QVERIFY(base); QCOMPARE(base->property(prop.toLatin1()).toBool(), true); } void tst_qqmllanguage::subclassedExtendedUncreateableRevision_data() { QTest::addColumn("version"); QTest::addColumn("prop"); QTest::addColumn("shouldWork"); QTest::newRow("prop1 exists in 1.0") << "1.0" << "prop1" << true; QTest::newRow("prop2 does not exist in 1.0") << "1.0" << "prop2" << false; QTest::newRow("prop3 does not exist in 1.0") << "1.0" << "prop3" << false; QTest::newRow("prop4 exists in 1.0") << "1.0" << "prop4" << true; QTest::newRow("prop5 exists in 1.0") << "1.0" << "prop5" << true; QTest::newRow("prop1 exists in 1.1") << "1.1" << "prop1" << true; QTest::newRow("prop2 exists in 1.1") << "1.1" << "prop2" << true; QTest::newRow("prop3 exists in 1.1") << "1.1" << "prop3" << true; QTest::newRow("prop4 exists in 1.1") << "1.1" << "prop4" << true; QTest::newRow("prop5 exists in 1.1") << "1.1" << "prop5" << true; } void tst_qqmllanguage::subclassedExtendedUncreateableRevision() { QFETCH(QString, version); QFETCH(QString, prop); QFETCH(bool, shouldWork); { QQmlEngine engine; QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyExtendedUncreateableBaseClass {}").arg(version); QQmlComponent c(&engine); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); QScopedPointer obj(c.create()); QCOMPARE(obj.data(), static_cast(nullptr)); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Cannot create MyExtendedUncreateableBaseClass")); } QQmlEngine engine; QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyExtendedCreateableDerivedClass {\n%3: true\n}").arg(version).arg(prop); QQmlComponent c(&engine); if (!shouldWork) QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); QScopedPointer obj(c.create()); if (!shouldWork) { QCOMPARE(obj.data(), static_cast(nullptr)); return; } QVERIFY(obj); MyExtendedUncreateableBaseClass *base = qobject_cast(obj.data()); QVERIFY(base); QCOMPARE(base->property(prop.toLatin1()).toBool(), true); } void tst_qqmllanguage::uncreatableTypesAsProperties() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("uncreatableTypeAsProperty.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); } void tst_qqmllanguage::initTestCase() { QQmlDataTest::initTestCase(); if (dataDirectoryUrl().scheme() == QLatin1String("qrc")) engine.addImportPath(dataDirectory()); else QVERIFY2(QDir::setCurrent(dataDirectory()), qPrintable("Could not chdir to " + dataDirectory())); defaultImportPathList = engine.importPathList(); QQmlMetaType::registerCustomStringConverter(qMetaTypeId(), myCustomVariantTypeConverter); registerTypes(); // Registered here because it uses testFileUrl qmlRegisterType(testFileUrl("CompositeType.qml"), "Test", 1, 0, "RegisteredCompositeType"); qmlRegisterType(testFileUrl("CompositeType.DoesNotExist.qml"), "Test", 1, 0, "RegisteredCompositeType2"); qmlRegisterType(testFileUrl("invalidRoot.1.qml"), "Test", 1, 0, "RegisteredCompositeType3"); qmlRegisterType(testFileUrl("CompositeTypeWithEnum.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithEnum"); qmlRegisterType(testFileUrl("CompositeTypeWithAttachedProperty.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithAttachedProperty"); // Registering the TestType class in other modules should have no adverse effects qmlRegisterType("org.qtproject.TestPre", 1, 0, "Test"); qmlRegisterType("org.qtproject.Test", 0, 0, "TestTP"); qmlRegisterType("org.qtproject.Test", 1, 0, "Test"); qmlRegisterType("org.qtproject.Test", 1, 5, "Test"); qmlRegisterType("org.qtproject.Test", 1, 8, "Test"); qmlRegisterType("org.qtproject.Test", 1, 9, "OldTest"); qmlRegisterType("org.qtproject.Test", 1, 12, "Test"); // Registering the TestType class in other modules should have no adverse effects qmlRegisterType("org.qtproject.TestPost", 1, 0, "Test"); // Create locale-specific file // For POSIX, this will just be data/I18nType.qml, since POSIX is 7-bit // For iso8859-1 locale, this will just be data/I18nType?????.qml where ????? is 5 8-bit characters // For utf-8 locale, this will be data/I18nType??????????.qml where ?????????? is 5 8-bit characters, UTF-8 encoded if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) { QFile in(testFileUrl(QLatin1String("I18nType30.qml")).toLocalFile()); QVERIFY2(in.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(in.fileName(), in.errorString()))); QFile out(testFileUrl(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")).toLocalFile()); QVERIFY2(out.open(QIODevice::WriteOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(out.fileName(), out.errorString()))); out.write(in.readAll()); } // Register a Composite Singleton. qmlRegisterSingletonType(testFileUrl("singleton/RegisteredCompositeSingletonType.qml"), "org.qtproject.Test", 1, 0, "RegisteredSingleton"); } void tst_qqmllanguage::aliasPropertyChangeSignals() { { QQmlComponent component(&engine, testFileUrl("aliasPropertyChangeSignals.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); } // QTCREATORBUG-2769 { QQmlComponent component(&engine, testFileUrl("aliasPropertyChangeSignals.2.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); } } // Tests property initializers void tst_qqmllanguage::propertyInit() { { QQmlComponent component(&engine, testFileUrl("propertyInit.1.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 1); } { QQmlComponent component(&engine, testFileUrl("propertyInit.2.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 123); } } // Test that registration order doesn't break type availability // QTBUG-16878 void tst_qqmllanguage::registrationOrder() { QQmlComponent component(&engine, testFileUrl("registrationOrder.qml")); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->metaObject(), &MyVersion2Class::staticMetaObject); } void tst_qqmllanguage::readonly() { QQmlComponent component(&engine, testFileUrl("readonly.qml")); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 18); QCOMPARE(o->property("test3").toInt(), 13); o->setProperty("testData", 13); QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 22); QCOMPARE(o->property("test3").toInt(), 13); o->setProperty("testData2", 2); QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 22); QCOMPARE(o->property("test3").toInt(), 2); o->setProperty("test1", 11); o->setProperty("test2", 11); o->setProperty("test3", 11); QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 22); QCOMPARE(o->property("test3").toInt(), 2); } void tst_qqmllanguage::readonlyObjectProperties() { QQmlComponent component(&engine, testFileUrl("readonlyObjectProperty.qml")); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QQmlProperty prop(o.data(), QStringLiteral("subObject"), &engine); QVERIFY(!prop.isWritable()); QVERIFY(!prop.write(QVariant::fromValue(o.data()))); QObject *subObject = qvariant_cast(prop.read()); QVERIFY(subObject); QCOMPARE(subObject->property("readWrite").toInt(), int(42)); subObject->setProperty("readWrite", QVariant::fromValue(int(100))); QCOMPARE(subObject->property("readWrite").toInt(), int(100)); } void tst_qqmllanguage::receivers() { QQmlComponent component(&engine, testFileUrl("receivers.qml")); QScopedPointer o(qobject_cast(component.create())); QVERIFY(o != nullptr); QCOMPARE(o->mySignalCount(), 1); QCOMPARE(o->propChangedCount(), 2); QCOMPARE(o->myUnconnectedSignalCount(), 0); QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::mySignal))); QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::propChanged))); QVERIFY(!o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::myUnconnectedSignal))); } void tst_qqmllanguage::registeredCompositeType() { QQmlComponent component(&engine, testFileUrl("registeredCompositeType.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); } // QTBUG-43582 void tst_qqmllanguage::registeredCompositeTypeWithEnum() { QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithEnum.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("enumValue0").toInt(), static_cast(MyCompositeBaseType::EnumValue0)); QCOMPARE(o->property("enumValue42").toInt(), static_cast(MyCompositeBaseType::EnumValue42)); QCOMPARE(o->property("enumValue15").toInt(), static_cast(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15)); } // QTBUG-43581 void tst_qqmllanguage::registeredCompositeTypeWithAttachedProperty() { QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithAttachedProperty.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("attachedProperty").toString(), QStringLiteral("test")); } // QTBUG-18268 void tst_qqmllanguage::remoteLoadCrash() { ThreadedTestHTTPServer server(dataDirectory()); QQmlComponent component(&engine); component.setData("import QtQuick 2.0; Text {}", server.url("/remoteLoadCrash.qml")); while (component.isLoading()) QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50); QScopedPointer o(component.create()); } void tst_qqmllanguage::signalWithDefaultArg() { QQmlComponent component(&engine, testFileUrl("signalWithDefaultArg.qml")); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->property("signalCount").toInt(), 0); QCOMPARE(object->property("signalArg").toInt(), 0); emit object->signalWithDefaultArg(); QCOMPARE(object-> property("signalCount").toInt(), 1); QCOMPARE(object->property("signalArg").toInt(), 5); emit object->signalWithDefaultArg(15); QCOMPARE(object->property("signalCount").toInt(), 2); QCOMPARE(object->property("signalArg").toInt(), 15); QMetaObject::invokeMethod(object.data(), "emitNoArgSignal"); QCOMPARE(object->property("signalCount").toInt(), 3); QCOMPARE(object->property("signalArg").toInt(), 5); QMetaObject::invokeMethod(object.data(), "emitArgSignal"); QCOMPARE(object->property("signalCount").toInt(), 4); QCOMPARE(object->property("signalArg").toInt(), 22); } void tst_qqmllanguage::signalParameterTypes() { // bound signal handlers { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.1.qml")); QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); } // dynamic signal connections { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.2.qml")); QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); } // dynamic signal connections { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.3.qml")); QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); } } void tst_qqmllanguage::functionParameterTypes() { QQmlComponent component(&engine, testFileUrl("functionParameterTypes.qml")); QScopedPointer obj(component.create()); QVERIFY2(!obj.isNull(), qPrintable(component.errorString())); const QMetaObject *metaObject = obj->metaObject(); { QMetaMethod slot = metaObject->method(metaObject->indexOfSlot("returnItem()")); QVERIFY(slot.isValid()); QCOMPARE(slot.returnType(), QMetaType::QObjectStar); QObject *returnedPtr = nullptr; slot.invoke(obj.data(), Qt::DirectConnection, Q_RETURN_ARG(QObject*, returnedPtr)); QCOMPARE(returnedPtr, obj.data()); } { QMetaMethod slot = metaObject->method(metaObject->indexOfSlot("takeString(QString)")); QVERIFY(slot.isValid()); QCOMPARE(slot.parameterCount(), 1); QCOMPARE(slot.parameterType(0), int(QMetaType::QString)); } } // QTBUG-20639 void tst_qqmllanguage::globalEnums() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); QQmlComponent component(&engine, testFileUrl("globalEnums.qml")); QScopedPointer o(component.create()); QVERIFY(o != nullptr); MyEnum1Class *enum1Class = o->findChild(QString::fromLatin1("enum1Class")); QVERIFY(enum1Class != nullptr); QVERIFY(enum1Class->getValue() == -1); MyEnumDerivedClass *enum2Class = o->findChild(QString::fromLatin1("enumDerivedClass")); QVERIFY(enum2Class != nullptr); QVERIFY(enum2Class->getValueA() == -1); QVERIFY(enum2Class->getValueB() == -1); QVERIFY(enum2Class->getValueC() == 0); QVERIFY(enum2Class->getValueD() == 0); QVERIFY(enum2Class->getValueE() == -1); QVERIFY(enum2Class->getValueE2() == -1); QVERIFY(enum2Class->property("aValue") == 0); QVERIFY(enum2Class->property("bValue") == 0); QVERIFY(enum2Class->property("cValue") == 0); QVERIFY(enum2Class->property("dValue") == 0); QVERIFY(enum2Class->property("eValue") == 0); QVERIFY(enum2Class->property("e2Value") == 0); QSignalSpy signalA(enum2Class, SIGNAL(valueAChanged(MyEnum1Class::EnumA))); QSignalSpy signalB(enum2Class, SIGNAL(valueBChanged(MyEnum2Class::EnumB))); QMetaObject::invokeMethod(o.data(), "setEnumValues"); QVERIFY(enum1Class->getValue() == MyEnum1Class::A_13); QVERIFY(enum2Class->getValueA() == MyEnum1Class::A_11); QVERIFY(enum2Class->getValueB() == MyEnum2Class::B_37); QVERIFY(enum2Class->getValueC() == Qt::RichText); QVERIFY(enum2Class->getValueD() == Qt::ElideMiddle); QVERIFY(enum2Class->getValueE() == MyEnum2Class::E_14); QVERIFY(enum2Class->getValueE2() == MyEnum2Class::E_76); QVERIFY(signalA.count() == 1); QVERIFY(signalB.count() == 1); QVERIFY(enum2Class->property("aValue") == MyEnum1Class::A_11); QVERIFY(enum2Class->property("bValue") == 37); QVERIFY(enum2Class->property("cValue") == 1); QVERIFY(enum2Class->property("dValue") == 2); QVERIFY(enum2Class->property("eValue") == 14); QVERIFY(enum2Class->property("e2Value") == 76); } void tst_qqmllanguage::lowercaseEnumRuntime_data() { QTest::addColumn("file"); QTest::newRow("enum from normal type") << "lowercaseEnumRuntime.1.qml"; QTest::newRow("enum from singleton type") << "lowercaseEnumRuntime.2.qml"; } void tst_qqmllanguage::lowercaseEnumRuntime() { QFETCH(QString, file); QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); delete component.create(); } void tst_qqmllanguage::lowercaseEnumCompileTime_data() { QTest::addColumn("file"); QTest::newRow("assignment to int property") << "lowercaseEnumCompileTime.1.qml"; QTest::newRow("assignment to enum property") << "lowercaseEnumCompileTime.2.qml"; } void tst_qqmllanguage::lowercaseEnumCompileTime() { QFETCH(QString, file); QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); delete component.create(); } void tst_qqmllanguage::scopedEnum() { QQmlComponent component(&engine, testFileUrl("scopedEnum.qml")); QScopedPointer o(qobject_cast(component.create())); QVERIFY(o != nullptr); QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal1); QCOMPARE(o->intProperty(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); QCOMPARE(o->property("listValue").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal3); QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal1); QMetaObject::invokeMethod(o.data(), "assignNewValue"); QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal2); QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); } void tst_qqmllanguage::scopedEnumsWithNameClash() { auto typeId = qmlRegisterUncreatableMetaObject( ScopedEnumsWithNameClash::staticMetaObject, "ScopedEnumsWithNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); auto registryGuard = qScopeGuard([typeId]() { QQmlMetaType::unregisterType(typeId); }); QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("scopedEnumsWithNameClash.qml")); QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal1"); QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal2"); QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal3"); QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); } void tst_qqmllanguage::scopedEnumsWithResolvedNameClash() { auto typeId = qmlRegisterUncreatableMetaObject( ScopedEnumsWithResolvedNameClash::staticMetaObject, "ScopedEnumsWithResolvedNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); auto registryGuard = qScopeGuard([typeId]() { QQmlMetaType::unregisterType(typeId); }); QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("scopedEnumsWithResolvedNameClash.qml")); QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); } class ObjectA : public QObject { Q_OBJECT public: enum TestEnum { Default = 42, TestValue1, TestValue2 }; Q_ENUM(TestEnum) ObjectA() = default; }; class ObjectB : public QObject { Q_OBJECT Q_PROPERTY(ObjectA::TestEnum testEnum READ testEnum WRITE setTestEnum NOTIFY testEnumChanged) public: ObjectB() = default; ObjectA::TestEnum testEnum() const {return m_testEnum;} public slots: void setTestEnum(ObjectA::TestEnum testEnum) {auto old = m_testEnum; m_testEnum = testEnum; if (old != m_testEnum) testEnumChanged(m_testEnum);} signals: void testEnumChanged(ObjectA::TestEnum testEnum); private: ObjectA::TestEnum m_testEnum = ObjectA::Default; }; class ObjectC : public ObjectB { Q_OBJECT public: ObjectC() = default; }; void tst_qqmllanguage::enumNoScopeLeak() { qmlRegisterType("test", 1, 0, "ObjectA"); qmlRegisterType("test", 1, 0, "ObjectB"); qmlRegisterType("test", 1, 0, "ObjectC"); QQmlEngine engine; auto url = testFileUrl("enumScopeLeak.qml"); QQmlComponent component(&engine, url); const auto msg = url.toString() + ":5:5: Unable to assign [undefined] to ObjectA::TestEnum"; QTest::ignoreMessage(QtMsgType::QtWarningMsg, msg.toUtf8().constData()); QScopedPointer root(component.create()); QVERIFY(root); QCOMPARE(root->property("testEnum").toInt(), ObjectA::Default); } void tst_qqmllanguage::qmlEnums() { QQmlEngine engine; engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); { QQmlComponent component(&engine, testFileUrl("TypeWithEnum.qml")); QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("enumValue").toInt(), 1); QCOMPARE(o->property("enumValue2").toInt(), 2); QCOMPARE(o->property("scopedEnumValue").toInt(), 1); QCOMPARE(o->property("otherEnumValue1").toInt(), 24); QCOMPARE(o->property("otherEnumValue2").toInt(), 25); QCOMPARE(o->property("otherEnumValue3").toInt(), 24); QCOMPARE(o->property("otherEnumValue4").toInt(), 25); QCOMPARE(o->property("otherEnumValue5").toInt(), 1); } { QQmlComponent component(&engine, testFileUrl("usingTypeWithEnum.qml")); QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("enumValue").toInt(), 1); QCOMPARE(o->property("enumValue2").toInt(), 0); QCOMPARE(o->property("scopedEnumValue").toInt(), 2); QCOMPARE(o->property("enumValueFromSingleton").toInt(), 42); // while this next test verifies current duplication behavior, I'm not sure it should be codified QCOMPARE(o->property("duplicatedEnumValueFromSingleton").toInt(), 2); QCOMPARE(o->property("scopedEnumValueFromSingleton1").toInt(), 43); QCOMPARE(o->property("scopedEnumValueFromSingleton2").toInt(), 2); QCOMPARE(o->property("scopedEnumValueFromSingleton3").toInt(), 2); } } void tst_qqmllanguage::literals_data() { QTest::addColumn("property"); QTest::addColumn("value"); QTest::newRow("hex") << "n1" << QVariant(0xfe32); // Octal integer literals are deprecated // QTest::newRow("octal") << "n2" << QVariant(015); QTest::newRow("fp1") << "n3" << QVariant(-4.2E11); QTest::newRow("fp2") << "n4" << QVariant(.1e9); QTest::newRow("fp3") << "n5" << QVariant(3e-12); QTest::newRow("fp4") << "n6" << QVariant(3e+12); QTest::newRow("fp5") << "n7" << QVariant(0.1e9); QTest::newRow("large-int1") << "n8" << QVariant((double) 1152921504606846976); QTest::newRow("large-int2") << "n9" << QVariant(100000000000000000000.); QTest::newRow("special1") << "c1" << QVariant(QString("\b")); QTest::newRow("special2") << "c2" << QVariant(QString("\f")); QTest::newRow("special3") << "c3" << QVariant(QString("\n")); QTest::newRow("special4") << "c4" << QVariant(QString("\r")); QTest::newRow("special5") << "c5" << QVariant(QString("\t")); QTest::newRow("special6") << "c6" << QVariant(QString("\v")); QTest::newRow("special7") << "c7" << QVariant(QString("\'")); QTest::newRow("special8") << "c8" << QVariant(QString("\"")); QTest::newRow("special9") << "c9" << QVariant(QString("\\")); // We don't handle octal escape sequences QTest::newRow("special10") << "c10" << QVariant(QString(1, QChar(0xa9))); QTest::newRow("special11") << "c11" << QVariant(QString(1, QChar(0x00A9))); } void tst_qqmllanguage::literals() { QFETCH(QString, property); QFETCH(QVariant, value); QQmlComponent component(&engine, testFile("literals.qml")); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property(property.toLatin1()), value); } void tst_qqmllanguage::objectDeletionNotify_data() { QTest::addColumn("file"); QTest::newRow("property QtObject") << "objectDeletionNotify.1.qml"; QTest::newRow("property variant") << "objectDeletionNotify.2.qml"; QTest::newRow("property var") << "objectDeletionNotify.3.qml"; QTest::newRow("property var guard removed") << "objectDeletionNotify.4.qml"; } void tst_qqmllanguage::objectDeletionNotify() { QFETCH(QString, file); QQmlComponent component(&engine, testFile(file)); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); QMetaObject::invokeMethod(object.data(), "destroyObject"); // Process the deletion event QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QCOMPARE(object->property("success").toBool(), true); } void tst_qqmllanguage::scopedProperties() { QQmlComponent component(&engine, testFileUrl("scopedProperties.qml")); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QVERIFY(o->property("success").toBool()); } void tst_qqmllanguage::deepProperty() { QQmlComponent component(&engine, testFileUrl("deepProperty.qml")); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QFont font = qvariant_cast(qvariant_cast(o->property("someObject"))->property("font")); QCOMPARE(font.family(), QStringLiteral("test")); } // Tests that the implicit import has lowest precedence, in the case where // there are conflicting types and types only found in the local import. // Tests that just check one (or the root) type are in ::importsOrder void tst_qqmllanguage::implicitImportsLast() { if (qmlCheckTypes()) QSKIP("This test is about maintaining the same choice when type is ambiguous."); if (engine.importPathList() == defaultImportPathList) engine.addImportPath(testFile("lib")); QQmlComponent component(&engine, testFileUrl("localOrderTest.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(QString(object->metaObject()->superClass()->superClass()->className()) .startsWith(QLatin1String("QQuickMouseArea"))); QObject* object2 = object->property("item").value(); QVERIFY(object2 != nullptr); QCOMPARE(QString(object2->metaObject()->superClass()->className()), QLatin1String("QQuickRectangle")); engine.setImportPathList(defaultImportPathList); } void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */) { QVERIFY(fileName != nullptr); QVERIFY(propertyName != nullptr); if (!fileName || !propertyName) return; QQmlComponent component(&engine, testFileUrl(fileName)); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); getSingletonInstance(object.data(), propertyName, result); } void tst_qqmllanguage::getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */) { QVERIFY(o != nullptr); QVERIFY(propertyName != nullptr); if (!o || !propertyName) return; QVariant variant = o->property(propertyName); QVERIFY(variant.isValid()); QObject *singleton = nullptr; if (variant.typeId() == qMetaTypeId()) singleton = variant.value(); else if (variant.typeId() == qMetaTypeId()) singleton = variant.value().toQObject(); QVERIFY(singleton != nullptr); *result = singleton; } void verifyCompositeSingletonPropertyValues(QObject* o, const char* n1, int v1, const char* n2, int v2) { QCOMPARE(o->property(n1).typeId(), (int)QMetaType::Int); QCOMPARE(o->property(n1), QVariant(v1)); QCOMPARE(o->property(n2).typeId(), QMetaType::QString); QString numStr; QCOMPARE(o->property(n2), QVariant(QString(QLatin1String("Test value: ")).append(numStr.setNum(v2)))); } // Reads values from a composite singleton type void tst_qqmllanguage::compositeSingletonProperties() { QQmlComponent component(&engine, testFileUrl("singletonTest1.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); } // Checks that the addresses of the composite singletons used in the same // engine are the same. void tst_qqmllanguage::compositeSingletonSameEngine() { QObject* s1 = nullptr; getSingletonInstance(engine, "singletonTest2.qml", "singleton1", &s1); QVERIFY(s1 != nullptr); s1->setProperty("testProp2", QVariant(13)); QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest3.qml", "singleton2", &s2); QVERIFY(s2 != nullptr); QCOMPARE(s2->property("testProp2"), QVariant(13)); QCOMPARE(s1, s2); } // Checks that the addresses of the composite singletons used in different // engines are different. void tst_qqmllanguage::compositeSingletonDifferentEngine() { QQmlEngine e2; QObject* s1 = nullptr; getSingletonInstance(engine, "singletonTest2.qml", "singleton1", &s1); QVERIFY(s1 != nullptr); s1->setProperty("testProp2", QVariant(13)); QObject* s2 = nullptr; getSingletonInstance(e2, "singletonTest3.qml", "singleton2", &s2); QVERIFY(s2 != nullptr); QCOMPARE(s2->property("testProp2"), QVariant(25)); QVERIFY(s1 != s2); } // pragma Singleton in a non-type qml file fails void tst_qqmllanguage::compositeSingletonNonTypeError() { QQmlComponent component(&engine, testFileUrl("singletonTest4.qml")); VERIFY_ERRORS("singletonTest4.error.txt"); } // Loads the singleton using a namespace qualifier void tst_qqmllanguage::compositeSingletonQualifiedNamespace() { QQmlComponent component(&engine, testFileUrl("singletonTest5.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); // lets verify that the singleton instance we are using is the same // when loaded through another file (without namespace!) QObject *s1 = nullptr; getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest5a.qml", "singletonInstance", &s2); QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } // Loads a singleton from a module void tst_qqmllanguage::compositeSingletonModule() { engine.addImportPath(testFile("singleton/module")); QQmlComponent component(&engine, testFileUrl("singletonTest6.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); verifyCompositeSingletonPropertyValues(o.data(), "value3", 125, "value4", -55); // lets verify that the singleton instance we are using is the same // when loaded through another file QObject *s1 = nullptr; getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest6a.qml", "singletonInstance", &s2); QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } // Loads a singleton from a module with a higher version void tst_qqmllanguage::compositeSingletonModuleVersioned() { engine.addImportPath(testFile("singleton/module")); QQmlComponent component(&engine, testFileUrl("singletonTest7.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 225, "value2", 55); verifyCompositeSingletonPropertyValues(o.data(), "value3", 225, "value4", 55); // lets verify that the singleton instance we are using is the same // when loaded through another file QObject *s1 = nullptr; getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest7a.qml", "singletonInstance", &s2); QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } // Loads a singleton from a module with a qualified namespace void tst_qqmllanguage::compositeSingletonModuleQualified() { engine.addImportPath(testFile("singleton/module")); QQmlComponent component(&engine, testFileUrl("singletonTest8.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 225, "value2", 55); verifyCompositeSingletonPropertyValues(o.data(), "value3", 225, "value4", 55); // lets verify that the singleton instance we are using is the same // when loaded through another file QObject *s1 = nullptr; getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest8a.qml", "singletonInstance", &s2); QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } // Tries to instantiate a type with a pragma Singleton and fails void tst_qqmllanguage::compositeSingletonInstantiateError() { QQmlComponent component(&engine, testFileUrl("singletonTest9.qml")); VERIFY_ERRORS("singletonTest9.error.txt"); } // Having a composite singleton type as dynamic property type is allowed void tst_qqmllanguage::compositeSingletonDynamicPropertyError() { QQmlComponent component(&engine, testFileUrl("singletonTest10.qml")); VERIFY_ERRORS(0); } void tst_qqmllanguage::compositeSingletonDynamicSignalAndJavaScriptPragma() { { // Having a composite singleton type as dynamic signal parameter succeeds // (like C++ singleton) QQmlComponent component(&engine, testFileUrl("singletonTest11.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55); } { // Load a composite singleton type and a javascript file that has .pragma library // in it. This will make sure that the javascript .pragma does not get mixed with // the pragma Singleton changes. QQmlComponent component(&engine, testFileUrl("singletonTest16.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); // The value1 that is read from the SingletonType was changed from 125 to 99 // above. As the type is a singleton and // the engine has not been destroyed, we just retrieve the old instance and // the value is still 99. verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333); } } // Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it. // This will fail as qmlRegisterType will only instantiate CompositeTypes. void tst_qqmllanguage::compositeSingletonQmlRegisterTypeError() { qmlRegisterType(testFileUrl("singleton/registeredComposite/CompositeType.qml"), "CompositeSingletonTest", 1, 0, "RegisteredCompositeType"); QQmlComponent component(&engine, testFileUrl("singletonTest12.qml")); VERIFY_ERRORS("singletonTest12.error.txt"); } // Qmldir defines a type as a singleton, but the qml file does not have a pragma Singleton. void tst_qqmllanguage::compositeSingletonQmldirNoPragmaError() { QQmlComponent component(&engine, testFileUrl("singletonTest13.qml")); VERIFY_ERRORS("singletonTest13.error.txt"); } // Invalid singleton definition in the qmldir file results in an error void tst_qqmllanguage::compositeSingletonQmlDirError() { QQmlComponent component(&engine, testFileUrl("singletonTest14.qml")); VERIFY_ERRORS("singletonTest14.error.txt"); } // Load a remote composite singleton type via qmldir that defines the type as a singleton void tst_qqmllanguage::compositeSingletonRemote() { ThreadedTestHTTPServer server(dataDirectory()); QFile f(testFile("singletonTest15.qml")); QVERIFY(f.open(QIODevice::ReadOnly)); QByteArray contents = f.readAll(); f.close(); contents.replace(QByteArrayLiteral("{{ServerBaseUrl}}"), server.baseUrl().toString().toUtf8()); QQmlComponent component(&engine); component.setData(contents, testFileUrl("singletonTest15.qml")); while (component.isLoading()) QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 525, "value2", 355); } // Reads values from a Singleton accessed through selectors. void tst_qqmllanguage::compositeSingletonSelectors() { QQmlEngine e2; QQmlFileSelector qmlSelector(&e2); qmlSelector.setExtraSelectors(QStringList() << "basicSelector"); QQmlComponent component(&e2, testFileUrl("singletonTest1.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 625, "value2", 455); } // Reads values from a Singleton that was registered through the C++ API: // qmlRegisterSingletonType. void tst_qqmllanguage::compositeSingletonRegistered() { QQmlComponent component(&engine, testFileUrl("singletonTest17.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); verifyCompositeSingletonPropertyValues(o.data(), "value1", 925, "value2", 755); } void tst_qqmllanguage::compositeSingletonCircular() { QQmlComponent component(&engine, testFileUrl("circularSingleton.qml")); VERIFY_ERRORS(0); QQmlTestMessageHandler messageHandler; QScopedPointer o(component.create()); QVERIFY(o != nullptr); // ensure we aren't hitting the recursion warning QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); QCOMPARE(o->property("value").toInt(), 2); } void tst_qqmllanguage::singletonsHaveContextAndEngine() { QObject *qmlSingleton = nullptr; getSingletonInstance(engine, "singletonTest18.qml", "qmlSingleton", &qmlSingleton); QVERIFY(qmlContext(qmlSingleton)); QCOMPARE(qmlEngine(qmlSingleton), &engine); QObject *jsSingleton = nullptr; getSingletonInstance(engine, "singletonTest18.qml", "jsSingleton", &jsSingleton); QVERIFY(qmlContext(jsSingleton)); QCOMPARE(qmlEngine(jsSingleton), &engine); QObject *cppSingleton = nullptr; getSingletonInstance(engine, "singletonTest18.qml", "cppSingleton", &cppSingleton); QVERIFY(qmlContext(cppSingleton)); QCOMPARE(qmlEngine(cppSingleton), &engine); } void tst_qqmllanguage::customParserBindingScopes() { QQmlComponent component(&engine, testFileUrl("customParserBindingScopes.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QPointer child = qvariant_cast(o->property("child")); QVERIFY(!child.isNull()); QCOMPARE(child->property("testProperty").toInt(), 42); } void tst_qqmllanguage::customParserEvaluateEnum() { QQmlComponent component(&engine, testFileUrl("customParserEvaluateEnum.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); } void tst_qqmllanguage::customParserProperties() { QQmlComponent component(&engine, testFileUrl("customParserProperties.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); SimpleObjectWithCustomParser *testObject = qobject_cast(o.data()); QVERIFY(testObject); QCOMPARE(testObject->customBindingsCount(), 0); QCOMPARE(testObject->intProperty(), 42); QCOMPARE(testObject->property("qmlString").toString(), QStringLiteral("Hello")); QVERIFY(!testObject->property("someObject").isNull()); } void tst_qqmllanguage::customParserWithExtendedObject() { QQmlComponent component(&engine, testFileUrl("customExtendedParserProperties.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); SimpleObjectWithCustomParser *testObject = qobject_cast(o.data()); QVERIFY(testObject); QCOMPARE(testObject->customBindingsCount(), 0); QCOMPARE(testObject->intProperty(), 42); QCOMPARE(testObject->property("qmlString").toString(), QStringLiteral("Hello")); QVERIFY(!testObject->property("someObject").isNull()); QVariant returnValue; QVERIFY(QMetaObject::invokeMethod(o.data(), "getExtendedProperty", Q_RETURN_ARG(QVariant, returnValue))); QCOMPARE(returnValue.toInt(), 1584); } void tst_qqmllanguage::nestedCustomParsers() { QQmlComponent component(&engine, testFileUrl("nestedCustomParsers.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); SimpleObjectWithCustomParser *testObject = qobject_cast(o.data()); QVERIFY(testObject); QCOMPARE(testObject->customBindingsCount(), 1); SimpleObjectWithCustomParser *nestedObject = qobject_cast(testObject->property("nested").value()); QVERIFY(nestedObject); QCOMPARE(nestedObject->customBindingsCount(), 1); } void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() { QQmlComponent component(&engine, testFileUrl("preservePropertyCacheOnGroupObjects.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QObject *subObject = qvariant_cast(o->property("subObject")); QVERIFY(subObject); QCOMPARE(subObject->property("value").toInt(), 42); QQmlData *ddata = QQmlData::get(subObject); QVERIFY(ddata); QQmlPropertyCache *subCache = ddata->propertyCache; QVERIFY(subCache); QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/nullptr, /*context*/nullptr); QVERIFY(pd); QCOMPARE(pd->propType(), QMetaType::fromType()); } void tst_qqmllanguage::propertyCacheInSync() { QQmlComponent component(&engine, testFileUrl("propertyCacheInSync.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QObject *anchors = qvariant_cast(o->property("anchors")); QVERIFY(anchors); QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(anchors); QVERIFY(vmemo); QQmlPropertyCache *vmemoCache = vmemo->propertyCache(); QVERIFY(vmemoCache); QQmlData *ddata = QQmlData::get(anchors); QVERIFY(ddata); QVERIFY(ddata->propertyCache); // Those always have to be in sync and correct. QCOMPARE(ddata->propertyCache, vmemoCache); QCOMPARE(anchors->property("margins").toInt(), 50); } void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() { QQmlComponent component(&engine, testFileUrl("rootObjectInCreationNotForSubObjects.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); // QQmlComponent should have set this back to false anyway QQmlData *ddata = QQmlData::get(o.data()); QVERIFY(!ddata->rootObjectInCreation); QObject *subObject = qvariant_cast(o->property("subObject")); QVERIFY(!subObject); qmlExecuteDeferred(o.data()); subObject = qvariant_cast(o->property("subObject")); QVERIFY(subObject); ddata = QQmlData::get(subObject); // This should never have been set in the first place as there is no // QQmlComponent to set it back to false. QVERIFY(!ddata->rootObjectInCreation); } // QTBUG-63036 void tst_qqmllanguage::lazyDeferredSubObject() { QQmlComponent component(&engine, testFileUrl("lazyDeferredSubObject.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QObject *subObject = qvariant_cast(object->property("subObject")); QVERIFY(subObject); QCOMPARE(object->objectName(), QStringLiteral("custom")); QCOMPARE(subObject->objectName(), QStringLiteral("custom")); } // QTBUG-63200 void tst_qqmllanguage::deferredProperties() { QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QObject *innerObj = object->findChild(QStringLiteral("innerobj")); QVERIFY(!innerObj); QObject *outerObj = object->findChild(QStringLiteral("outerobj")); QVERIFY(!outerObj); QObject *groupProperty = object->property("groupProperty").value(); QVERIFY(!groupProperty); QQmlListProperty listProperty = object->property("listProperty").value>(); QCOMPARE(listProperty.count(&listProperty), 0); QQmlData *qmlData = QQmlData::get(object.data()); QVERIFY(qmlData); QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2" QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2" qmlExecuteDeferred(object.data()); QCOMPARE(qmlData->deferredData.count(), 0); innerObj = object->findChild(QStringLiteral("innerobj")); // MyDeferredListProperty.qml QVERIFY(innerObj); QCOMPARE(innerObj->property("wasCompleted"), QVariant(true)); outerObj = object->findChild(QStringLiteral("outerobj")); // deferredListProperty.qml QVERIFY(outerObj); QCOMPARE(outerObj->property("wasCompleted"), QVariant(true)); groupProperty = object->property("groupProperty").value(); QCOMPARE(groupProperty, outerObj); listProperty = object->property("listProperty").value>(); QCOMPARE(listProperty.count(&listProperty), 4); QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("innerlist1")); // MyDeferredListProperty.qml QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true)); QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("innerlist2")); // MyDeferredListProperty.qml QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); QCOMPARE(listProperty.at(&listProperty, 2)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml QCOMPARE(listProperty.at(&listProperty, 2)->property("wasCompleted"), QVariant(true)); QCOMPARE(listProperty.at(&listProperty, 3)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml QCOMPARE(listProperty.at(&listProperty, 3)->property("wasCompleted"), QVariant(true)); } static void beginDeferredOnce(QQmlEnginePrivate *enginePriv, const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState) { QObject *object = property.object(); QQmlData *ddata = QQmlData::get(object); Q_ASSERT(!ddata->deferredData.isEmpty()); int propertyIndex = property.index(); for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) { QQmlData::DeferredData *deferData = *dit; auto range = deferData->bindings.equal_range(propertyIndex); if (range.first == deferData->bindings.end()) continue; QQmlComponentPrivate::ConstructionState *state = new QQmlComponentPrivate::ConstructionState; state->completePending = true; state->creator.reset(new QQmlObjectCreator( deferData->context->parent(), deferData->compilationUnit, QQmlRefPointer())); enginePriv->inProgressCreations++; std::deque reversedBindings; std::copy(range.first, range.second, std::front_inserter(reversedBindings)); state->creator->beginPopulateDeferred(deferData->context); for (const QV4::CompiledData::Binding *binding: reversedBindings) state->creator->populateDeferredBinding(property, deferData->deferredIdx, binding); state->creator->finalizePopulateDeferred(); state->errors << state->creator->errors; deferredState->constructionStates += state; // Cleanup any remaining deferred bindings for this property, also in inner contexts, // to avoid executing them later and overriding the property that was just populated. while (dit != ddata->deferredData.rend()) { (*dit)->bindings.remove(propertyIndex); ++dit; } break; } } static void testExecuteDeferredOnce(const QQmlProperty &property) { QObject *object = property.object(); QQmlData *data = QQmlData::get(object); if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); QQmlComponentPrivate::DeferredState state; beginDeferredOnce(ep, property, &state); // Release deferred data for those compilation units that no longer have deferred bindings data->releaseDeferredData(); QQmlComponentPrivate::completeDeferred(ep, &state); } } void tst_qqmllanguage::executeDeferredPropertiesOnce() { QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QObjectList innerObjsAtCreation = object->findChildren(QStringLiteral("innerobj")); QVERIFY(innerObjsAtCreation.isEmpty()); QObjectList outerObjsAtCreation = object->findChildren(QStringLiteral("outerobj")); QVERIFY(outerObjsAtCreation.isEmpty()); QObject *groupProperty = object->property("groupProperty").value(); QVERIFY(!groupProperty); QQmlListProperty listProperty = object->property("listProperty").value>(); QCOMPARE(listProperty.count(&listProperty), 0); QQmlData *qmlData = QQmlData::get(object.data()); QVERIFY(qmlData); QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2" QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2" // first execution creates the outer object testExecuteDeferredOnce(QQmlProperty(object.data(), "groupProperty")); QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2" QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2" QObjectList innerObjsAfterFirstExecute = object->findChildren(QStringLiteral("innerobj")); // MyDeferredListProperty.qml QVERIFY(innerObjsAfterFirstExecute.isEmpty()); QObjectList outerObjsAfterFirstExecute = object->findChildren(QStringLiteral("outerobj")); // deferredListProperty.qml QCOMPARE(outerObjsAfterFirstExecute.count(), 1); QCOMPARE(outerObjsAfterFirstExecute.first()->property("wasCompleted"), QVariant(true)); groupProperty = object->property("groupProperty").value(); QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first()); listProperty = object->property("listProperty").value>(); QCOMPARE(listProperty.count(&listProperty), 0); // re-execution does nothing (to avoid overriding the property) testExecuteDeferredOnce(QQmlProperty(object.data(), "groupProperty")); QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2" QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2" QObjectList innerObjsAfterSecondExecute = object->findChildren(QStringLiteral("innerobj")); // MyDeferredListProperty.qml QVERIFY(innerObjsAfterSecondExecute.isEmpty()); QObjectList outerObjsAfterSecondExecute = object->findChildren(QStringLiteral("outerobj")); // deferredListProperty.qml QCOMPARE(outerObjsAfterFirstExecute, outerObjsAfterSecondExecute); groupProperty = object->property("groupProperty").value(); QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first()); listProperty = object->property("listProperty").value>(); QCOMPARE(listProperty.count(&listProperty), 0); // execution of a list property should execute all outer list bindings testExecuteDeferredOnce(QQmlProperty(object.data(), "listProperty")); QCOMPARE(qmlData->deferredData.count(), 0); listProperty = object->property("listProperty").value>(); QCOMPARE(listProperty.count(&listProperty), 2); QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true)); QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); } void tst_qqmllanguage::noChildEvents() { QQmlComponent component(&engine); component.setData("import QtQml 2.0; import Test 1.0; MyQmlObject { property QtObject child: QtObject {} }", QUrl()); VERIFY_ERRORS(0); QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->childAddedEventCount(), 0); } void tst_qqmllanguage::earlyIdObjectAccess() { QQmlComponent component(&engine, testFileUrl("earlyIdObjectAccess.qml")); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QVERIFY(o->property("success").toBool()); } void tst_qqmllanguage::deleteSingletons() { QPointer singleton; { QQmlEngine tmpEngine; QQmlComponent component(&tmpEngine, testFileUrl("singletonTest5.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QObject *s1 = nullptr; getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); singleton = s1; QVERIFY(singleton.data() != nullptr); } QVERIFY(singleton.data() == nullptr); } void tst_qqmllanguage::arrayBuffer_data() { QTest::addColumn("file"); QTest::newRow("arraybuffer_property_get") << "arraybuffer_property_get.qml"; QTest::newRow("arraybuffer_property_set") << "arraybuffer_property_set.qml"; QTest::newRow("arraybuffer_signal_arg") << "arraybuffer_signal_arg.qml"; QTest::newRow("arraybuffer_method_arg") << "arraybuffer_method_arg.qml"; QTest::newRow("arraybuffer_method_return") << "arraybuffer_method_return.qml"; QTest::newRow("arraybuffer_method_overload") << "arraybuffer_method_overload.qml"; } void tst_qqmllanguage::arrayBuffer() { QFETCH(QString, file); QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("ok").toBool(), true); } void tst_qqmllanguage::defaultListProperty() { QQmlComponent component(&engine, testFileUrl("defaultListProperty.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); } void tst_qqmllanguage::namespacedPropertyTypes() { QQmlComponent component(&engine, testFileUrl("namespacedPropertyTypes.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); } void tst_qqmllanguage::qmlTypeCanBeResolvedByName_data() { QTest::addColumn("componentUrl"); // Built-in C++ types QTest::newRow("C++ - Anonymous") << testFileUrl("quickTypeByName_anon.qml"); QTest::newRow("C++ - Named") << testFileUrl("quickTypeByName_named.qml"); // Composite types with a qmldir QTest::newRow("QML - Anonymous - qmldir") << testFileUrl("compositeTypeByName_anon_qmldir.qml"); QTest::newRow("QML - Named - qmldir") << testFileUrl("compositeTypeByName_named_qmldir.qml"); } void tst_qqmllanguage::qmlTypeCanBeResolvedByName() { QFETCH(QUrl, componentUrl); QQmlEngine engine; QQmlComponent component(&engine, componentUrl); VERIFY_ERRORS(0); QTest::ignoreMessage(QtMsgType::QtWarningMsg, "[object Object]"); // a bit crude, but it will do QScopedPointer o(component.create()); QVERIFY(!o.isNull()); } // Tests for the QML-only extensions of instanceof. Tests for the regular JS // instanceof belong in tst_qqmlecmascript! void tst_qqmllanguage::instanceof_data() { QTest::addColumn("documentToTestIn"); QTest::addColumn("expectedValue"); // so the way this works is that the name of the test tag defines the test // to run. // // the expectedValue is either a boolean true or false for whether the two // operands are indeed an instanceof each other, or a string for the // expected error message. // assert that basic types don't convert to QObject QTest::newRow("1 instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); QTest::newRow("true instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); QTest::newRow("\"foobar\" instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); // assert that Managed don't either QTest::newRow("new String(\"foobar\") instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); QTest::newRow("new Object() instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); QTest::newRow("new Date() instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); // test that simple QtQml comparisons work QTest::newRow("qtobjectInstance instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(true); QTest::newRow("qtobjectInstance instanceof Timer") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); QTest::newRow("timerInstance instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(true); QTest::newRow("timerInstance instanceof Timer") << testFileUrl("instanceof_qtqml.qml") << QVariant(true); QTest::newRow("connectionsInstance instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") << QVariant(true); QTest::newRow("connectionsInstance instanceof Timer") << testFileUrl("instanceof_qtqml.qml") << QVariant(false); QTest::newRow("connectionsInstance instanceof Connections") << testFileUrl("instanceof_qtqml.qml") << QVariant(true); // make sure they still work when imported with a qualifier QTest::newRow("qtobjectInstance instanceof QmlImport.QtObject") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(true); QTest::newRow("qtobjectInstance instanceof QmlImport.Timer") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(false); QTest::newRow("timerInstance instanceof QmlImport.QtObject") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(true); QTest::newRow("timerInstance instanceof QmlImport.Timer") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(true); QTest::newRow("connectionsInstance instanceof QmlImport.QtObject") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(true); QTest::newRow("connectionsInstance instanceof QmlImport.Timer") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(false); QTest::newRow("connectionsInstance instanceof QmlImport.Connections") << testFileUrl("instanceof_qtqml_qualified.qml") << QVariant(true); // test that Quick C++ types work ok QTest::newRow("itemInstance instanceof QtObject") << testFileUrl("instanceof_qtquick.qml") << QVariant(true); QTest::newRow("itemInstance instanceof Timer") << testFileUrl("instanceof_qtquick.qml") << QVariant(false); QTest::newRow("itemInstance instanceof Rectangle") << testFileUrl("instanceof_qtquick.qml") << QVariant(false); QTest::newRow("rectangleInstance instanceof Item") << testFileUrl("instanceof_qtquick.qml") << QVariant(true); QTest::newRow("rectangleInstance instanceof Rectangle") << testFileUrl("instanceof_qtquick.qml") << QVariant(true); QTest::newRow("rectangleInstance instanceof MouseArea") << testFileUrl("instanceof_qtquick.qml") << QVariant(false); QTest::newRow("mouseAreaInstance instanceof Item") << testFileUrl("instanceof_qtquick.qml") << QVariant(true); QTest::newRow("mouseAreaInstance instanceof Rectangle") << testFileUrl("instanceof_qtquick.qml") << QVariant(false); QTest::newRow("mouseAreaInstance instanceof MouseArea") << testFileUrl("instanceof_qtquick.qml") << QVariant(true); // test that unqualified quick composite types work ok QTest::newRow("rectangleInstance instanceof CustomRectangle") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(false); QTest::newRow("customRectangleInstance instanceof Rectangle") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(true); QTest::newRow("customRectangleInstance instanceof Item") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(true); QTest::newRow("customRectangleWithPropInstance instanceof CustomRectangleWithProp") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(true); QTest::newRow("customRectangleWithPropInstance instanceof CustomRectangle") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(false); // ### XXX: QTBUG-58477 QTest::newRow("customRectangleWithPropInstance instanceof Rectangle") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(true); QTest::newRow("customRectangleInstance instanceof MouseArea") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(false); QTest::newRow("customMouseAreaInstance instanceof MouseArea") << testFileUrl("instanceof_qtquick_composite.qml") << QVariant(true); // test that they still work when qualified QTest::newRow("rectangleInstance instanceof CustomImport.CustomRectangle") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(false); QTest::newRow("customRectangleInstance instanceof QuickImport.Rectangle") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(true); QTest::newRow("customRectangleInstance instanceof QuickImport.Item") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(true); QTest::newRow("customRectangleWithPropInstance instanceof CustomImport.CustomRectangleWithProp") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(true); QTest::newRow("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(false); // ### XXX: QTBUG-58477 QTest::newRow("customRectangleWithPropInstance instanceof QuickImport.Rectangle") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(true); QTest::newRow("customRectangleInstance instanceof QuickImport.MouseArea") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(false); QTest::newRow("customMouseAreaInstance instanceof QuickImport.MouseArea") << testFileUrl("instanceof_qtquick_composite_qualified.qml") << QVariant(true); } void tst_qqmllanguage::instanceof() { QFETCH(QUrl, documentToTestIn); QFETCH(QVariant, expectedValue); QQmlEngine engine; QQmlComponent component(&engine, documentToTestIn); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o != nullptr); QQmlExpression expr(engine.contextForObject(o.data()), nullptr, QString::fromLatin1(QTest::currentDataTag())); QVariant ret = expr.evaluate(); if (expectedValue.typeId() == QMetaType::Bool) { // no error expected QVERIFY2(!expr.hasError(), qPrintable(expr.error().description())); bool returnValue = ret.toBool(); if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") || QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle")) QCOMPARE(returnValue, expectedValue.toBool()); } else { QVERIFY(expr.hasError()); QCOMPARE(expr.error().description(), expectedValue.toString()); } } void tst_qqmllanguage::concurrentLoadQmlDir() { ThreadedTestHTTPServer server(dataDirectory()); QString serverdir = server.urlString("/lib/"); engine.setImportPathList(QStringList(defaultImportPathList) << serverdir); QQmlComponent component(&engine, testFileUrl("concurrentLoad_main.qml")); QTRY_VERIFY(component.isReady()); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); engine.setImportPathList(defaultImportPathList); } // Test that deleting an object and then accessing it doesn't crash. // QTBUG-44153 class ObjectCreator : public QObject { Q_OBJECT public slots: QObject *create() { return (new ObjectCreator); } void del() { delete this; } }; void tst_qqmllanguage::accessDeletedObject() { QQmlEngine engine; QScopedPointer creator(new ObjectCreator); engine.rootContext()->setContextProperty("objectCreator", creator.get()); QQmlComponent component(&engine, testFileUrl("accessDeletedObject.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); } void tst_qqmllanguage::lowercaseTypeNames() { QCOMPARE(qmlRegisterType("Test", 1, 0, "lowerCaseTypeName"), -1); QCOMPARE(qmlRegisterSingletonType("Test", 1, 0, "lowerCaseTypeName", nullptr), -1); } void tst_qqmllanguage::thisInQmlScope() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("thisInQmlScope.qml")); QTRY_VERIFY(component.isReady()); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QCOMPARE(o->property("x"), QVariant(42)); QCOMPARE(o->property("y"), QVariant(42)); QCOMPARE(o->property("a"), QVariant(42)); QCOMPARE(o->property("b"), QVariant(42)); } void tst_qqmllanguage::valueTypeGroupPropertiesInBehavior() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("groupPropertyInPropertyValueSource.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QObject *animation = qmlContext(o.data())->contextProperty("animation").value(); QVERIFY(animation); QCOMPARE(animation->property("easing").value().type(), QEasingCurve::InOutQuad); } void tst_qqmllanguage::retrieveQmlTypeId() { // Register in reverse order to provoke wrong minor version matching. int id2 = qmlRegisterType("Test", 2, 3, "SomeTestType"); int id1 = qmlRegisterType("Test", 2, 1, "SomeTestType"); QCOMPARE(qmlTypeId("Test", 2, 1, "SomeTestType"), id1); QCOMPARE(qmlTypeId("Test", 2, 2, "SomeTestType"), id1); QCOMPARE(qmlTypeId("Test", 2, 3, "SomeTestType"), id2); // Error cases QCOMPARE(qmlTypeId("Test", 2, 0, "SomeTestType"), -1); QCOMPARE(qmlTypeId("Test", 2, 3, "DoesNotExist"), -1); QCOMPARE(qmlTypeId("DoesNotExist", 2, 3, "SomeTestType"), -1); // Must also work for other types (defined in testtpes.cpp) QVERIFY(qmlTypeId("Test", 1, 0, "MyExtendedUncreateableBaseClass") >= 0); QVERIFY(qmlTypeId("Test", 1, 0, "MyUncreateableBaseClass") >= 0); QVERIFY(qmlTypeId("Test", 1, 0, "MyTypeObjectSingleton") >= 0); } void tst_qqmllanguage::polymorphicFunctionLookup() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("polymorphicFunctionLookup.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QVERIFY(o->property("ok").toBool()); } void tst_qqmllanguage::anchorsToParentInPropertyChanges() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("anchorsToParentInPropertyChagnes.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QTRY_COMPARE(o->property("edgeWidth").toInt(), 200); } void tst_qqmllanguage::typeWrapperToVariant() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("typeWrapperToVariant.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QObject *connections = qvariant_cast(o->property("connections")); QVERIFY(connections); QObject *target = qvariant_cast(connections->property("target")); QVERIFY(target); } void tst_qqmllanguage::extendedForeignTypes() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("foreignExtended.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QCOMPARE(o->property("extendedBase").toInt(), 43); QCOMPARE(o->property("extendedExtension").toInt(), 42); QCOMPARE(o->property("foreignExtendedExtension").toInt(), 42); QCOMPARE(o->property("foreignObjectName").toString(), QLatin1String("foreign")); QCOMPARE(o->property("foreignExtendedObjectName").toString(), QLatin1String("foreignExtended")); QCOMPARE(o->property("extendedInvokable").toInt(), 123); QCOMPARE(o->property("extendedSlot").toInt(), 456); } void tst_qqmllanguage::foreignTypeSingletons() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("foreignSingleton.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QCOMPARE(o->property("number").toInt(), 42); } void tst_qqmllanguage::selfReference() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("SelfReference.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component); auto compilationUnit = componentPrivate->compilationUnit; QVERIFY(compilationUnit); const QMetaObject *metaObject = o->metaObject(); QMetaProperty selfProperty = metaObject->property(metaObject->indexOfProperty("self")); QCOMPARE(selfProperty.metaType().id(), compilationUnit->typeIds.id.id()); QByteArray typeName = selfProperty.typeName(); QVERIFY(typeName.endsWith('*')); typeName = typeName.chopped(1); QCOMPARE(typeName, metaObject->className()); QMetaMethod selfFunction = metaObject->method(metaObject->indexOfMethod("returnSelf()")); QVERIFY(selfFunction.isValid()); QCOMPARE(selfFunction.returnType(), compilationUnit->typeIds.id.id()); QMetaMethod selfSignal; for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) { QMetaMethod method = metaObject->method(i); if (method.isValid() && method.name().startsWith("blah")) { selfSignal = method; break; } } QVERIFY(selfSignal.isValid()); QCOMPARE(selfSignal.parameterCount(), 1); QCOMPARE(selfSignal.parameterType(0), compilationUnit->typeIds.id.id()); } void tst_qqmllanguage::selfReferencingSingleton() { QQmlEngine engine; engine.addImportPath(dataDirectory()); QPointer singletonPointer; { QQmlComponent component(&engine); component.setData(QByteArray(R"(import QtQml 2.0 import selfreferencingsingletonmodule 1.0 QtObject { property SelfReferencingSingleton singletonPointer: SelfReferencingSingleton })"), QUrl()); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); singletonPointer = o->property("singletonPointer").value(); } QVERIFY(!singletonPointer.isNull()); QCOMPARE(singletonPointer->property("dummy").toInt(), 42); } void tst_qqmllanguage::listContainingDeletedObject() { QQmlEngine engine; auto url = testFileUrl("listContainingDeleted.qml"); const QString message = url.toString() + ":24: TypeError: Cannot read property 'enabled' of null"; QTest::ignoreMessage(QtMsgType::QtWarningMsg, message.toUtf8().data()); QQmlComponent comp(&engine, url); QScopedPointer root(comp.create()); QVERIFY(root); auto cmp = root->property("a").value(); auto o = cmp->create(); QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o))); delete o; QMetaObject::invokeMethod(root.get(), "use"); } void tst_qqmllanguage::overrideSingleton() { auto check = [](const QString &name, const QByteArray &singletonElement) { const QByteArray testQml = "import Test 1.0\n" "import QtQml 2.0\n" "QtObject { objectName: " + singletonElement + ".objectName }"; QQmlEngine engine; QQmlComponent component(&engine, nullptr); component.setData(testQml, QUrl("singleton.qml")); QVERIFY(component.isReady()); QScopedPointer obj(component.create()); QCOMPARE(obj->objectName(), name); }; check("statically registered", "BareSingleton"); BareSingleton singleton; singleton.setObjectName("dynamically registered"); qmlRegisterSingletonInstance("Test", 1, 0, "BareSingleton", &singleton); check("dynamically registered", "BareSingleton"); QTest::ignoreMessage( QtWarningMsg, "singleton.qml:3: TypeError: Cannot read property 'objectName' of undefined"); check("", "UncreatableSingleton"); qmlRegisterSingletonInstance("Test", 1, 0, "UncreatableSingleton", UncreatableSingleton::instance()); check("uncreatable", "UncreatableSingleton"); } class AttachedObject; class InnerObject : public QObject { Q_OBJECT Q_PROPERTY(bool revisionedProperty READ revisionedProperty WRITE setRevisionedProperty NOTIFY revisionedPropertyChanged REVISION 2) public: InnerObject(QObject *parent = nullptr) : QObject(parent) {} bool revisionedProperty() const { return m_revisionedProperty; } void setRevisionedProperty(bool revisionedProperty) { if (revisionedProperty != m_revisionedProperty) { m_revisionedProperty = revisionedProperty; emit revisionedPropertyChanged(); } } static AttachedObject *qmlAttachedProperties(QObject *object); signals: Q_REVISION(2) void revisionedPropertyChanged(); private: bool m_revisionedProperty = false; }; class AttachedObject : public QObject { Q_OBJECT Q_PROPERTY(InnerObject *attached READ attached CONSTANT) public: explicit AttachedObject(QObject *parent = nullptr) : QObject(parent), m_attached(new InnerObject(this)) {} InnerObject *attached() const { return m_attached; } private: InnerObject *m_attached; }; class OuterObject : public QObject { Q_OBJECT public: explicit OuterObject(QObject *parent = nullptr) : QObject(parent) {} }; AttachedObject *InnerObject::qmlAttachedProperties(QObject *object) { return new AttachedObject(object); } QML_DECLARE_TYPE(InnerObject) QML_DECLARE_TYPEINFO(InnerObject, QML_HAS_ATTACHED_PROPERTIES) void tst_qqmllanguage::revisionedPropertyOfAttachedObjectProperty() { qmlRegisterAnonymousType("foo", 2); qmlRegisterType("foo", 2, 0, "InnerObject"); qmlRegisterType("foo", 2, 2, "InnerObject"); qmlRegisterType("foo", 2, 2, "OuterObject"); QQmlEngine engine; QQmlComponent component(&engine); component.setData("import foo 2.2\n" "OuterObject {\n" " InnerObject.attached.revisionedProperty: true\n" "}", QUrl()); QVERIFY(component.isReady()); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); } void tst_qqmllanguage::inlineComponent() { QFETCH(QUrl, componentUrl); QFETCH(QColor, color); QFETCH(int, width); QFETCH(bool, checkProperties); QQmlEngine engine; QQmlComponent component(&engine, componentUrl); QScopedPointer o(component.create()); if (component.isError()) { qDebug() << component.errorString(); } QVERIFY(!o.isNull()); if (checkProperties) { auto icInstance = o->findChild("icInstance"); QVERIFY(icInstance); QCOMPARE(icInstance->property("color").value(),color); QCOMPARE(icInstance->property("width").value(), width); } } void tst_qqmllanguage::inlineComponent_data() { QTest::addColumn("componentUrl"); QTest::addColumn("color"); QTest::addColumn("width"); QTest::addColumn("checkProperties"); QTest::newRow("Usage from other component") << testFileUrl("inlineComponentUser1.qml") << QColorConstants::Blue << 24 << true; QTest::newRow("Reexport") << testFileUrl("inlineComponentUser2.qml") << QColorConstants::Svg::green << 24 << true; QTest::newRow("Usage in same component") << testFileUrl("inlineComponentUser3.qml") << QColorConstants::Blue << 24 << true; QTest::newRow("Resolution happens at instantiation") << testFileUrl("inlineComponentUser4.qml") << QColorConstants::Blue << 24 << true; QTest::newRow("Non-toplevel IC is found") << testFileUrl("inlineComponentUser5.qml") << QColorConstants::Svg::red << 24 << true; QTest::newRow("Resolved in correct order") << testFileUrl("inlineComponentOrder.qml") << QColorConstants::Blue << 200 << true; QTest::newRow("ID resolves correctly") << testFileUrl("inlineComponentWithId.qml") << QColorConstants::Svg::red << 42 << true; QTest::newRow("Alias resolves correctly") << testFileUrl("inlineComponentWithAlias.qml") << QColorConstants::Svg::lime << 42 << true; QTest::newRow("Two inline components in same do not crash (QTBUG-86989)") << testFileUrl("twoInlineComponents.qml") << QColor() << 0 << false; QTest::newRow("Inline components used in same file (QTBUG-89173)") << testFileUrl("inlineComponentsSameFile.qml") << QColor() << 0 << false; } void tst_qqmllanguage::inlineComponentReferenceCycle_data() { QTest::addColumn("componentUrl"); QTest::newRow("Simple cycle") << testFileUrl("icSimpleCycle.qml"); QTest::newRow("Via property") << testFileUrl("icCycleViaProperty.qml"); } void tst_qqmllanguage::inlineComponentReferenceCycle() { QFETCH(QUrl, componentUrl); QQmlEngine engine; QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); QQmlComponent component(&engine, componentUrl); QScopedPointer o(component.create()); QVERIFY(o.isNull()); QVERIFY(component.isError()); QCOMPARE(component.errorString(), componentUrl.toString() + QLatin1String(":-1 Inline components form a cycle!\n")); } void tst_qqmllanguage::nestedInlineComponentNotAllowed() { QQmlEngine engine; auto url = testFileUrl("nestedIC.qml"); QQmlComponent component(&engine, url); QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); QScopedPointer o(component.create()); QVERIFY(component.isError()); QCOMPARE(component.errorString(), QLatin1String("%1:%2").arg(url.toString(), QLatin1String("5 Nested inline components are not supported\n"))); } void tst_qqmllanguage::inlineComponentStaticTypeResolution() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("InlineComponentChild.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("i").toInt(), 42); } void tst_qqmllanguage::inlineComponentInSingleton() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("singletonICTest.qml")); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY(!o.isNull()); auto untyped = o->property("singleton1"); QVERIFY(untyped.isValid()); auto singleton1 = untyped.value(); QVERIFY(singleton1); QCOMPARE(singleton1->property("iProp").value(), 42); QCOMPARE(singleton1->property("sProp").value(), QString::fromLatin1("Hello, world")); QVERIFY(!o.isNull()); } void tst_qqmllanguage::nonExistingInlineComponent_data() { QTest::addColumn("componentUrl"); QTest::addColumn("errorMessage"); QTest::addColumn("line"); QTest::addColumn("column"); QTest::newRow("Property type") << testFileUrl("nonExistingICUser1.qml") << QString("Type InlineComponentProvider has no inline component type called NonExisting") << 4 << 5; QTest::newRow("Instantiation") << testFileUrl("nonExistingICUser2.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 4 << 5; QTest::newRow("Inheritance") << testFileUrl("nonExistingICUser3.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 3 << 1; QTest::newRow("From singleton") << testFileUrl("nonExistingICUser4.qml") << QString("Type MySingleton.SingletonTypeWithIC has no inline component type called NonExisting") << 5 << 5; QTest::newRow("Cannot access parent inline components from child") << testFileUrl("nonExistingICUser5.qml") << QString("Type InlineComponentProviderChild has no inline component type called StyledRectangle") << 4 << 5; } void tst_qqmllanguage::nonExistingInlineComponent() { QFETCH(QUrl, componentUrl); QFETCH(QString, errorMessage); QFETCH(int, line); QFETCH(int, column); QQmlEngine engine; QQmlComponent component(&engine, componentUrl); auto errors = component.errors(); QCOMPARE(errors.size(), 1); const auto &error = errors.first(); QCOMPARE(error.description(), errorMessage); QCOMPARE(error.line(), line); QCOMPARE(error.column(), column); } void tst_qqmllanguage::inlineComponentFoundBeforeOtherImports() { QQmlEngine engine; QUrl url = testFileUrl("inlineComponentFoundBeforeOtherImports.qml"); QQmlComponent component(&engine, url); QTest::ignoreMessage(QtMsgType::QtInfoMsg, "Created"); QScopedPointer root {component.create()}; } void tst_qqmllanguage::inlineComponentDuplicateNameError() { QQmlEngine engine; QUrl url = testFileUrl("inlineComponentDuplicateName.qml"); QQmlComponent component(&engine, url); QString message = QLatin1String("%1:5 Inline component names must be unique per file\n").arg(url.toString()); QScopedPointer root {component.create()}; QVERIFY(root.isNull()); QVERIFY(component.isError()); QCOMPARE(component.errorString(), message); } struct QJSValueConvertible { Q_GADGET public: QString msg; }; bool operator==(const QJSValueConvertible &lhs, const QJSValueConvertible &rhs) { return lhs.msg == rhs.msg; } class TestItem : public QObject { Q_OBJECT Q_PROPERTY( QVector positions MEMBER m_points ) Q_PROPERTY( QSet barrays MEMBER m_barrays ) Q_PROPERTY( QVector convertibles MEMBER m_convertibles) public: TestItem() = default; QVector< QPointF > m_points; QSet m_barrays; QVector m_convertibles; }; Q_DECLARE_METATYPE(QVector); Q_DECLARE_METATYPE(QSet); Q_DECLARE_METATYPE(QJSValueConvertible); Q_DECLARE_METATYPE(QVector); void tst_qqmllanguage::arrayToContainer() { QMetaType::registerConverter< QJSValue, QJSValueConvertible >( [](const QJSValue& value) { return QJSValueConvertible{value.toString()}; } ); QQmlEngine engine; qmlRegisterType("qt.test", 1, 0, "TestItem"); QVector points { QPointF (2.0, 3.0) }; QSet barrays { QByteArray("hello"), QByteArray("world") }; engine.rootContext()->setContextProperty("test", QVariant::fromValue(points)); QQmlComponent component(&engine, testFileUrl("arrayToContainer.qml")); VERIFY_ERRORS(0); QScopedPointer root(qobject_cast(component.createWithInitialProperties( {{"vector", QVariant::fromValue(points)}, {"myset", QVariant::fromValue(barrays)} } ))); QVERIFY(root); QCOMPARE(root->m_points.at(0), QPointF (2.0, 3.0) ); QVERIFY(root->m_barrays.contains("hello")); QVERIFY(root->m_barrays.contains("world")); QCOMPARE(root->m_convertibles.size(), 2); QCOMPARE(root->m_convertibles.at(0).msg, QLatin1String("hello")); QCOMPARE(root->m_convertibles.at(1).msg, QLatin1String("world")); } class EnumTester : public QObject { Q_OBJECT public: enum Types { FIRST = 0, SECOND, THIRD }; Q_ENUM(Types) }; void tst_qqmllanguage::qualifiedScopeInCustomParser() { qmlRegisterUncreatableType("scoped.custom.test", 1, 0, "EnumTester", "Object only creatable in C++"); QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQml.Models 2.12\n" "import scoped.custom.test 1.0 as BACKEND\n" "ListModel {\n" " ListElement { text: \"a\"; type: BACKEND.EnumTester.FIRST }\n" "}\n", QUrl()); QVERIFY(component.isReady()); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); } void tst_qqmllanguage::checkUncreatableNoReason() { qmlRegisterTypesAndRevisions("qt.uncreatable.noreason", 1, 0); QQmlEngine engine; QString qml = QString("import QtQuick 2.0\nimport qt.uncreatable.noreason 1.0\nUncreatableElementNoReason {}"); QQmlComponent c(&engine); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Type cannot be created in QML.")); } void tst_qqmllanguage::checkURLtoURLObject() { QQmlEngine engine; QString qml = QString("import QtQuick 2.0\nItem { property url source: 'file:///foo/bar/'; " "Component.onCompleted: { new URL(parent.source); } }"); QQmlComponent c(&engine); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); QCOMPARE(c.errors().count(), 0); } struct TestValueType { Q_GADGET Q_PROPERTY(int foo MEMBER foo) public: int foo = 12; friend bool operator==(const TestValueType &a, const TestValueType &b) { return a.foo == b.foo; } friend bool operator!=(const TestValueType &a, const TestValueType &b) { return a.foo != b.foo; } }; struct TestExtendedValueType { Q_GADGET Q_PROPERTY(int bar READ bar WRITE setBar) public: TestValueType wrapped; int bar() const { return wrapped.foo; } void setBar(int bar) { wrapped.foo = bar; } }; class TestObjectType : public QObject { Q_OBJECT Q_PROPERTY(TestValueType test MEMBER test) public: TestValueType test; }; void tst_qqmllanguage::registerValueTypes() { QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"UpperCase\"; value type names should begin with a lowercase letter"); QVERIFY(qmlRegisterType("DoesNotWork", 1, 0, "UpperCase") >= 0); QVERIFY(qmlRegisterType("DoesWork", 1, 0, "TestObject") >= 0); { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import QtQml\nimport DoesWork\nTestObject { Component.onCompleted: test.foo = 14 }", QUrl()); QVERIFY(c.isReady()); QScopedPointer obj(c.create()); QCOMPARE(obj->property("test").value().foo, 14); QQmlComponent c2(&engine); c2.setData("import QtQml\nimport DoesWork\n TestObject { Component.onCompleted: test.bar = 14 }", QUrl()); QVERIFY(c2.isReady()); QScopedPointer obj2(c2.create()); QCOMPARE(obj2->property("test").value().foo, 12); } QVERIFY((qmlRegisterExtendedType("DoesWork", 1, 0, "lowerCase")) >= 0); { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import QtQml\nimport DoesWork\nTestObject { Component.onCompleted: test.foo = 14 }", QUrl()); QVERIFY(c.isReady()); QScopedPointer obj(c.create()); // The foo property is hidden now. QCOMPARE(obj->property("test").value().foo, 12); QQmlComponent c2(&engine); c2.setData("import QtQml\nimport DoesWork\n TestObject { Component.onCompleted: test.bar = 14 }", QUrl()); QVERIFY(c2.isReady()); QScopedPointer obj2(c2.create()); QCOMPARE(obj2->property("test").value().foo, 14); } } void tst_qqmllanguage::accessNullPointerPropertyCache() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("NullPointerPropertyCache.qml")); QVERIFY(c.isReady()); QScopedPointer obj(c.create()); QVERIFY(!obj.isNull()); } void tst_qqmllanguage::extendedNamespace() { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import StaticTest\n" "import QtQml\n" "ExtendedByNamespace {\n" " property int mine: own\n" " property int myEnum: ExtendedByNamespace.Moo\n" " property int fromExtension: ExtendedByNamespace.Bar\n" "}", QUrl()); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer obj(c.create()); QVERIFY(!obj.isNull()); QCOMPARE(obj->property("mine").toInt(), 93); QCOMPARE(obj->property("myEnum").toInt(), 16); QCOMPARE(obj->property("fromExtension").toInt(), 9); } void tst_qqmllanguage::factorySingleton() { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import StaticTest\n" "import QtQml\n" "QtObject {\n" " property int mine: FactorySingleton.foo\n" "}", QUrl()); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer obj(c.create()); QVERIFY(!obj.isNull()); QCOMPARE(obj->property("mine").toInt(), 314); } void tst_qqmllanguage::extendedSingleton() { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import StaticTest\n" "import QtQml\n" "QtObject {\n" " property int a: ExtendedSingleton.foo\n" " property int b: NamespaceExtendedSingleton.foo\n" " property int c: ExtendedSingleton.extension\n" " property int d: NamespaceExtendedSingleton.Bar\n" "}", QUrl()); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer obj(c.create()); QVERIFY(!obj.isNull()); QCOMPARE(obj->property("a").toInt(), 315); QCOMPARE(obj->property("b").toInt(), 316); QCOMPARE(obj->property("c").toInt(), 42); QCOMPARE(obj->property("d").toInt(), 9); } void tst_qqmllanguage::qtbug_85932() { QString warning1 = QLatin1String("%1:10:9: id is not unique").arg(testFileUrl("SingletonTest.qml").toString()); QString warning2 = QLatin1String("%1:4: Error: Due to the preceding error(s), Singleton \"SingletonTest\" could not be loaded.").arg(testFileUrl("qtbug_85932.qml").toString()); QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning2)); QQmlEngine engine; QList allWarnings; QObject::connect(&engine, &QQmlEngine::warnings, [&allWarnings](const QList &warnings) { allWarnings.append(warnings); }); QQmlComponent c(&engine, testFileUrl("qtbug_85932.qml")); QScopedPointer obj(c.create()); QTRY_COMPARE(allWarnings.count(), 2); QCOMPARE(allWarnings.at(0).toString(), warning1); QCOMPARE(allWarnings.at(1).toString(), warning2); } void tst_qqmllanguage::multiExtension() { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import StaticTest\nMultiExtension {}", QUrl()); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer o(c.create()); QCOMPARE(o->property("a").toInt(), int('a')); QCOMPARE(o->property("b").toInt(), int('b')); QCOMPARE(o->property("p").toInt(), int('p')); QCOMPARE(o->property("e").toInt(), int('e')); // Extension properties override base object properties QCOMPARE(o->property("c").toInt(), 12); QCOMPARE(o->property("d").toInt(), 22); QCOMPARE(o->property("f").toInt(), 31); QCOMPARE(o->property("g").toInt(), 44); } void tst_qqmllanguage::invalidInlineComponent() { QQmlEngine e; QQmlComponent c(&engine); c.setData("import QtQuick 2.0\n" "import QtQuick.Window 2.1\n" "Window {\n" " component TestPopup: Window {\n" " visibility: Window.Windowed\n" " }\n" " TestPopup { color: \"blue\" }\n" "}", QUrl()); QVERIFY(c.isError()); QVERIFY(c.errorString().contains("\"Window.visibility\" is not available in QtQuick 2.0.")); } void tst_qqmllanguage::qtbug_86482() { QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray(R"(import QtQml 2.0 import StaticTest QtObject { id: root property string result property StringSignaler str: StringSignaler { onSignal: function(value) { root.result = value; } } Component.onCompleted: str.call(); })"), QUrl()); VERIFY_ERRORS(0); QScopedPointer o(component.create()); QVERIFY2(component.isReady(), qPrintable(component.errorString())); QCOMPARE(o->property("result").toString(), QStringLiteral("Hello world!")); } QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc"