Revision control

Copy as Markdown

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmltc.h"
// Generated headers:
#include "ResolvedNameConflict.h"
#include "helloworld.h"
#include "simpleqtquicktypes.h"
#include "typewithenums.h"
#include "methods.h"
#if QT_CONFIG(quick_tableview)
#include "properties.h"
#endif
#include "objectwithid.h"
#if QT_CONFIG(quick_gridview)
#include "documentwithids.h"
#endif
#include "importnamespace.h"
#include "deferredproperties.h"
#include "deferredproperties_group.h"
#include "deferredproperties_attached.h"
#include "deferredproperties_complex.h"
#include "gradients.h"
#include "qjsvalueassignments.h"
#include "extensiontypebindings.h"
#include "qtbug103956_main.h"
#include "qtbug120700_main.h"
#include "nonstandardinclude.h"
#include "specialproperties.h"
#include "regexpbindings.h"
#include "aliasassignments.h"
#include "connections.h"
#include "signalhandlers.h"
#include "javascriptfunctions.h"
#include "changingbindings.h"
#include "propertyalias.h"
#include "propertyalias_external.h"
#include "propertyaliasattributes.h"
#include "complexaliases.h"
#include "propertychangehandler.h"
#include "nestedhelloworld.h"
#include "componenthelloworld.h"
#include "propertyreturningfunction.h"
#include "listproperty.h"
#include "listpropertysamename.h"
#include "defaultproperty.h"
#include "defaultpropertycorrectselection.h"
#include "attachedproperty.h"
#include "attachedpropertyderived.h"
#include "groupedproperty.h"
#include "groupedproperty_qquicktext.h"
#include "localimport.h"
#include "localimport_explicit.h"
#include "newpropertyboundtoold.h"
#include "oldpropertyboundtonew.h"
#include "nonlocalqmlpropertyboundtoany.h"
#include "localderived.h"
#include "justanimation.h"
#include "justanimationonalias.h"
#include "behaviorandanimation.h"
#include "behaviorandanimationonalias.h"
#include "bindingsthroughids.h"
#include "localimport_context.h"
#include "neighbors_context.h"
#include "delegate_context.h"
#include "nontrivial_context.h"
#include "javascriptcaller.h"
#include "listview.h"
#include "bindingonvaluetype.h"
#include "keyevents.h"
#include "privatepropertysubclass.h"
#include "calqlatrbits.h"
#include "propertychangeandsignalhandlers.h"
#include "valuetypelistproperty.h"
#include "translations.h"
#include "translationsbyid.h"
#include "defaultalias.h"
#include "generalizedgroupedproperty.h"
#include "appendtoqqmllistproperty.h"
#include "inlinecomponents.h"
#include "repeatercrash.h"
#include "aliases.h"
#include "inlinecomponentsfromdifferentfiles.h"
#include "helloexportedworld.h"
#include "helloexportedworldnofilename.h"
#include "testprivateproperty.h"
#include "singletons.h"
#include "mysignals.h"
#include "namespacedtypes.h"
#include "type.h"
#if QT_CONFIG(qml_labs) && QT_CONFIG(qml_table_model)
#include "qmltablemodel.h"
#endif
#include "stringtourl.h"
#include "signalconnections.h"
#include "requiredproperties.h"
#include "hpp.h"
#include "attachedcomponentproperty.h"
#include "attachednamespacedproperty.h"
#include "newlinetranslation.h"
#include "mymatryoshkaitems.h"
#include "typedmethodcallonidlookup.h"
// Qt:
#include <QtCore/qstring.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qurl.h>
#include <QtCore/qmetatype.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmllist.h>
#include <private/qqmltimer_p.h>
#include <QtTest/qsignalspy.h>
#include <QtGui/qquaternion.h>
#include <QtCore/private/qobject_p.h>
#include <QtTest/private/qemulationdetector_p.h>
#include <QtCore/qtranslator.h>
#include <private/qqmljslogger_p.h>
#include <private/qqmltcvisitor_p.h>
#ifndef QMLTC_TESTS_DISABLE_CACHE
# error "QMLTC_TESTS_DISABLE_CACHE is supposed to be defined and be equal to either 0 or 1"
#endif
#define PREPEND_NAMESPACE(name) QmltcTests::name // silent contract that the namespace is QmltcTest
using namespace Qt::StringLiterals;
tst_qmltc::tst_qmltc()
{
#if defined(QMLTC_TESTS_DISABLE_CACHE) && QMLTC_TESTS_DISABLE_CACHE
qputenv("QML_DISABLE_DISK_CACHE", "1");
#else
qputenv("QML_DISABLE_DISK_CACHE", "0");
#endif
}
void tst_qmltc::initTestCase()
{
const auto status = isCacheDisabled() ? u"DISABLED" : u"ENABLED";
qInfo() << u"Disk cache is" << status;
// Note: just check whether the QML code is valid. QQmlComponent is good for
// it. also, we can use qrc to make sure the file is in the resource system.
QUrl urls[] = {
QUrl(u"qrc:/qt/qml/QmltcTests/NameConflict.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/HelloWorld.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/simpleQtQuickTypes.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/typeWithEnums.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/methods.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/properties.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/ObjectWithId.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/documentWithIds.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/importNamespace.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/gradients.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/qjsvalueAssignments.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/deferredProperties.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/deferredProperties_group.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/deferredProperties_attached.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/deferredProperties_complex.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/extensionTypeBindings.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/nonStandardInclude.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/specialProperties.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/regexpBindings.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/AliasBase.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/aliasAssignments.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/Connections.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/qtbug103956/SubComponent.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/qtbug103956/MainComponent.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/qtbug103956/qtbug103956_main.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/qtbug120700_main.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/signalHandlers.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/javaScriptFunctions.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/changingBindings.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/propertyAlias.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/propertyAlias_external.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/complexAliases.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/propertyChangeHandler.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/NestedHelloWorld.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/ComponentHelloWorld.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/propertyReturningFunction.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/listProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/listPropertySameName.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/defaultProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/defaultPropertyCorrectSelection.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/AttachedProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/attachedPropertyDerived.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/groupedProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/groupedProperty_qquicktext.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/localImport.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/newPropertyBoundToOld.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/oldPropertyBoundToNew.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/nonLocalQmlPropertyBoundToAny.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/justAnimation.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/justAnimationOnAlias.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/behaviorAndAnimation.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/behaviorAndAnimationOnAlias.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/bindingsThroughIds.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/localImport_context.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/neighbors_context.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/delegate_context.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/nontrivial_context.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/javascriptCaller.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/listView.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/bindingOnValueType.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/keyEvents.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/PrivateProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/privatePropertySubclass.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/calqlatrBits.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/requiredProperties.qml"_s),
QUrl(u"qrc:/qt/qml/QmltcTests/typedMethodCallOnIdLookup.qml"_s),
};
QQmlEngine e;
QQmlComponent component(&e);
for (const auto &url : urls) {
component.loadUrl(url);
QVERIFY2(!component.isError(), qPrintable(u"Bad QML file. "_s + component.errorString()));
}
}
void tst_qmltc::qmlNameConflictResolution()
{
// we can include user-renamed files
QQmlEngine e;
// Note: the C++ class name is derived from the source qml file path, not
// the output .h/.cpp, so: NameConflict class name for NameConflict.qml
PREPEND_NAMESPACE(NameConflict) created(&e); // note: declared in ResolvedNameConflict.h
}
void tst_qmltc::helloWorld()
{
QQmlEngine e;
PREPEND_NAMESPACE(HelloWorld) created(&e);
QCOMPARE(created.hello(), u"Hello, World");
QCOMPARE(created.property("hello").toString(), QStringLiteral("Hello, World"));
QCOMPARE(created.greeting(), QStringLiteral("Hello, World!"));
QCOMPARE(created.property("greeting").toString(), QStringLiteral("Hello, World!"));
created.setProperty("hello", QStringLiteral("Hello, Qml"));
QCOMPARE(created.property("hello").toString(), QStringLiteral("Hello, Qml"));
QCOMPARE(created.property("greeting").toString(), QStringLiteral("Hello, Qml!"));
QCOMPARE(created.greeting(), QStringLiteral("Hello, Qml!"));
}
void tst_qmltc::qtQuickIncludes()
{
QQmlEngine e;
PREPEND_NAMESPACE(simpleQtQuickTypes) created(&e); // it should just compile as well
// since the file name is lower-case, let's also test that it's marked as
// QML_ANONYMOUS
const QMetaObject *mo = created.metaObject();
QVERIFY(mo);
QCOMPARE(mo->classInfo(mo->indexOfClassInfo("QML.Element")).value(), "anonymous");
}
void tst_qmltc::enumerations()
{
QQmlEngine e;
PREPEND_NAMESPACE(typeWithEnums) created(&e);
// sanity
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::NoValuesSpecified::A, 0);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::NoValuesSpecified::B, 1);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::NoValuesSpecified::C, 2);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::NoValuesSpecified::D, 3);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::A_, 1);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::B_, 2);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::B2_, 3);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::C_, 41);
QCOMPARE(PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::D_, 42);
const QMetaObject *mo = created.metaObject();
QVERIFY(mo);
const QMetaEnum enumerator1 = mo->enumerator(mo->indexOfEnumerator("NoValuesSpecified"));
QCOMPARE(enumerator1.enumName(), "NoValuesSpecified");
QCOMPARE(enumerator1.keyCount(), 4);
QCOMPARE(enumerator1.key(2), "C");
QCOMPARE(enumerator1.value(2), PREPEND_NAMESPACE(typeWithEnums)::NoValuesSpecified::C);
const QMetaEnum enumerator2 = mo->enumerator(mo->indexOfEnumerator("ValuesSpecified"));
QCOMPARE(enumerator2.enumName(), "ValuesSpecified");
QCOMPARE(enumerator2.keyCount(), 5);
QCOMPARE(enumerator2.key(2), "B2_");
QCOMPARE(enumerator2.value(2), PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::B2_);
}
void tst_qmltc::methods()
{
QQmlEngine e;
PREPEND_NAMESPACE(methods) created(&e);
const QMetaObject *mo = created.metaObject();
QVERIFY(mo);
QMetaMethod metaJustSignal = mo->method(mo->indexOfSignal("justSignal()"));
QMetaMethod metaTypedSignal = mo->method(mo->indexOfSignal(
QMetaObject::normalizedSignature("typedSignal(QString,QObject *,double)")));
QMetaMethod metaJustMethod = mo->method(mo->indexOfMethod("justMethod()"));
QMetaMethod metaUntypedMethod = mo->method(mo->indexOfMethod(
QMetaObject::normalizedSignature("untypedMethod(QVariant,QVariant)")));
QMetaMethod metaTypedMethod = mo->method(
mo->indexOfMethod(QMetaObject::normalizedSignature("typedMethod(double,int)")));
QVERIFY(metaJustSignal.isValid());
QVERIFY(metaTypedSignal.isValid());
QVERIFY(metaJustMethod.isValid());
QVERIFY(metaUntypedMethod.isValid());
QVERIFY(metaTypedMethod.isValid());
QCOMPARE(metaJustSignal.methodType(), QMetaMethod::Signal);
QCOMPARE(metaTypedSignal.methodType(), QMetaMethod::Signal);
QCOMPARE(metaJustMethod.methodType(), QMetaMethod::Method);
QCOMPARE(metaUntypedMethod.methodType(), QMetaMethod::Method);
QCOMPARE(metaTypedMethod.methodType(), QMetaMethod::Method);
QCOMPARE(metaTypedSignal.parameterMetaType(0), QMetaType::fromType<QString>());
QCOMPARE(metaTypedSignal.parameterMetaType(1), QMetaType::fromType<QObject *>());
QCOMPARE(metaTypedSignal.parameterMetaType(2), QMetaType::fromType<double>());
QCOMPARE(metaTypedSignal.parameterNames(), QList<QByteArray>({ "a", "b", "c" }));
QCOMPARE(metaUntypedMethod.parameterMetaType(0), QMetaType::fromType<QVariant>());
QCOMPARE(metaUntypedMethod.parameterMetaType(1), QMetaType::fromType<QVariant>());
QCOMPARE(metaUntypedMethod.parameterNames(), QList<QByteArray>({ "d", "c" }));
QCOMPARE(metaTypedMethod.parameterMetaType(0), QMetaType::fromType<double>());
QCOMPARE(metaTypedMethod.parameterMetaType(1), QMetaType::fromType<int>());
QCOMPARE(metaTypedMethod.returnMetaType(), QMetaType::fromType<QString>());
QCOMPARE(metaTypedMethod.parameterNames(), QList<QByteArray>({ "a", "b" }));
}
#if QT_CONFIG(quick_tableview)
void tst_qmltc::properties()
{
QQmlEngine e;
PREPEND_NAMESPACE(properties) created(&e); // check that it is creatable
// what we can do is compare the types via QMetaType
const QMetaObject *mo = created.metaObject();
QVERIFY(mo);
const auto propertyMetaType = [&](const char *propertyName) {
return mo->property(mo->indexOfProperty(propertyName)).metaType();
};
QCOMPARE(propertyMetaType("boolP"), QMetaType::fromType<bool>());
QCOMPARE(propertyMetaType("doubleP"), QMetaType::fromType<double>());
QCOMPARE(propertyMetaType("intP"), QMetaType::fromType<int>());
QCOMPARE(propertyMetaType("listQtObjP"), QMetaType::fromType<QQmlListProperty<QObject>>());
QCOMPARE(propertyMetaType("realP"), QMetaType::fromType<double>());
QCOMPARE(propertyMetaType("stringP"), QMetaType::fromType<QString>());
QCOMPARE(propertyMetaType("urlP"), QMetaType::fromType<QUrl>());
QCOMPARE(propertyMetaType("varP"), QMetaType::fromType<QVariant>());
QCOMPARE(propertyMetaType("nullObjP"), QMetaType::fromType<QObject *>());
QCOMPARE(propertyMetaType("nullVarP"), QMetaType::fromType<QVariant>());
QCOMPARE(propertyMetaType("varP"), QMetaType::fromType<QVariant>());
QCOMPARE(propertyMetaType("colorP"), QMetaType::fromType<QColor>());
QCOMPARE(propertyMetaType("dateP"), QMetaType::fromType<QDateTime>());
QCOMPARE(propertyMetaType("fontP"), QMetaType::fromType<QFont>());
QCOMPARE(propertyMetaType("matrix4x4P"), QMetaType::fromType<QMatrix4x4>());
QCOMPARE(propertyMetaType("pointP"), QMetaType::fromType<QPointF>());
QCOMPARE(propertyMetaType("quatP"), QMetaType::fromType<QQuaternion>());
QCOMPARE(propertyMetaType("rectP"), QMetaType::fromType<QRectF>());
QCOMPARE(propertyMetaType("sizeP"), QMetaType::fromType<QSizeF>());
QCOMPARE(propertyMetaType("vec2dP"), QMetaType::fromType<QVector2D>());
QCOMPARE(propertyMetaType("vec3dP"), QMetaType::fromType<QVector3D>());
QCOMPARE(propertyMetaType("vec4dP"), QMetaType::fromType<QVector4D>());
QCOMPARE(propertyMetaType("defaultObjP"), QMetaType::fromType<QObject *>());
// attributes:
QCOMPARE(mo->classInfo(mo->indexOfClassInfo("DefaultProperty")).value(), "defaultObjP");
QVERIFY(!mo->property(mo->indexOfProperty("readonlyStringP")).isWritable());
QVERIFY(mo->property(mo->indexOfProperty("requiredRealP")).isRequired());
// extra:
QCOMPARE(propertyMetaType("timerP"), QMetaType::fromType<QQmlTimer *>());
QCOMPARE(propertyMetaType("listCompP"), QMetaType::fromType<QQmlListProperty<QQmlComponent>>());
QCOMPARE(propertyMetaType("table"), QMetaType::fromType<QObject *>());
QCOMPARE(propertyMetaType("explicitCompP"), QMetaType::fromType<QObject *>());
QCOMPARE(propertyMetaType("sentinelForComponent"), QMetaType::fromType<QObject *>());
// now, test property values:
QCOMPARE(created.boolP(), true);
QCOMPARE(created.doubleP(), 0.5);
QCOMPARE(created.intP(), 42);
QCOMPARE(created.realP(), 2.32);
QCOMPARE(created.stringP(), u"hello, world"_s);
QCOMPARE(created.urlP(), QUrl(u"https://www.qt.io/"_s));
QCOMPARE(created.pointP(), QPoint(100, 200));
QCOMPARE(created.quatP(), QQuaternion(400, 100, 200, 300));
QCOMPARE(created.rectP(), QRectF(100, 200, 300, 400));
QCOMPARE(created.sizeP(), QSizeF(100, 200));
QCOMPARE(created.vec2dP(), QVector2D(100, 200));
QCOMPARE(created.vec3dP(), QVector3D(100, 200, 300));
QCOMPARE(created.vec4dP(), QVector4D(100, 200, 300, 400));
QCOMPARE(created.varP(), 42.42);
QCOMPARE(created.boolP(), true);
QCOMPARE(created.boolP(), true);
QCOMPARE(created.colorP(), QColor(u"blue"_s));
QCOMPARE(created.readonlyStringP(), u"foobar"_s);
// object bindinds:
const auto objectCtx = e.contextForObject(&created);
QQmlListReference listQtObj(&created, "listQtObjP");
QCOMPARE(listQtObj.size(), 3);
{
QQuickText *child0 = qobject_cast<QQuickText *>(listQtObj.at(0));
QVERIFY(child0);
QCOMPARE(child0->text(), u"child0"_s);
QCOMPARE(objectCtx->objectForName(u"listQtObjP_child_0"_s), child0);
QObject *child1 = listQtObj.at(1);
QVERIFY(child1);
QCOMPARE(child1->property("what").toString(), u"child1"_s);
QQuickItem *child2 = qobject_cast<QQuickItem *>(listQtObj.at(2));
QVERIFY(child2);
QQmlListReference data(child2, "data");
QCOMPARE(data.size(), 1);
QQuickRectangle *child2Rect = qobject_cast<QQuickRectangle *>(data.at(0));
QVERIFY(child2Rect);
QCOMPARE(objectCtx->objectForName(u"listQtObjP_child_2_rect"_s), child2Rect);
}
QQmlTimer *timer = created.timerP();
QVERIFY(timer);
QCOMPARE(timer->interval(), 42);
// nulls:
QCOMPARE(created.nullObjP(), nullptr);
QCOMPARE(created.nullVarP(), QVariant::fromValue(nullptr));
QQuickTableView *table = qobject_cast<QQuickTableView *>(created.table());
QVERIFY(table);
{
QQmlComponent *beforeDelegate = qvariant_cast<QQmlComponent *>(table->property("before"));
QVERIFY(beforeDelegate);
QQmlComponent *delegate = table->delegate();
QVERIFY(delegate);
QQmlComponent *afterDelegate = qvariant_cast<QQmlComponent *>(table->property("after"));
QVERIFY(afterDelegate);
QScopedPointer<QObject> beforeDelegateObject(beforeDelegate->create());
QVERIFY(beforeDelegateObject);
QVERIFY(qobject_cast<QQuickText *>(beforeDelegateObject.get()));
QCOMPARE(beforeDelegateObject->property("text").toString(), u"beforeDelegate"_s);
QScopedPointer<QObject> delegateObject(delegate->create());
QVERIFY(delegateObject);
QVERIFY(qobject_cast<QQuickText *>(delegateObject.get()));
QCOMPARE(delegateObject->property("text").toString(), u"delegate"_s);
QScopedPointer<QObject> afterDelegateObject(afterDelegate->create());
QVERIFY(afterDelegateObject);
QVERIFY(qobject_cast<QQuickText *>(afterDelegateObject.get()));
QCOMPARE(afterDelegateObject->property("text").toString(), u"afterDelegate"_s);
}
QQmlComponent *explicitComp = qobject_cast<QQmlComponent *>(created.explicitCompP());
QVERIFY(explicitComp);
QScopedPointer<QObject> explicitCompObject(explicitComp->create());
QVERIFY(explicitCompObject);
QVERIFY(qobject_cast<QQuickText *>(explicitCompObject.get()));
QCOMPARE(explicitCompObject->property("text").toString(), u"not a delegate"_s);
QObject *sentinelForComponent = created.sentinelForComponent();
QVERIFY(sentinelForComponent);
QCOMPARE(sentinelForComponent->property("text").toString(), u"should be correctly created"_s);
}
#endif // QT_CONFIG(quick_tableview)
#if QT_CONFIG(quick_tableview) && QT_CONFIG(quick_gridview)
void tst_qmltc::ids()
{
{
QQmlEngine e;
PREPEND_NAMESPACE(ObjectWithId) created(&e); // shouldn't crash here
auto objectCtx = QQmlContextData::get(e.contextForObject(&created));
QVERIFY(objectCtx);
QCOMPARE(objectCtx->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(objectCtx->asQQmlContext()->objectForName(u"objectWithId"_s), &created);
QCOMPARE(objectCtx->contextObject(), &created);
}
{
QQmlEngine e;
PREPEND_NAMESPACE(documentWithIds) created(&e); // shouldn't crash here
auto ctx = e.contextForObject(&created);
QVERIFY(ctx);
auto objectCtx = QQmlContextData::get(ctx);
QVERIFY(objectCtx);
QCOMPARE(objectCtx->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(objectCtx->contextObject(), &created);
// first check that ids match object names
const auto objectNameById = [&ctx](const QString &id) {
auto object = ctx->objectForName(id);
if (!object)
return QString();
return object->objectName();
};
QCOMPARE(objectNameById(u"rectangle"_s), u"rectangle"_s);
QCOMPARE(objectNameById(u"row"_s), u"row"_s);
QCOMPARE(objectNameById(u"textInRectangle"_s), u"textInRectangle"_s);
QCOMPARE(objectNameById(u"itemInList"_s), u"itemInList"_s);
QCOMPARE(objectNameById(u"objectInList"_s), u"objectInList"_s);
QCOMPARE(objectNameById(u"item"_s), u"item"_s);
QCOMPARE(objectNameById(u"gridView"_s), u"gridView"_s);
QCOMPARE(objectNameById(u"tableView"_s), u"tableView"_s);
QCOMPARE(objectNameById(u"sentinel"_s), u"sentinel"_s);
const auto verifyComponent = [&](QQmlComponent *component, const QString &componentId,
const QString &objectId) {
QVERIFY(component);
if (!componentId.isEmpty()) // empty string for implicit components
QCOMPARE(ctx->objectForName(componentId), component);
QCOMPARE(ctx->objectForName(objectId), nullptr);
QScopedPointer<QObject> root(component->create());
QCOMPARE(root->objectName(), objectId);
auto rootCtx = e.contextForObject(root.get());
QVERIFY(rootCtx);
QCOMPARE(rootCtx->objectForName(objectId), root.get());
};
auto explicitComponent = qobject_cast<QQmlComponent *>(created.explicitCompProperty());
verifyComponent(explicitComponent, u"explicitComponent"_s, u"explicitText"_s);
QQmlListReference children(&created, "data");
QCOMPARE(children.size(), 2);
QQuickTableView *table = qobject_cast<QQuickTableView *>(children.at(1));
QVERIFY(table);
QCOMPARE(ctx->objectForName(u"tableView"_s), table);
QCOMPARE(table->objectName(), u"tableView"_s);
auto before = qvariant_cast<QQmlComponent *>(table->property("before"));
verifyComponent(before, u"beforeDelegate"_s, u"beforeDelegateText"_s);
auto after = qvariant_cast<QQmlComponent *>(table->property("after"));
verifyComponent(after, u"afterDelegate"_s, u"afterDelegateText"_s);
auto delegate = table->delegate();
verifyComponent(delegate, /* implicit component */ QString(), u"delegateRect"_s);
// TableView is really special when you add Component to a default
// property. see QQuickFlickablePrivate::data_append
QQmlComponent *beforeChild = nullptr;
QQmlComponent *afterChild = nullptr;
const auto tableChildren = table->children(); // QObject::children()
QVERIFY(tableChildren.size() >= 2);
for (QObject *child : tableChildren) {
auto comp = qobject_cast<QQmlComponent *>(child);
if (!comp)
continue;
// this is bad, but there doesn't seem to be any better choice
if (ctx->objectForName(u"beforeDelegateDefaultProperty"_s) == comp)
beforeChild = comp;
else if (ctx->objectForName(u"afterDelegateDefaultProperty"_s) == comp)
afterChild = comp;
}
// we just used ctx->objectForName() to find these components, so
// there's no point in checking the same condition in verifyComponent()
verifyComponent(beforeChild, QString(), u"beforeDelegateDefaultPropertyText"_s);
verifyComponent(afterChild, QString(), u"afterDelegateDefaultPropertyText"_s);
}
}
#endif // QT_CONFIG(quick_tableview) && QT_CONFIG(quick_gridview)
void tst_qmltc::importNamespace()
{
QQmlEngine e;
PREPEND_NAMESPACE(importNamespace) created(&e); // compilation of this type shouldn't crash
QCOMPARE(created.text(), u"hello, world"_s);
}
void tst_qmltc::deferredProperties()
{
{
QQmlEngine e;
PREPEND_NAMESPACE(deferredProperties) created(&e);
QVERIFY(created.deferredProperty()
== nullptr); // binding is not applied since it is deferred
qmlExecuteDeferred(&created);
QQuickRectangle *rect = qobject_cast<QQuickRectangle *>(created.deferredProperty());
QVERIFY(rect);
QCOMPARE(rect->width(), created.width() * 2);
QCOMPARE(rect->implicitHeight(), 4);
QCOMPARE(rect->height(), rect->implicitHeight());
QQmlListReference children(rect, "data");
QCOMPARE(children.size(), 1);
QQuickRectangle *subRect = qobject_cast<QQuickRectangle *>(children.at(0));
QVERIFY(subRect);
QCOMPARE(subRect->width(), created.width());
QCOMPARE(subRect->height(), rect->height());
}
{
QQmlEngine e;
PREPEND_NAMESPACE(deferredProperties_group) created(&e);
QCOMPARE(created.getGroup()->getStr(), u"foobar"_s);
QCOMPARE(created.getGroup()->getDeferred(), 0);
// Note: we can't easily evaluate a deferred binding for a
// `group.deferred` here, so just accept the fact the the value is not
// set at all as a successful test
}
{
QQmlEngine e;
PREPEND_NAMESPACE(deferredProperties_attached) created(&e);
TestTypeAttachedWithDeferred *attached = qobject_cast<TestTypeAttachedWithDeferred *>(
qmlAttachedPropertiesObject<DeferredAttached>(&created, false));
QVERIFY(attached);
QCOMPARE(attached->getAttachedFormula(), 43);
QCOMPARE(attached->getDeferred(), 0);
// Note: we can't easily evaluate a deferred binding for a
// `group.deferred` here, so just accept the fact the the value is not
// set at all as a successful test
}
{
QQmlEngine e;
PREPEND_NAMESPACE(deferredProperties_complex) created(&e);
// `group` binding is not deferred as per current behavior outside of
// PropertyChanges and friends. we defer `group.deferred` binding though
QCOMPARE(created.getGroup()->getStr(), u"still immediate"_s);
QCOMPARE(created.getGroup()->getDeferred(), 0);
QVERIFY(!qmlAttachedPropertiesObject<DeferredAttached>(&created, false));
qmlExecuteDeferred(&created);
TestTypeAttachedWithDeferred *attached = qobject_cast<TestTypeAttachedWithDeferred *>(
qmlAttachedPropertiesObject<DeferredAttached>(&created, false));
QVERIFY(attached);
QCOMPARE(attached->getAttachedFormula(), 20);
QCOMPARE(attached->getDeferred(), 100);
}
}
// QTBUG-102560
void tst_qmltc::gradients()
{
QQmlEngine e;
PREPEND_NAMESPACE(gradients) created(&e);
QQmlListReference children(&created, "data");
QCOMPARE(children.size(), 1);
QQuickRectangle *rect = qobject_cast<QQuickRectangle *>(children.at(0));
QVERIFY(rect);
QVERIFY(rect->gradient().isQObject());
QQuickGradient *gradient = qobject_cast<QQuickGradient *>(rect->gradient().toQObject());
QVERIFY(gradient);
QQmlListProperty<QQuickGradientStop> stops = gradient->stops();
QCOMPARE(stops.count(&stops), 2);
QQuickGradientStop *stop0 = stops.at(&stops, 0);
QVERIFY(stop0);
QQuickGradientStop *stop1 = stops.at(&stops, 1);
QVERIFY(stop1);
QCOMPARE(stop0->position(), 0.0);
QCOMPARE(stop1->position(), 1.0);
QCOMPARE(stop0->color(), QColor::fromString("black"));
QCOMPARE(stop1->color(), QColor::fromString("yellow"));
}
void tst_qmltc::jsvalueAssignments()
{
QQmlEngine e;
PREPEND_NAMESPACE(qjsvalueAssignments) created(&e);
QVERIFY(created.jsvalue().isBool());
QVERIFY(created.jsvalue().toBool());
}
void tst_qmltc::extensionTypeBindings()
{
const auto verifyExtensionType = [](QObject *root) {
QQmlListReference data(root, "data");
QCOMPARE(data.count(), 9);
// NB: Text object is not at index 0 due to non-QQuickItem-derived types
// added along with it. This has something to do with QQuickItem's
// internals that we just accept here
auto text = qobject_cast<QQuickText *>(data.at(8));
QVERIFY(text);
auto withExtension = qobject_cast<TypeWithExtension *>(data.at(0));
QVERIFY(withExtension);
auto withExtensionDerived = qobject_cast<TypeWithExtensionDerived *>(data.at(1));
QVERIFY(withExtensionDerived);
auto withExtensionNamespace = qobject_cast<TypeWithExtensionNamespace *>(data.at(2));
QVERIFY(withExtensionNamespace);
// extra:
auto withBaseTypeExtension = qobject_cast<TypeWithBaseTypeExtension *>(data.at(3));
QVERIFY(withBaseTypeExtension);
// qml:
auto qmlWithExtension = qobject_cast<TypeWithExtension *>(data.at(4));
QVERIFY(qmlWithExtension);
auto qmlWithBaseTypeExtension = qobject_cast<TypeWithBaseTypeExtension *>(data.at(5));
QVERIFY(qmlWithBaseTypeExtension);
// script bindings:
auto withExtensionDerivedScript = qobject_cast<TypeWithExtensionDerived *>(data.at(6));
QVERIFY(withExtensionDerivedScript);
auto withExtensionNamespaceScript = qobject_cast<TypeWithExtensionNamespace *>(data.at(7));
QVERIFY(withExtensionNamespaceScript);
QFont font = text->font();
QCOMPARE(font.letterSpacing(), 13);
QCOMPARE(withExtension->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(root->property("extCountAlias").toInt(), -10);
root->setProperty("extCountAlias", 42);
QCOMPARE(withExtension->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(root->property("extCountAlias").toInt(), 42);
QVERIFY(withExtension->property("shouldBeVisible").toBool());
QCOMPARE(withExtension->property("foo").toDouble(), 0);
QCOMPARE(withExtensionDerived->getStr(), TypeWithExtensionDerived::unsetStr);
QCOMPARE(withExtensionDerived->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(root->property("extDerivedStrAlias").toString(), u"hooray"_s);
QCOMPARE(root->property("extDerivedCountAlias").toInt(), -10);
// taken from extension
QCOMPARE(withExtensionDerived->property("str").toString(), u"hooray"_s);
QCOMPARE(withExtensionDerived->property("count").toInt(), -10);
root->setProperty("extDerivedStrAlias", u"foo"_s);
root->setProperty("extDerivedCountAlias", 42);
QCOMPARE(withExtensionDerived->getStr(), TypeWithExtensionDerived::unsetStr);
QCOMPARE(withExtensionDerived->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(root->property("extDerivedStrAlias").toString(), u"foo"_s);
QCOMPARE(root->property("extDerivedCountAlias").toInt(), 42);
QCOMPARE(withExtensionDerived->property("str").toString(), u"foo"_s);
QCOMPARE(withExtensionDerived->property("count").toInt(), 42);
QVERIFY(withExtensionDerived->property("shouldBeVisible").toBool());
// namespace properties are ignored
QCOMPARE(withExtensionNamespace->getCount(), -10);
QCOMPARE(root->property("extNamespaceCountAlias").toInt(), -10);
root->setProperty("extNamespaceCountAlias", 42);
QCOMPARE(withExtensionNamespace->getCount(), 42);
QCOMPARE(root->property("extNamespaceCountAlias").toInt(), 42);
QVERIFY(withExtensionNamespace->property("shouldBeVisible").toBool());
// extra:
QCOMPARE(withBaseTypeExtension->getStr(), TypeWithExtensionDerived::unsetStr);
QCOMPARE(withBaseTypeExtension->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(withBaseTypeExtension->property("str").toString(), u"hooray"_s);
QCOMPARE(withBaseTypeExtension->property("count").toInt(), -10);
QVERIFY(withBaseTypeExtension->property("shouldBeVisible").toBool());
// qml:
QCOMPARE(qmlWithExtension->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(qmlWithExtension->property("count"), -10);
QVERIFY(qmlWithExtension->property("shouldBeVisibleFromBase").toBool());
QVERIFY(qmlWithExtension->property("shouldBeVisible").toBool());
QCOMPARE(qmlWithBaseTypeExtension->getStr(), TypeWithExtensionDerived::unsetStr);
QCOMPARE(qmlWithBaseTypeExtension->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(qmlWithBaseTypeExtension->property("str").toString(), u"hooray"_s);
QCOMPARE(qmlWithBaseTypeExtension->property("count").toInt(), -10);
QVERIFY(qmlWithBaseTypeExtension->property("shouldBeVisibleFromBase").toBool());
QVERIFY(qmlWithBaseTypeExtension->property("shouldBeVisible").toBool());
// script bindings:
QCOMPARE(withExtensionDerivedScript->getStr(), TypeWithExtensionDerived::unsetStr);
QCOMPARE(withExtensionDerivedScript->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(withExtensionDerivedScript->property("str").toString(), u"hooray"_s);
QCOMPARE(withExtensionDerivedScript->property("count").toInt(), -10);
QCOMPARE(withExtensionNamespaceScript->getCount(), -10);
QCOMPARE(withExtensionNamespaceScript->property("count").toInt(), -10);
};
{
QQmlEngine e;
QQmlComponent component(&e);
component.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/extensionTypeBindings.qml"_s));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> root(component.create());
QVERIFY2(root, qPrintable(component.errorString()));
verifyExtensionType(root.get());
}
if (QTest::currentTestFailed()) {
qDebug() << "QQmlComponent test failed";
return;
}
{
QQmlEngine e;
PREPEND_NAMESPACE(extensionTypeBindings) created(&e);
verifyExtensionType(&created);
// additionally, check that setting aliases directly works fine
QQmlListReference data(&created, "data");
auto withExtension = qobject_cast<TypeWithExtension *>(data.at(0));
auto withExtensionDerived = qobject_cast<TypeWithExtensionDerived *>(data.at(1));
auto withExtensionNamespace = qobject_cast<TypeWithExtensionNamespace *>(data.at(2));
created.setExtCountAlias(-77);
QCOMPARE(withExtension->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(withExtension->property("count").toInt(), -77); // via extension
QCOMPARE(created.extCountAlias(), -77);
created.setExtDerivedCountAlias(-77);
created.setExtDerivedStrAlias(u"bar"_s);
QCOMPARE(withExtensionDerived->getCount(), TypeWithExtension::unsetCount);
QCOMPARE(withExtensionDerived->getStr(), TypeWithExtensionDerived::unsetStr);
QCOMPARE(withExtensionDerived->property("count").toInt(), -77); // via extension
QCOMPARE(withExtensionDerived->property("str").toString(), u"bar"_s); // via extension
QCOMPARE(created.extDerivedCountAlias(), -77);
QCOMPARE(created.extDerivedStrAlias(), u"bar"_s);
created.setExtNamespaceCountAlias(-77);
QCOMPARE(withExtensionNamespace->getCount(), -77);
QCOMPARE(withExtensionNamespace->property("count").toInt(), -77);
}
}
// QTBUG-103956
void tst_qmltc::visibleAliasMethods()
{
QQmlEngine e;
PREPEND_NAMESPACE(qtbug103956_main) created(&e);
QVERIFY(created.firstComponent());
QCOMPARE(created.firstComponent()->setMe(), true);
}
// QTBUG-120700
void tst_qmltc::customInitialization()
{
int valueToTest = 10;
QQuickItem firstItem;
QQuickItem secondItem;
QQmlEngine e;
PREPEND_NAMESPACE(qtbug120700_main)
created(&e, {valueToTest} ,nullptr, [valueToTest, &firstItem, &secondItem](auto& component) {
component.setSomeComplexValueThatWillBeSet(valueToTest);
component.setPropertyFromExtension(static_cast<double>(valueToTest));
component.setDefaultedBindable(static_cast<double>(valueToTest));
component.setValueTypeList({1, 2, 3, 4});
component.setObjectTypeList({&firstItem, &secondItem});
component.setExtensionObjectList({&firstItem, &secondItem});
component.setCppObjectList({&firstItem, &secondItem});
});
// QTBUG-114403: onValueChanged should have not been triggered
// when setting the initial value for the property.
// If this is true then the handler was called.
QCOMPARE(created.wasSomeValueChanged(), false);
// someComplexValueThatWillBeSet is set through a binding in the
// QML code, but is initialized when the instance is created.
// The bindings, which is generally set after the custom
// initialization was perfomed, should not overwrite the initial
// value that the user provided.
// On the other side, someComplexValueThatWillNotBeSet should
// still respect the original binding as an initial value for it
// was not provided.
QCOMPARE(created.someComplexValueThatWillBeSet(), valueToTest);
QCOMPARE(created.someComplexValueThatWillNotBeSet(), 5);
QCOMPARE(created.someValue(), valueToTest);
QCOMPARE(created.someValueAlias(), valueToTest);
QCOMPARE(created.someValueBinding(), valueToTest + 1);
QCOMPARE(created.property("propertyFromExtension").toDouble(), static_cast<double>(valueToTest));
QCOMPARE(created.bindableDefaultedBindable().value(), static_cast<double>(valueToTest));
QCOMPARE(created.valueTypeList(), QList({1, 2, 3, 4}));
QCOMPARE(created.objectTypeList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem}));
QCOMPARE(
created.property("extensionObjectList").value<QQmlListProperty<QQuickItem>>().toList<QList<QQuickItem*>>(),
QList({&firstItem, &secondItem})
);
QCOMPARE(created.getCppObjectList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem}));
}
void tst_qmltc::requiredPropertiesInitialization()
{
QQuickItem item{};
int aliasToInnerThatWillBeMarkedRequired = 10;
int aliasToPropertyThatShadows = 42;
int aliasToRequiredInner = 11;
QQuickItem inheritedRequiredProperty{};
int nonRequiredInheritedPropertyThatWillBeMarkedRequired = 12;
QList<QQuickItem*> objectList{&item};
int primitiveType = 13;
int propertyThatWillBeMarkedRequired = 14;
int requiredAliasToUnrequiredProperty = 15;
double requiredPropertyFromExtension = 16.0;
QList<int> valueList{1, 2, 3, 4};
QQmlEngine e;
PREPEND_NAMESPACE(requiredProperties) created(
&e,
{
aliasToInnerThatWillBeMarkedRequired,
aliasToPropertyThatShadows,
aliasToRequiredInner,
&inheritedRequiredProperty,
nonRequiredInheritedPropertyThatWillBeMarkedRequired,
objectList,
primitiveType,
propertyThatWillBeMarkedRequired,
requiredAliasToUnrequiredProperty,
requiredPropertyFromExtension,
valueList
}
);
QCOMPARE(created.aliasToInnerThatWillBeMarkedRequired(), aliasToInnerThatWillBeMarkedRequired);
QCOMPARE(created.aliasToPropertyThatShadows(), aliasToPropertyThatShadows);
QCOMPARE(created.aliasToRequiredInner(), aliasToRequiredInner);
QCOMPARE(created.getInheritedRequiredProperty(), &inheritedRequiredProperty);
QCOMPARE(created.getNonRequiredInheritedPropertyThatWillBeMarkedRequired(), nonRequiredInheritedPropertyThatWillBeMarkedRequired);
QCOMPARE(created.objectList().toList<QList<QQuickItem*>>(), objectList);
QCOMPARE(created.primitiveType(), primitiveType);
QCOMPARE(created.propertyThatWillBeMarkedRequired(), propertyThatWillBeMarkedRequired);
QCOMPARE(created.requiredAliasToUnrequiredProperty(), requiredAliasToUnrequiredProperty);
QCOMPARE(created.property("requiredPropertyFromExtension").toDouble(), requiredPropertyFromExtension);
QCOMPARE(created.valueList(), valueList);
}
// QTBUG-104094
void tst_qmltc::nonStandardIncludesInsideModule()
{
QQmlEngine e;
PREPEND_NAMESPACE(nonStandardInclude) created(&e);
QVERIFY(created.good());
}
void tst_qmltc::specialProperties()
{
QQmlEngine e;
PREPEND_NAMESPACE(specialProperties) created(&e);
QCOMPARE(created.property("x"), 42);
QCOMPARE(created.m_y, u"fourty two"_s);
QCOMPARE(created.bindableZ().value(), 3.2);
QCOMPARE(created.xAlias(), 42);
QCOMPARE(created.yAlias(), u"fourty two"_s);
QCOMPARE(created.zAlias(), 3.2);
created.setXAlias(43);
QCOMPARE(created.property("x"), 43);
created.setYAlias(u"foo"_s);
QCOMPARE(created.m_y, u"foo"_s);
created.setZAlias(4.2);
QCOMPARE(created.bindableZ().value(), 4.2);
// alias attributes:
const QMetaObject *mo = created.metaObject();
QMetaProperty xxAlias = mo->property(mo->indexOfProperty("xxAlias"));
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/specialProperties.qml"_s));
QScopedPointer<QObject> _fromEngine(c.create());
QVERIFY2(_fromEngine, qPrintable(c.errorString()));
QObject &fromEngine = *_fromEngine;
const QMetaObject *fromEngineMetaObject = fromEngine.metaObject();
QMetaProperty xxAliasFromEngine =
fromEngineMetaObject->property(mo->indexOfProperty("xxAlias"));
QVERIFY(xxAlias.isValid());
QVERIFY(xxAliasFromEngine.isValid());
QCOMPARE(xxAlias.isConstant(), xxAliasFromEngine.isConstant());
QCOMPARE(created.xyAlias(), u"reset");
}
void tst_qmltc::regexpBindings()
{
QQmlEngine e;
PREPEND_NAMESPACE(regexpBindings) created(&e);
QCOMPARE(created.regularExpression().pattern(), u"ab*c");
QVERIFY(created.regularExpression().match(u"abbbc"_s).hasMatch());
}
void tst_qmltc::aliasAssignments()
{
{
QQmlEngine e;
PREPEND_NAMESPACE(AliasBase) created(&e);
QCOMPARE(created.alias1(), 2);
}
{
QQmlEngine e;
PREPEND_NAMESPACE(aliasAssignments) created(&e);
QCOMPARE(created.alias1(), 4);
QCOMPARE(created.alias2(), 4);
}
}
void tst_qmltc::connections()
{
QQmlEngine e;
PREPEND_NAMESPACE(Connections) created(&e);
}
void tst_qmltc::signalHandlers()
{
QQmlEngine e;
PREPEND_NAMESPACE(signalHandlers) created(&e);
// signal emission works from C++
emit created.signal1();
emit created.signal2(QStringLiteral("42"), 42);
QCOMPARE(created.property("signal1P").toInt(), 1);
QCOMPARE(created.property("signal2P1").toString(), QStringLiteral("42"));
QCOMPARE(created.property("signal2P2").toInt(), 42);
QCOMPARE(created.property("signal2P3").toString(), QStringLiteral("4242"));
// signal emission works through meta object system
QMetaObject::invokeMethod(&created, "signal1");
QMetaObject::invokeMethod(&created, "signal2", Q_ARG(QString, QStringLiteral("foo")),
Q_ARG(int, 23));
QCOMPARE(created.property("signal1P").toInt(), 2);
QCOMPARE(created.property("signal2P1").toString(), QStringLiteral("foo"));
QCOMPARE(created.property("signal2P2").toInt(), 23);
QCOMPARE(created.property("signal2P3").toString(), QStringLiteral("foo23"));
// signal emission works through QML/JS
created.qmlEmitSignal1();
created.qmlEmitSignal2();
QCOMPARE(created.property("signal1P").toInt(), 3);
QCOMPARE(created.property("signal2P1").toString(), QStringLiteral("xyz"));
QCOMPARE(created.property("signal2P2").toInt(), 123);
QCOMPARE(created.property("signal2P3").toString(), QStringLiteral("xyz123"));
created.qmlEmitSignal2WithArgs(QStringLiteral("abc"), 0);
QCOMPARE(created.property("signal2P1").toString(), QStringLiteral("abc"));
QCOMPARE(created.property("signal2P2").toInt(), 0);
QCOMPARE(created.property("signal2P3").toString(), QStringLiteral("abc0"));
}
void tst_qmltc::jsFunctions()
{
QQmlEngine e;
PREPEND_NAMESPACE(javaScriptFunctions) created(&e);
created.func1();
created.func2(QStringLiteral("abc"));
QCOMPARE(created.property("func1P").toInt(), 1);
QCOMPARE(created.property("func2P").toString(), QStringLiteral("abc"));
QCOMPARE(created.func3(), false);
created.setProperty("func3P", true);
QCOMPARE(created.func3(), true);
}
void tst_qmltc::changingBindings()
{
QQmlEngine e;
PREPEND_NAMESPACE(changingBindings) created(&e);
// test initial binding
QCOMPARE(created.property("p2").toInt(), 2); // p1 + 1
// test JS constant value
created.resetToConstant();
QCOMPARE(created.property("p2").toInt(), 42); // 42
// test Qt.binding()
created.resetToNewBinding();
created.setProperty("p1", 100);
QCOMPARE(created.property("p2").toInt(), 200); // p1 * 2
// test resetting value through C++
created.setP2(0);
created.setP1(-10);
QCOMPARE(created.property("p2").toInt(), 0);
created.setProperty("p2", 1);
QCOMPARE(created.property("p2").toInt(), 1);
// test binding can be set again even after reset from C++
created.resetToNewBinding();
QCOMPARE(created.property("p2").toInt(), -20);
}
void tst_qmltc::propertyAlias()
{
QQmlEngine e;
PREPEND_NAMESPACE(propertyAlias) created(&e);
// test initial binding
QCOMPARE(created.property("origin").toInt(), 6); // dummy / 2
QCOMPARE(created.property("aliasToOrigin").toInt(), 6);
QCOMPARE(created.getAliasValue().toInt(), 6);
QCOMPARE(created.aliasToOrigin(), 6);
created.setDummy(10);
QCOMPARE(created.property("aliasToOrigin").toInt(), 5);
QCOMPARE(created.getAliasValue().toInt(), 5);
QCOMPARE(created.aliasToOrigin(), 5);
// test the C++ setter
created.setOrigin(7);
QCOMPARE(created.property("aliasToOrigin").toInt(), 7);
QCOMPARE(created.getAliasValue().toInt(), 7);
QCOMPARE(created.aliasToOrigin(), 7);
// test meta-object setter
created.setProperty("origin", 1);
QCOMPARE(created.property("aliasToOrigin").toInt(), 1);
QCOMPARE(created.getAliasValue().toInt(), 1);
QCOMPARE(created.aliasToOrigin(), 1);
// test QML/JS setter
created.resetOriginToConstant();
QCOMPARE(created.property("aliasToOrigin").toInt(), 189);
QCOMPARE(created.getAliasValue().toInt(), 189);
QCOMPARE(created.aliasToOrigin(), 189);
// test QML/JS alias setter
created.resetAliasToConstant();
QCOMPARE(created.property("origin").toInt(), 42);
QCOMPARE(created.origin(), 42);
// check the alias just to make sure it also works
QCOMPARE(created.property("aliasToOrigin").toInt(), 42);
QCOMPARE(created.getAliasValue().toInt(), 42);
QCOMPARE(created.aliasToOrigin(), 42);
// test QML/JS binding reset
created.resetOriginToNewBinding(); // dummy
created.setDummy(99);
QCOMPARE(created.property("aliasToOrigin").toInt(), 99);
QCOMPARE(created.getAliasValue().toInt(), 99);
QCOMPARE(created.aliasToOrigin(), 99);
// test QML/JS binding reset through alias
created.resetAliasToNewBinding(); // dummy * 3
created.setDummy(-8);
QCOMPARE(created.property("origin").toInt(), -24);
QCOMPARE(created.origin(), -24);
QCOMPARE(created.property("aliasToOrigin").toInt(), -24);
QCOMPARE(created.getAliasValue().toInt(), -24);
QCOMPARE(created.aliasToOrigin(), -24);
}
void tst_qmltc::propertyAlias_external()
{
QQmlEngine e;
PREPEND_NAMESPACE(propertyAlias_external) created(&e);
QCOMPARE(created.height(), 1);
QCOMPARE(created.height(), created.heightAlias());
created.setHeightAlias(42);
QCOMPARE(created.height(), 42);
QCOMPARE(created.height(), created.heightAlias());
QCOMPARE(created.parentItem(), nullptr);
QCOMPARE(created.parentAlias(), created.parentItem());
QSignalSpy heightChangedSpy(&created, &QQuickItem::heightChanged);
QSignalSpy heightAliasChangedSpy(&created, &PREPEND_NAMESPACE(propertyAlias_external)::heightAliasChanged);
created.setHeight(10);
QCOMPARE(created.heightAlias(), 10);
QCOMPARE(heightChangedSpy.size(), 1);
QCOMPARE(heightAliasChangedSpy.size(), 1);
}
void tst_qmltc::propertyAliasAttribute()
{
QQmlEngine e;
PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e, {u""_s});
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/propertyAliasAttributes.qml"_s));
QScopedPointer<QObject> _fromEngine(c.create());
QVERIFY2(_fromEngine, qPrintable(c.errorString()));
QObject &fromEngine = *_fromEngine;
const QMetaObject *fromEngineMetaObject = fromEngine.metaObject();
const QString stringA = u"The quick brown fox"_s;
const QString stringB = u"jumps over the lazy dog."_s;
QCOMPARE(fromQmltc.readOnlyAlias(), u"Hello World!"_s);
QCOMPARE(fromEngine.property("readOnlyAlias"), u"Hello World!"_s);
QVERIFY(!fromQmltc.setProperty("readOnlyAlias", u"Some string"_s));
QVERIFY(!fromEngine.setProperty("readOnlyAlias", u"Some string"_s));
// reading and writing from alias is already covered in the alias test
// check if it works on properties with the MEMBERS attribute
fromQmltc.setReadAndWriteMemberAlias(stringA);
fromEngine.setProperty("readAndWriteMemberAlias", stringA);
QCOMPARE(fromQmltc.property("readAndWriteMember"), stringA);
QCOMPARE(fromEngine.property("readAndWriteMember"), stringA);
fromQmltc.setReadAndWriteMemberAlias(stringB);
fromEngine.setProperty("readAndWriteMemberAlias", stringB);
QCOMPARE(fromQmltc.readAndWriteMemberAlias(), stringB);
QCOMPARE(fromQmltc.property("readAndWriteMember"), stringB);
QCOMPARE(fromEngine.property("readAndWriteMemberAlias"), stringB);
QCOMPARE(fromEngine.property("readAndWriteMember"), stringB);
// check if alias can be reset through property
fromQmltc.setResettableAlias(stringA);
fromEngine.setProperty("resettableAlias", stringB);
fromQmltc.resetResettable();
const int resettableIdx = fromEngineMetaObject->indexOfProperty("resettable");
QVERIFY(fromEngineMetaObject->property(resettableIdx).reset(&fromEngine));
QCOMPARE(fromQmltc.resettable(), u"Reset!"_s);
QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s);
QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s);
QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s);
// check if property can be reset through alias
fromQmltc.setResettableAlias(stringA);
fromEngine.setProperty("resettableAlias", stringA);
fromQmltc.resetResettableAlias();
QMetaMethod resetResettableAlias = fromEngineMetaObject->method(
fromEngineMetaObject->indexOfMethod("resetResettableAlias"));
resetResettableAlias.invoke(&fromEngine);
QCOMPARE(fromQmltc.resettable(), u"Reset!"_s);
QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s);
QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s);
QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s);
// check if property can be reset by assigning undefined to alias
fromQmltc.setResettableAlias(stringA);
fromEngine.setProperty("resettableAlias", stringA);
fromQmltc.assignUndefinedToResettableAlias();
QMetaMethod assignUndefinedToResettableAlias = fromEngineMetaObject->method(
fromEngineMetaObject->indexOfMethod("assignUndefinedToResettableAlias"));
assignUndefinedToResettableAlias.invoke(&fromEngine);
QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s);
QCOMPARE(fromQmltc.resettable(), u"Reset!"_s);
QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s);
QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s);
// check if property can be reset by assigning undefined to alias of
// non-resettable prop which should not happen: instead, nothing should happen
fromQmltc.setUnresettableAlias(stringA);
fromEngine.setProperty("unresettableAlias", stringA);
fromQmltc.assignUndefinedToUnresettableAlias();
QMetaMethod assignUndefinedToUnresettableAlias = fromEngineMetaObject->method(
fromEngineMetaObject->indexOfMethod("assignUndefinedToUnresettableAlias"));
assignUndefinedToUnresettableAlias.invoke(&fromEngine);
QCOMPARE(fromQmltc.unresettableAlias(), stringA);
QCOMPARE(fromQmltc.property("unresettable"), stringA);
QCOMPARE(fromEngine.property("unresettableAlias"), stringA);
QCOMPARE(fromEngine.property("unresettable"), stringA);
// check if notify arrives!
fromQmltc.setReadAndWrite(stringB);
fromEngine.setProperty("readAndWrite", stringB);
qsizetype calls = 0;
QSignalSpy spyQmltc(&fromQmltc, SIGNAL(notifiableChanged(QString)));
QSignalSpy spyEngine(&fromEngine, SIGNAL(notifiableChanged(QString)));
// write through alias
fromQmltc.setNotifiableAlias(stringA);
QVERIFY(fromEngine.setProperty("notifiableAlias", stringA));
QCOMPARE(spyQmltc.size(), ++calls);
QCOMPARE(spyEngine.size(), calls);
// write through property
fromQmltc.setReadAndWriteAndNotify(stringB);
QVERIFY(fromEngine.setProperty("notifiable", stringB));
QCOMPARE(spyQmltc.size(), ++calls);
QCOMPARE(spyEngine.size(), calls);
fromQmltc.setNotifiableMemberAlias(stringA);
QVERIFY(fromEngine.setProperty("notifiableMemberAlias", stringA));
QCOMPARE(spyQmltc.size(), ++calls);
QCOMPARE(spyEngine.size(), calls);
fromQmltc.setProperty("notifiableMember", stringB);
QVERIFY(fromEngine.setProperty("notifiableMember", stringB));
QCOMPARE(spyQmltc.size(), ++calls);
QCOMPARE(spyEngine.size(), calls);
// check that the alias to a revisioned property works
fromQmltc.setLatestReadAndWriteAlias(stringA);
QVERIFY(fromEngine.setProperty("latestReadAndWriteAlias", stringA));
QCOMPARE(fromQmltc.latestReadAndWriteAlias(), stringA);
QCOMPARE(fromQmltc.property("latestReadAndWrite"), stringA);
QCOMPARE(fromEngine.property("latestReadAndWriteAlias"), stringA);
QCOMPARE(fromEngine.property("latestReadAndWrite"), stringA);
QVERIFY(fromQmltc.setProperty("latestReadAndWrite", stringB));
QVERIFY(fromEngine.setProperty("latestReadAndWrite", stringB));
QCOMPARE(fromQmltc.latestReadAndWriteAlias(), stringB);
QCOMPARE(fromQmltc.property("latestReadAndWrite"), stringB);
QCOMPARE(fromEngine.property("latestReadAndWriteAlias"), stringB);
QCOMPARE(fromEngine.property("latestReadAndWrite"), stringB);
// check if metaobject of alias is correct
const QList<const QMetaObject *> metaObjects = {
fromQmltc.metaObject(),
fromEngine.metaObject(),
};
QVERIFY(metaObjects[0]);
QVERIFY(metaObjects[1]);
QList<QHash<QString, QMetaProperty>> metaProperties(2);
for (int j = 0; j < metaObjects.size(); j++) {
const QMetaObject *metaObject = metaObjects[j];
for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) {
metaProperties[j][QString::fromLatin1(metaObject->property(i).name())] =
metaObject->property(i);
}
}
{
QVERIFY(metaProperties[0].contains(u"hasAllAttributesAlias"_s));
QVERIFY(metaProperties[1].contains(u"hasAllAttributesAlias"_s));
QMetaProperty mpQmltc = metaProperties[0].value(u"hasAllAttributesAlias"_s);
QMetaProperty mpEngine = metaProperties[1].value(u"hasAllAttributesAlias"_s);
QCOMPARE(mpQmltc.isReadable(), mpEngine.isReadable());
QCOMPARE(mpQmltc.isWritable(), mpEngine.isWritable());
QCOMPARE(mpQmltc.isResettable(), mpEngine.isResettable());
QCOMPARE(mpQmltc.hasNotifySignal(), mpEngine.hasNotifySignal());
QCOMPARE(mpQmltc.revision(), mpEngine.revision());
QCOMPARE(mpQmltc.isDesignable(), mpEngine.isDesignable());
QCOMPARE(mpQmltc.isScriptable(), mpEngine.isScriptable());
QCOMPARE(mpQmltc.isStored(), mpEngine.isStored());
QCOMPARE(mpQmltc.isUser(), mpEngine.isUser());
QCOMPARE(mpQmltc.isBindable(), mpEngine.isBindable());
QCOMPARE(mpQmltc.isConstant(), mpEngine.isConstant());
QCOMPARE(mpQmltc.isFinal(), mpEngine.isFinal());
QCOMPARE(mpQmltc.isRequired(), mpEngine.isRequired());
}
{
QVERIFY(metaProperties[0].contains(u"hasAllAttributes2Alias"_s));
QVERIFY(metaProperties[1].contains(u"hasAllAttributes2Alias"_s));
QMetaProperty mpQmltc = metaProperties[0].value(u"hasAllAttributes2Alias"_s);
QMetaProperty mpEngine = metaProperties[1].value(u"hasAllAttributes2Alias"_s);
QCOMPARE(mpQmltc.isReadable(), mpEngine.isReadable());
QCOMPARE(mpQmltc.isWritable(), mpEngine.isWritable());
QCOMPARE(mpQmltc.isResettable(), mpEngine.isResettable());
QCOMPARE(mpQmltc.hasNotifySignal(), mpEngine.hasNotifySignal());
QCOMPARE(mpQmltc.revision(), mpEngine.revision());
QCOMPARE(mpQmltc.isDesignable(), mpEngine.isDesignable());
QCOMPARE(mpQmltc.isScriptable(), mpEngine.isScriptable());
QCOMPARE(mpQmltc.isStored(), mpEngine.isStored());
QCOMPARE(mpQmltc.isUser(), mpEngine.isUser());
QCOMPARE(mpQmltc.isBindable(), mpEngine.isBindable());
QCOMPARE(mpQmltc.isConstant(), mpEngine.isConstant());
QCOMPARE(mpQmltc.isFinal(), mpEngine.isFinal());
QCOMPARE(mpQmltc.isRequired(), mpEngine.isRequired());
}
}
// TODO: we need to support RESET in aliases as well? (does it make sense?)
void tst_qmltc::complexAliases()
{
QQmlEngine e;
PREPEND_NAMESPACE(complexAliases) created(&e);
// setup:
QQuickItem *theRect = nullptr;
QQuickItem *theText = nullptr;
QObject *accessibleBehavior = nullptr;
PREPEND_NAMESPACE(complexAliases_LocallyImported) *localImport = nullptr;
PREPEND_NAMESPACE(complexAliases_LocallyImported_Rectangle) *theRectInsideImported = nullptr;
{
QQmlListReference children(&created, "data");
QCOMPARE(children.size(), 2);
theRect = qobject_cast<QQuickItem *>(children.at(0));
QVERIFY(theRect);
{
QQmlListReference children(theRect, "data");
QCOMPARE(children.size(), 1);
theText = qobject_cast<QQuickItem *>(children.at(0));
QVERIFY(theText);
}
localImport = qobject_cast<PREPEND_NAMESPACE(complexAliases_LocallyImported) *>(children.at(1));
QVERIFY(localImport);
{
QQmlListReference children(localImport, "baseDefaultList");
QCOMPARE(children.size(), 1);
theRectInsideImported =
qobject_cast<PREPEND_NAMESPACE(complexAliases_LocallyImported_Rectangle) *>(children.at(0));
QVERIFY(theRectInsideImported);
}
accessibleBehavior = qvariant_cast<QObject *>(
e.contextForObject(&created)->contextProperty(u"accessibleBehavior"_s));
QVERIFY(accessibleBehavior);
}
// aText:
QCOMPARE(created.aText(), theRect->property("text").toString());
created.updateTextThroughAlias(QStringLiteral("42"));
QCOMPARE(created.aText(), QStringLiteral("42"));
QCOMPARE(created.aText(), theRect->property("text").toString());
created.updateText(QStringLiteral("fourty-two"));
QCOMPARE(theRect->property("text").toString(), QStringLiteral("fourty-two"));
QCOMPARE(created.aText(), theRect->property("text").toString());
// aColor:
QCOMPARE(created.aColor(), QColor(QStringLiteral("red")));
QCOMPARE(created.aColor(), theText->property("color").value<QColor>());
created.setAColor(QColor("black"));
QCOMPARE(created.aColor(), QColor(QStringLiteral("black")));
QCOMPARE(created.aColor(), theText->property("color").value<QColor>());
theText->setProperty("color", QColor(QStringLiteral("green")));
QCOMPARE(theText->property("color").value<QColor>(), QColor(QStringLiteral("green")));
QCOMPARE(created.aColor(), theText->property("color").value<QColor>());
// aliases to ids:
QCOMPARE(created.aRectObject(), theRect);
QCOMPARE(created.aTextObject(), theText);
// aLetterSpacing:
QCOMPARE(created.aLetterSpacing(), theText->property("font").value<QFont>().letterSpacing());
created.setALetterSpacing(5);
QCOMPARE(created.aLetterSpacing(), 5);
QCOMPARE(created.aLetterSpacing(), theText->property("font").value<QFont>().letterSpacing());
// aWordSpacing:
QCOMPARE(created.aWordSpacing(), theText->property("font").value<QFont>().wordSpacing());
created.setAWordSpacing(42);
QCOMPARE(created.aWordSpacing(), 42);
QCOMPARE(created.aWordSpacing(), theText->property("font").value<QFont>().wordSpacing());
// aFont:
QCOMPARE(created.aFont(), theText->property("font").value<QFont>());
QSignalSpy aFontSpy(&created, &PREPEND_NAMESPACE(complexAliases)::aFontChanged);
QFont newFont = created.aFont();
newFont.setWeight(QFont::DemiBold);
created.setAFont(newFont);
QCOMPARE(created.aFont(), newFont);
QCOMPARE(created.aFont(), theText->property("font").value<QFont>());
QCOMPARE(aFontSpy.size(), 1);
newFont.setStyle(QFont::StyleOblique);
theText->setProperty("font", newFont);
QCOMPARE(theText->property("font").value<QFont>(), newFont);
QCOMPARE(created.aFont(), theText->property("font").value<QFont>());
QCOMPARE(aFontSpy.size(), 2);
created.setAWordSpacing(1);
QCOMPARE(aFontSpy.size(), 3);
// aliasToObjectAlias:
QCOMPARE(created.aliasToObjectAlias(), created.aRectObject());
// aliasToPropertyAlias:
QCOMPARE(created.aliasToPropertyAlias(), created.aText());
created.setAliasToPropertyAlias(QStringLiteral("milky way"));
QCOMPARE(created.aliasToPropertyAlias(), QStringLiteral("milky way"));
QCOMPARE(created.aliasToPropertyAlias(), created.aText());
QCOMPARE(created.aliasToPropertyAlias(), theRect->property("text").toString());
QProperty<QString> source;
created.bindableAliasToPropertyAlias().setBinding(Qt::makePropertyBinding(source));
source.setValue(QStringLiteral("bound value"));
QCOMPARE(created.aliasToPropertyAlias(), QStringLiteral("bound value"));
QCOMPARE(created.aliasToPropertyAlias(), created.aText());
QCOMPARE(created.aliasToPropertyAlias(), theRect->property("text").toString());
source.setValue(QStringLiteral("foobar"));
QCOMPARE(created.aliasToPropertyAlias(), QStringLiteral("foobar"));
QCOMPARE(created.aliasToPropertyAlias(), created.aText());
QCOMPARE(created.aliasToPropertyAlias(), theRect->property("text").toString());
created.setAliasToPropertyAlias(QStringLiteral("resetting back"));
source.setValue(QStringLiteral("unused"));
QCOMPARE(created.aliasToPropertyAlias(), QStringLiteral("resetting back"));
QCOMPARE(created.aliasToPropertyAlias(), created.aText());
QCOMPARE(created.aliasToPropertyAlias(), theRect->property("text").toString());
// aliasToValueTypeAlias:
QCOMPARE(created.aliasToValueTypeAlias(), created.aFont());
QSignalSpy aliasToValueTypeAliasSpy(&created, &PREPEND_NAMESPACE(complexAliases)::aliasToValueTypeAliasChanged);
newFont.setPixelSize(389);
created.setAliasToValueTypeAlias(newFont);
QCOMPARE(created.aliasToValueTypeAlias(), newFont);
QCOMPARE(created.aliasToValueTypeAlias(), created.aFont());
QCOMPARE(created.aliasToValueTypeAlias(), theText->property("font").value<QFont>());
QCOMPARE(aFontSpy.size(), 4);
QCOMPARE(aliasToValueTypeAliasSpy.size(), 1);
newFont.setPixelSize(12);
created.setAFont(newFont);
QCOMPARE(aFontSpy.size(), 5);
QCOMPARE(aliasToValueTypeAliasSpy.size(), 2);
QCOMPARE(created.aliasToValueTypeAlias(), created.aFont());
QCOMPARE(created.aliasToValueTypeAlias(), theText->property("font").value<QFont>());
// aliasToPropertyOfValueTypeAlias:
QCOMPARE(created.aliasToPropertyOfValueTypeAlias(), newFont.pixelSize());
created.setAliasToPropertyOfValueTypeAlias(3);
QCOMPARE(created.aliasToPropertyOfValueTypeAlias(), created.aFont().pixelSize());
QCOMPARE(created.aFont(), theText->property("font").value<QFont>());
QCOMPARE(aFontSpy.size(), 6);
QCOMPARE(aliasToValueTypeAliasSpy.size(), 3);
// aliasToImportedMessage:
QCOMPARE(created.aliasToImportedMessage(), localImport->property("message").toString());
created.setAliasToImportedMessage(QStringLiteral("new value"));
QCOMPARE(created.aliasToImportedMessage(), localImport->property("message").toString());
localImport->setProperty("message", QStringLiteral("old value"));
QCOMPARE(created.aliasToImportedMessage(), localImport->property("message").toString());
// don't test bindability, since it's tested by another property
// aliasToOnAssignmentProperty
QCOMPARE(created.aliasToOnAssignmentProperty(), QStringLiteral("width"));
// localImport.importedAliasToText:
QCOMPARE(localImport->importedAliasToText(), theRect->property("text").toString());
localImport->setImportedAliasToText(QStringLiteral("text never seen before"));
QCOMPARE(localImport->importedAliasToText(), theRect->property("text").toString());
theRect->setProperty("text", QStringLiteral("text, maybe, already seen before"));
QCOMPARE(localImport->importedAliasToText(), theRect->property("text").toString());
QCOMPARE(localImport->importedAliasToText(), created.aText());
// theRectInsideImported.internallyImportedAliasToText:
QCOMPARE(theRectInsideImported->internallyImportedAliasToText(),
theText->property("text").toString());
theRectInsideImported->setInternallyImportedAliasToText(QStringLiteral("set unset reset"));
QCOMPARE(theRectInsideImported->internallyImportedAliasToText(),
theText->property("text").toString());
theText->setProperty("text", QStringLiteral("zzz"));
QCOMPARE(theRectInsideImported->internallyImportedAliasToText(),
theText->property("text").toString());
// aliasToPrivatePalette:
QCOMPARE(created.aliasToPrivatePalette(), QQuickItemPrivate::get(theRect)->palette());
QSignalSpy paletteChangedSpy(&created, &PREPEND_NAMESPACE(complexAliases)::aliasToPrivatePaletteChanged);
QQuickPalette *newPalette = new QQuickPalette(&created);
newPalette->fromQPalette(QPalette(QColor(u"cyan"_s)));
QCOMPARE(newPalette->button(), QColor(u"cyan"_s));
created.setAliasToPrivatePalette(newPalette);
QCOMPARE(paletteChangedSpy.size(), 1);
QCOMPARE(QQuickItemPrivate::get(theRect)->palette()->button(), QColor(u"cyan"_s));
QCOMPARE(created.aliasToPrivatePalette(), QQuickItemPrivate::get(theRect)->palette());
// aliasToAnchors:
QCOMPARE(created.aliasToAnchors(), QQuickItemPrivate::get(theRect)->anchors());
QQuickItemPrivate::get(theRect)->anchors()->setTopMargin(5);
QCOMPARE(created.aliasToAnchors()->topMargin(), 5);
// aliasToPrivateData:
QCOMPARE(created.aliasToPrivateData(), QQuickItemPrivate::get(&created)->data());
}
void tst_qmltc::propertyChangeHandler()
{
{
QQmlEngine e;
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/propertyChangeHandler.qml"_s));
QScopedPointer<QObject> root(c.create());
QVERIFY2(root, qPrintable(c.errorString()));
QCOMPARE(root->property("watcher").toInt(), 42);
}
QQmlEngine e;
PREPEND_NAMESPACE(propertyChangeHandler) created(&e);
// NB: watcher is set to 42 - aligned with QQmlComponent
QCOMPARE(created.watcher(), 42);
QCOMPARE(created.p(), 42); // due to binding
QCOMPARE(created.watcher(), 42);
QCOMPARE(created.property("watcher").toInt(), 42);
// test that binding triggers property change handler
created.setDummy(20);
QCOMPARE(created.watcher(), 20);
QCOMPARE(created.property("watcher").toInt(), 20);
// test that property setting (through C++) triggers property change handler
created.setWatcher(-100);
created.setProperty("p", 18);
QCOMPARE(created.watcher(), 18);
// test that property setting triggers property change handler
created.setWatcher(-47);
created.setP(96);
QCOMPARE(created.property("watcher").toInt(), 96);
}
void tst_qmltc::nestedHelloWorld()
{
QQmlEngine e;
PREPEND_NAMESPACE(NestedHelloWorld) created(&e);
QCOMPARE(created.hello(), QStringLiteral("Hello"));
QCOMPARE(created.jsGetGreeting().toString(), QStringLiteral("Hello, World!"));
QCOMPARE(created.greeting(), QStringLiteral("Hello, World!"));
QPointer<PREPEND_NAMESPACE(NestedHelloWorld_QtObject)> firstLevelChild(
qobject_cast<PREPEND_NAMESPACE(NestedHelloWorld_QtObject) *>(created.child()));
QVERIFY(firstLevelChild);
QCOMPARE(firstLevelChild->hello(), QStringLiteral("Hello"));
QCOMPARE(firstLevelChild->jsGetGreeting().toString(), QStringLiteral("Hello, Qt!"));
QCOMPARE(firstLevelChild->greeting(), QStringLiteral("Hello, Qt!"));
QPointer<PREPEND_NAMESPACE(NestedHelloWorld_QtObject_QtObject)> secondLevelChild(
qobject_cast<PREPEND_NAMESPACE(NestedHelloWorld_QtObject_QtObject) *>(firstLevelChild->child()));
QVERIFY(secondLevelChild);
QCOMPARE(secondLevelChild->hello(), QStringLiteral("Hello"));
QCOMPARE(secondLevelChild->jsGetGreeting().toString(), QStringLiteral("Hello, Qml!"));
QCOMPARE(secondLevelChild->greeting(), QStringLiteral("Hello, Qml!"));
}
void tst_qmltc::componentHelloWorld()
{
QQmlEngine e;
QScopedPointer<PREPEND_NAMESPACE(ComponentHelloWorld)> created(new PREPEND_NAMESPACE(ComponentHelloWorld)(&e));
QVERIFY(QQmlData::get(created.get())->context->componentAttacheds());
QCOMPARE(created->hello(), QStringLiteral("Hello, World!"));
QSignalSpy onDestroySpy(created.get(), &PREPEND_NAMESPACE(ComponentHelloWorld)::sDestroying);
QCOMPARE(onDestroySpy.size(), 0);
created.reset();
QCOMPARE(onDestroySpy.size(), 1);
}
void tst_qmltc::propertyReturningFunction()
{
QQmlEngine e;
PREPEND_NAMESPACE(propertyReturningFunction) created(&e);
QCOMPARE(created.counter(), 0);
QVariant f = created.f();
QCOMPARE(created.counter(), 0);
created.property("f");
QCOMPARE(created.counter(), 0);
QJSValue function = qvariant_cast<QJSValue>(f);
QVERIFY(function.isCallable());
function.call();
QCOMPARE(created.counter(), 1);
function.call();
QCOMPARE(created.counter(), 2);
}
void tst_qmltc::listProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(listProperty) created(&e);
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/listProperty.qml"_s));
QScopedPointer<QObject> fromEngine(c.create());
QVERIFY2(fromEngine, qPrintable(c.errorString()));
QCOMPARE(created.hello(), QStringLiteral("Hello from parent"));
QQmlListReference ref(&created, "children");
QCOMPARE(ref.count(), 2);
QList<QObject *> children = { ref.at(0), ref.at(1) };
for (auto &child : children) {
QVERIFY(child);
}
QCOMPARE(children.at(0)->property("hello").toString(),
QStringLiteral("Hello from parent.children[0]"));
QCOMPARE(children.at(1)->property("hello").toString(),
QStringLiteral("Hello from parent.children[1]"));
QQmlListReference refIds(&created, "ids");
QCOMPARE(refIds.count(), 3);
QCOMPARE(refIds.at(0), &created);
QCOMPARE(refIds.at(1), ref.at(0));
QCOMPARE(refIds.at(2), ref.at(1));
QCOMPARE(fromEngine->property("firstCount"), 4);
QCOMPARE(fromEngine->property("secondCount"), 5);
QCOMPARE(created.firstCount(), 4);
QCOMPARE(created.secondCount(), 5);
QCOMPARE(created.childrenCount(), 2);
QCOMPARE(created.childrenAt(0)->property("hello"), u"Hello from parent.children[0]"_s);
QCOMPARE(created.childrenAt(1)->property("hello"), u"Hello from parent.children[1]"_s);
created.childrenAppend(created.appendMe());
QCOMPARE(created.childrenCount(), 3);
QCOMPARE(created.childrenAt(0)->property("hello"), u"Hello from parent.children[0]"_s);
QCOMPARE(created.childrenAt(1)->property("hello"), u"Hello from parent.children[1]"_s);
QCOMPARE(created.childrenAt(2)->property("hello"), u"Hello from parent.children[2]"_s);
created.childrenReplace(0, created.appendMe());
QCOMPARE(created.childrenCount(), 3);
QCOMPARE(created.childrenAt(0)->property("hello"), u"Hello from parent.children[2]"_s);
QCOMPARE(created.childrenAt(1)->property("hello"), u"Hello from parent.children[1]"_s);
QCOMPARE(created.childrenAt(2)->property("hello"), u"Hello from parent.children[2]"_s);
created.childrenRemoveLast();
QCOMPARE(created.childrenCount(), 2);
QCOMPARE(created.childrenAt(0)->property("hello"), u"Hello from parent.children[2]"_s);
QCOMPARE(created.childrenAt(1)->property("hello"), u"Hello from parent.children[1]"_s);
created.childrenClear();
QCOMPARE(created.childrenCount(), 0);
}
void tst_qmltc::listPropertiesWithTheSameName()
{
QQmlEngine e;
PREPEND_NAMESPACE(listPropertySameName) created(&e);
QQmlListReference rootData(&created, "data");
QCOMPARE(rootData.count(), 1);
QObject *child = rootData.at(0);
QVERIFY(child);
QCOMPARE(child->property("what").toString(), QStringLiteral("child"));
QQmlListReference childData(child, "data");
QCOMPARE(childData.count(), 1);
QObject *childChild = childData.at(0);
QVERIFY(childChild);
QCOMPARE(childChild->property("what").toString(), QStringLiteral("child.child"));
}
void tst_qmltc::defaultProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(defaultProperty) created(&e);
QCOMPARE(created.hello(), QStringLiteral("Hello from parent"));
QObject *firstLevelChild = created.child();
QVERIFY(firstLevelChild);
QCOMPARE(firstLevelChild->property("hello").toString(),
QStringLiteral("Hello from parent.child"));
QVERIFY(qobject_cast<PREPEND_NAMESPACE(defaultProperty_DefaultPropertyManyChildren) *>(firstLevelChild));
QQmlListReference ref(firstLevelChild, "children");
QCOMPARE(ref.count(), 2);
QList<QObject *> secondLevelChildren = { ref.at(0), ref.at(1) };
for (auto &child : secondLevelChildren) {
QVERIFY(child);
}
QCOMPARE(secondLevelChildren.at(0)->property("hello").toString(),
QStringLiteral("Hello from parent.child.children[0]"));
QCOMPARE(secondLevelChildren.at(1)->property("hello").toString(),
QStringLiteral("Hello from parent.child.children[1]"));
}
void tst_qmltc::defaultPropertyCorrectSelection()
{
QQmlEngine e;
PREPEND_NAMESPACE(defaultPropertyCorrectSelection) created(&e);
QQmlListReference children(&created, "data");
QCOMPARE(children.count(), 1);
QCOMPARE(created.unused(), nullptr);
}
void tst_qmltc::defaultAlias()
{
QQmlEngine e;
PREPEND_NAMESPACE(defaultAlias) created(&e);
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/defaultAlias.qml"_s));
QScopedPointer<QObject> fromEngine(c.create());
QVERIFY2(fromEngine, qPrintable(c.errorString()));
auto *child = static_cast<PREPEND_NAMESPACE(defaultAlias_QtObject) *>(created.child());
QVERIFY(fromEngine->property("child").canConvert<QObject *>());
QObject *childFromEngine = fromEngine->property("child").value<QObject *>();
QVERIFY(childFromEngine);
QCOMPARE(child->hello(), childFromEngine->property("hello"));
}
void tst_qmltc::attachedProperty()
{
QScopeGuard exitTest([oldCount = TestTypeAttached::creationCount]() {
QCOMPARE(TestTypeAttached::creationCount, oldCount + 1);
});
Q_UNUSED(exitTest);
QQmlEngine e;
PREPEND_NAMESPACE(AttachedProperty) created(&e);
TestTypeAttached *attached = qobject_cast<TestTypeAttached *>(
qmlAttachedPropertiesObject<TestType>(&created, false));
QVERIFY(attached);
QCOMPARE(attached->getAttachedCount(), 42);
QCOMPARE(attached->getAttachedFormula(), 42);
QVERIFY(attached->getAttachedObject());
QCOMPARE(attached->getAttachedObject()->property("name").toString(),
QStringLiteral(u"root.TestType.attachedObject"));
// sanity
QCOMPARE(created.myCount(), attached->getAttachedCount());
QCOMPARE(created.myTriggerFired(), false);
emit created.myTriggered();
QCOMPARE(created.myTriggerFired(), true);
QCOMPARE(attached->getAttachedCount(), 43);
QCOMPARE(created.myCount(), attached->getAttachedCount());
QCOMPARE(attached->getAttachedFormula(), 42 * 2);
created.setMyTriggerFired(false);
emit attached->triggered();
QCOMPARE(created.myTriggerFired(), true);
QCOMPARE(attached->getAttachedCount(), 44);
QCOMPARE(created.myCount(), attached->getAttachedCount());
QCOMPARE(attached->getAttachedFormula(), 42 * 2 * 2);
emit created.updateAttachedCount();
QCOMPARE(attached->getAttachedCount(), 45);
QCOMPARE(created.myCount(), attached->getAttachedCount());
QCOMPARE(attached->getAttachedFormula(), 42 * 2 * 2 * 2);
}
void tst_qmltc::attachedPropertyObjectCreatedOnce()
{
QScopeGuard exitTest([oldCount = TestTypeAttached::creationCount]() {
QCOMPARE(TestTypeAttached::creationCount, oldCount + 1);
});
Q_UNUSED(exitTest);
QQmlEngine e;
PREPEND_NAMESPACE(attachedPropertyDerived) created(&e);
TestTypeAttached *attached = qobject_cast<TestTypeAttached *>(
qmlAttachedPropertiesObject<TestType>(&created, false));
QVERIFY(attached);
QCOMPARE(attached->getAttachedCount(), -314);
}
void tst_qmltc::groupedProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(groupedProperty) created(&e);
TestTypeGrouped *grouped = created.getGroup();
QVERIFY(grouped);
int unspecifiedValue = 42; // could be 42 or 84
QCOMPARE(grouped->getCount(), 42);
QCOMPARE(grouped->getFormula(), unspecifiedValue); // TODO: this is unspecified
QVERIFY(grouped->getObject());
QCOMPARE(grouped->getObject()->property("name").toString(),
QStringLiteral(u"root.group.object"));
// sanity
QCOMPARE(created.myCount(), grouped->getCount());
QCOMPARE(created.myTriggerFired(), false);
const int initialFormulaValue = grouped->getFormula();
emit created.myTriggered();
QCOMPARE(created.myTriggerFired(), true);
QCOMPARE(grouped->getCount(), 43);
QCOMPARE(created.myCount(), grouped->getCount());
QCOMPARE(grouped->getFormula(), initialFormulaValue * 2);
created.setMyTriggerFired(false);
emit grouped->triggered();
QCOMPARE(created.myTriggerFired(), true);
QCOMPARE(grouped->getCount(), 44);
QCOMPARE(created.myCount(), grouped->getCount());
QCOMPARE(grouped->getFormula(), initialFormulaValue * 2 * 2);
emit created.updateCount();
QCOMPARE(grouped->getCount(), 45);
QCOMPARE(created.myCount(), grouped->getCount());
QCOMPARE(grouped->getFormula(), initialFormulaValue * 2 * 2 * 2);
}
// TODO: add a test for private property which is a value type
// TODO: what about signals of private properties?
void tst_qmltc::groupedProperty_qquicktext()
{
QQmlEngine e;
PREPEND_NAMESPACE(groupedProperty_qquicktext) created(&e);
QQuickAnchors *anchors = QQuickItemPrivate::get(&created)->anchors();
QVERIFY(anchors);
QCOMPARE(anchors->alignWhenCentered(), false);
QCOMPARE(anchors->topMargin(), qreal(1));
QCOMPARE(anchors->bottomMargin(), qreal(42));
QFont font = created.font();
QCOMPARE(font.family(), u"Helvetica"_s);
QCOMPARE(font.pointSize(), 4);
QCOMPARE(font.letterSpacing(), 3);
QQmlListReference ref(&created, "data");
QCOMPARE(ref.count(), 1);
QQuickItem *child = qobject_cast<QQuickItem *>(ref.at(0));
QVERIFY(child);
QQuickAnchors *childAnchors = QQuickItemPrivate::get(child)->anchors();
QVERIFY(childAnchors);
QCOMPARE(childAnchors->topMargin(), qreal(1));
QCOMPARE(childAnchors->bottomMargin(), qreal(42));
anchors->setTopMargin(2);
anchors->setBottomMargin(11);
QCOMPARE(childAnchors->topMargin(), qreal(2));
QCOMPARE(childAnchors->bottomMargin(), qreal(11));
}
template<typename T>
void localImport_impl(T &created)
{
QCOMPARE(created.baseMessage(), QStringLiteral(u"base"));
QCOMPARE(created.count(), 1);
QCOMPARE(created.derivedMessage(), QStringLiteral(u"derived"));
QCOMPARE(created.message(), QStringLiteral(u"derived.message"));
QVERIFY(created.baseObject());
QQuickText *baseObject = qobject_cast<QQuickText *>(created.baseObject());
QVERIFY(baseObject);
QCOMPARE(baseObject->text(), QStringLiteral(u"derived.baseObject"));
QQmlListReference ref(&created, "baseDefaultList");
QCOMPARE(ref.count(), 3);
QList<QObject *> children = { ref.at(0), ref.at(1), ref.at(2) };
for (auto &child : children) {
QVERIFY(child);
}
QVERIFY(qobject_cast<QQuickText *>(children.at(0)));
QVERIFY(qobject_cast<QQuickRectangle *>(children.at(1)));
QVERIFY(qobject_cast<PREPEND_NAMESPACE(HelloWorld) *>(children.at(2)));
QCOMPARE(children.at(0)->property("text").toString(), QStringLiteral("derived.child[0]"));
QCOMPARE(children.at(1)->property("text").toString(), QStringLiteral("derived.child[1]"));
QCOMPARE(children.at(2)->property("text").toString(), QStringLiteral("derived.child[2]"));
QCOMPARE(children.at(2)->property("hello").toString(), QStringLiteral("Hello, World"));
}
void tst_qmltc::localImport()
{
QQmlEngine e;
PREPEND_NAMESPACE(localImport) created(&e);
localImport_impl(created);
}
void tst_qmltc::explicitLocalImport()
{
QQmlEngine e;
PREPEND_NAMESPACE(localImport_explicit) created(&e);
localImport_impl(created);
}
void tst_qmltc::newPropertyBoundToOld()
{
QQmlEngine e;
PREPEND_NAMESPACE(newPropertyBoundToOld) created(&e);
// sanity
QCOMPARE(created.width(), 10);
QCOMPARE(created.widthComponent(), 10);
QCOMPARE(created.newWidth(), created.width() + created.widthComponent());
// update new style property first to see if this works
created.setWidthComponent(11);
QCOMPARE(created.widthComponent(), 11);
QCOMPARE(created.width(), 10);
QCOMPARE(created.newWidth(), 21);
// update old style property afterwards to see if this also works
created.setWidth(89);
QCOMPARE(created.width(), 89);
QCOMPARE(created.widthComponent(), 11);
QCOMPARE(created.newWidth(), 100);
}
void tst_qmltc::oldPropertyBoundToNew()
{
QQmlEngine e;
PREPEND_NAMESPACE(oldPropertyBoundToNew) created(&e);
// sanity
QCOMPARE(created.width(), 10);
QCOMPARE(created.heightComponent(), 10);
QCOMPARE(created.height(), created.width() + created.heightComponent());
// update old style property first to see if this works
created.setWidth(11);
QCOMPARE(created.width(), 11);
QCOMPARE(created.heightComponent(), 10);
QCOMPARE(created.height(), 21);
// update new style property afterwards to see if this also works
created.setHeightComponent(89);
QCOMPARE(created.heightComponent(), 89);
QCOMPARE(created.width(), 11);
QCOMPARE(created.height(), 100);
}
void tst_qmltc::nonLocalQmlPropertyBoundToAny()
{
QQmlEngine e;
PREPEND_NAMESPACE(nonLocalQmlPropertyBoundToAny) created(&e);
// sanity
QCOMPARE(created.width(), 10);
QCOMPARE(created.newWidth(), 10);
QCOMPARE(created.message(), QStringLiteral("width=20"));
// update old style property
created.setWidth(11);
QCOMPARE(created.width(), 11);
QCOMPARE(created.newWidth(), 10);
QCOMPARE(created.message(), QStringLiteral("width=21"));
// update new style property
created.setNewWidth(89);
QCOMPARE(created.newWidth(), 89);
QCOMPARE(created.width(), 11);
QCOMPARE(created.message(), QStringLiteral("width=100"));
}
void tst_qmltc::localImportWithOnCompleted()
{
QQmlEngine e;
PREPEND_NAMESPACE(localDerived) created(&e);
// sanity
QCOMPARE(created.count(), 1); // from base type
QCOMPARE(created.p1(), 323);
QCOMPARE(created.p2(), created.p1() + 10);
created.setP1(32);
QCOMPARE(created.p1(), 32);
QCOMPARE(created.p2(), created.p1() + 10);
}
void tst_qmltc::justAnimation()
{
QQmlEngine e;
PREPEND_NAMESPACE(justAnimation) created(&e);
// sanity
QCOMPARE(created.width(), 0);
created.setWidth(500);
QTRY_VERIFY(created.width() != 0);
QTRY_VERIFY(created.width() == 500);
}
void tst_qmltc::justAnimationOnAlias()
{
QQmlEngine e;
PREPEND_NAMESPACE(justAnimationOnAlias) created(&e);
// sanity
QCOMPARE(created.width(), 100);
QCOMPARE(created.widthAlias(), 100);
// through "real" property
created.setWidth(500);
QTRY_VERIFY(created.widthAlias() != 100);
QTRY_VERIFY(created.widthAlias() == 500);
QCOMPARE(created.widthAlias(), 500);
// through alias
created.setWidthAlias(10);
QTRY_VERIFY(created.widthAlias() != 500);
QTRY_VERIFY(created.widthAlias() == 10);
}
void tst_qmltc::behaviorAndAnimation()
{
QQmlEngine e;
PREPEND_NAMESPACE(behaviorAndAnimation) created(&e);
// sanity
QCOMPARE(created.width(), 100);
created.setWidth(500);
QTRY_VERIFY(created.width() != 100);
QTRY_VERIFY(created.width() == 500);
}
void tst_qmltc::behaviorAndAnimationOnAlias()
{
QQmlEngine e;
PREPEND_NAMESPACE(behaviorAndAnimationOnAlias) created(&e);
// sanity
QCOMPARE(created.width(), 100);
QCOMPARE(created.widthAlias(), 100);
// through "real" property
created.setWidth(500);
QTRY_VERIFY(created.widthAlias() != 100);
QTRY_VERIFY(created.widthAlias() == 500);
QCOMPARE(created.widthAlias(), 500);
// through alias
created.setWidthAlias(10);
QTRY_VERIFY(created.widthAlias() != 500);
QTRY_VERIFY(created.widthAlias() == 10);
}
void tst_qmltc::bindingsThroughIds()
{
QQmlEngine e;
PREPEND_NAMESPACE(bindingsThroughIds) created(&e);
QCOMPARE(created.text(), QStringLiteral("theText.text"));
QCOMPARE(created.width(), 42);
QQmlContext *mainContext = e.contextForObject(&created);
QVERIFY(mainContext);
QCOMPARE(mainContext->objectForName(QStringLiteral("theItem")), &created);
QList<QObject *> children = created.children();
QCOMPARE(children.size(), 2);
QQmlContext *theTextContext = e.contextForObject(children.at(0));
QCOMPARE(mainContext, theTextContext);
QCOMPARE(theTextContext->objectForName(QStringLiteral("theText")), children.at(0));
QQmlContext *theRectContext = e.contextForObject(children.at(1));
QCOMPARE(mainContext, theRectContext);
QCOMPARE(theRectContext->objectForName(QStringLiteral("theRect")), children.at(1));
QCOMPARE(children.at(0)->property("color"), children.at(1)->property("color"));
}
void tst_qmltc::contextHierarchy_rootBaseIsQml()
{
{
QQmlEngine e;
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/localImport_context.qml"_s));
QScopedPointer<QObject> root(c.create());
QVERIFY2(root, qPrintable(c.errorString()));
// sanity
QCOMPARE(root->property("count").toInt(), 43);
}
QQmlEngine e;
PREPEND_NAMESPACE(localImport_context) created(&e);
QCOMPARE(created.p1(), 41);
// NB: this is super weird, but count would indeed be 43 and not (p1 + 1)
// due to parent's Component.onCompleted being called after the binding
// setup in the child class - same happens in QQmlComponent case
QCOMPARE(created.count(), 43);
QQmlContext *ctx = e.contextForObject(&created);
QVERIFY(ctx);
QVERIFY(ctx->parentContext());
QEXPECT_FAIL("",
"Inconsistent with QQmlComponent: 'foo' could actually be found in generated "
"C++ base class context",
Continue);
QCOMPARE(ctx->contextProperty(u"foo"_s), QVariant());
QCOMPARE(qvariant_cast<QObject *>(ctx->contextProperty(u"bar"_s)), &created);
// NB: even though ctx->contextProperty(u"foo"_s) finds the object,
// objectForName(u"foo"_s) still returns nullptr as "foo" exists in the
// ctx->parent()
QCOMPARE(ctx->objectForName(u"foo"_s), nullptr);
QCOMPARE(ctx->objectForName(u"bar"_s), &created);
QEXPECT_FAIL("",
"Inconsistent with QQmlComponent: LocallyImported is a _visible_ parent of "
"localImport, same stays true for context",
Continue);
QCOMPARE(QQmlContextData::get(ctx)->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(QQmlContextData::get(ctx)->parent()->parent(), QQmlContextData::get(e.rootContext()));
int count = created.count();
QCOMPARE(created.getMagicValue().toInt(), (count * 3 + 1));
count = created.count();
QCOMPARE(created.localGetMagicValue().toInt(), (count * 3 + 1));
}
void tst_qmltc::contextHierarchy_childBaseIsQml()
{
QQmlEngine e;
PREPEND_NAMESPACE(neighbors_context) created(&e);
QQmlListReference children(&created, "data");
QCOMPARE(children.size(), 2);
auto *child1 = qobject_cast<PREPEND_NAMESPACE(neighbors_context_QtObject) *>(children.at(0));
auto child2 = qobject_cast<PREPEND_NAMESPACE(neighbors_context_LocallyImported_context) *>(children.at(1));
QVERIFY(child1 && child2);
auto rootCtx = QQmlContextData::get(e.contextForObject(&created));
auto child1Ctx = QQmlContextData::get(e.contextForObject(child1));
auto child2Ctx = QQmlContextData::get(e.contextForObject(child2));
QCOMPARE(rootCtx->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(child1Ctx, rootCtx);
QCOMPARE(child2Ctx, rootCtx);
QCOMPARE(child2Ctx->parent(), QQmlContextData::get(e.rootContext()));
// the context of child2 is certainly the root context of the document.
QCOMPARE(child2Ctx, rootCtx);
QQmlContext *rootQmlCtx = rootCtx->asQQmlContext();
QCOMPARE(rootQmlCtx->objectForName(u"root"_s), &created);
QCOMPARE(rootQmlCtx->objectForName(u"child1"_s), child1);
QCOMPARE(rootQmlCtx->objectForName(u"child2"_s), child2);
QCOMPARE(child1->p(), 41);
QCOMPARE(child1->p2(), child2->count() * 2);
QCOMPARE(child2->p(), child1->p() + 1);
child1->setP(44);
QCOMPARE(child2->p(), 45);
child2->setCount(4);
QCOMPARE(child1->p2(), 8);
int count = child2->count();
QVariant magicValue {};
QMetaObject::invokeMethod(child2, "getMagicValue", Q_RETURN_ARG(QVariant, magicValue));
QCOMPARE(magicValue.toInt(), (count * 3 + 1));
}
void tst_qmltc::contextHierarchy_delegate()
{
// TODO: not everything works (if the QML file is changed, so QQmlComponent
// here is useful)
{
QQmlEngine e;
QQmlComponent c(&e);
c.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/delegate_context.qml"_s));
QScopedPointer<QObject> root(c.create());
QVERIFY2(root, qPrintable(c.errorString()));
QQmlListReference data(root.get(), "data");
QCOMPARE(data.count(), 1);
auto listView = qobject_cast<QQuickListView *>(data.at(0));
QVERIFY(listView);
QPointer<QObject> delegate = listView->currentItem();
QVERIFY(delegate);
QCOMPARE(delegate->property("text").toString(), QStringLiteral("hello delegate"));
QQmlListReference dataOfDelegate(delegate.get(), "data");
QCOMPARE(dataOfDelegate.count(), 1);
QVERIFY(dataOfDelegate.at(0));
QCOMPARE(dataOfDelegate.at(0)->property("text").toString(),
QStringLiteral("hello delegate text"));
}
QQmlEngine e;
PREPEND_NAMESPACE(delegate_context) created(&e);
QQmlListReference data(&created, "data");
QCOMPARE(data.count(), 1);
auto listView = qobject_cast<PREPEND_NAMESPACE(delegate_context_ListView) *>(data.at(0));
QVERIFY(listView);
QCOMPARE(created.text(), QStringLiteral("hello"));
QVERIFY(listView->delegate());
QPointer<QObject> delegate = listView->currentItem();
QVERIFY(delegate);
QCOMPARE(delegate->property("text").toString(), QStringLiteral("hello delegate"));
QQmlListReference dataOfDelegate(delegate.get(), "data");
QCOMPARE(dataOfDelegate.count(), 1);
QVERIFY(dataOfDelegate.at(0));
QCOMPARE(dataOfDelegate.at(0)->property("text").toString(),
QStringLiteral("hello delegate text"));
}
void tst_qmltc::contextHierarchy_nontrivial()
{
// NB: test that we don't crash
QQmlEngine e;
PREPEND_NAMESPACE(nontrivial_context) created(&e);
Q_UNUSED(created);
QQmlListReference children(&created, "data");
QCOMPARE(children.count(), 3);
auto imported = qobject_cast<PREPEND_NAMESPACE(nontrivial_context_LocallyImported) *>(children.at(2));
QVERIFY(imported);
QQmlListReference childrenOfImported(imported, "baseDefaultList");
QCOMPARE(childrenOfImported.count(), 2);
QVERIFY(childrenOfImported.at(0));
QCOMPARE(childrenOfImported.at(0)->property("text").toString(), QStringLiteral("hello, world"));
auto mouseArea = qobject_cast<PREPEND_NAMESPACE(nontrivial_context_MouseArea) *>(children.at(1));
QVERIFY(mouseArea);
emit mouseArea->clicked(nullptr);
QCOMPARE(childrenOfImported.at(0)->property("text").toString(), QStringLiteral("clicked"));
auto helloWorld = qobject_cast<PREPEND_NAMESPACE(HelloWorld) *>(childrenOfImported.at(1));
QVERIFY(helloWorld);
QCOMPARE(helloWorld->greeting(), QStringLiteral("Hello, World!"));
}
void tst_qmltc::javascriptImport()
{
QQmlEngine e;
PREPEND_NAMESPACE(javascriptCaller) created(&e);
QVERIFY(!created.valueIsBad());
QVERIFY(created.valueIsGood());
}
void tst_qmltc::listView()
{
if (QTestPrivate::isRunningArmOnX86())
QSKIP("Flaky on QEMU. Sometimes can't correctly run JavaScript code, QTBUG-99355");
QQmlEngine e;
PREPEND_NAMESPACE(listView) created(&e);
QQmlListReference children(&created, "data");
QCOMPARE(children.count(), 1);
auto *view = qobject_cast<PREPEND_NAMESPACE(listView_ListView) *>(children.at(0));
QVERIFY(view);
QQmlListModel *model = qvariant_cast<QQmlListModel *>(view->model());
QVERIFY(model);
QCOMPARE(model->count(), 0);
created.appendDigit(u"5"_s);
QCOMPARE(model->count(), 1);
created.appendOperator(u"+"_s);
QCOMPARE(model->count(), 2);
// TODO: add more testing (e.g. check that values are actually recorded)
}
void tst_qmltc::bindingOnValueType()
{
QQmlEngine e;
PREPEND_NAMESPACE(bindingOnValueType) created(&e);
QQmlListReference children(&created, "data");
QCOMPARE(children.count(), 1);
PREPEND_NAMESPACE(bindingOnValueType_QtObject) *subItem =
qobject_cast<PREPEND_NAMESPACE(bindingOnValueType_QtObject) *>(children.at(0));
QVERIFY(subItem);
QCOMPARE(subItem->value(), 1);
QCOMPARE(created.font().pixelSize(), 2 * subItem->value());
subItem->setValue(3);
QCOMPARE(created.font().pixelSize(), 2 * subItem->value());
}
void tst_qmltc::keyEvents()
{
QQmlEngine e;
PREPEND_NAMESPACE(keyEvents) created(&e);
QCOMPARE(created.k(), QStringLiteral(""));
QKeyEvent keyPlusEvent(QEvent::KeyPress, Qt::Key_Plus, Qt::NoModifier);
QCoreApplication::sendEvent(&created, &keyPlusEvent);
QCOMPARE(created.k(), QStringLiteral("+"));
QKeyEvent keySlashEvent(QEvent::KeyPress, Qt::Key_Slash, Qt::NoModifier);
QCoreApplication::sendEvent(&created, &keySlashEvent);
QCOMPARE(created.k(), QStringLiteral("/"));
QKeyEvent keyUnknownhEvent(QEvent::KeyPress, Qt::Key_Comma, Qt::NoModifier);
QCoreApplication::sendEvent(&created, &keyUnknownhEvent);
QCOMPARE(created.k(), QStringLiteral("unknown"));
// repeat for postEvents
QCoreApplication::postEvent(&created,
new QKeyEvent(QEvent::KeyPress, Qt::Key_Plus, Qt::NoModifier));
QCoreApplication::sendPostedEvents(&created);
QCOMPARE(created.k(), QStringLiteral("+"));
QCoreApplication::postEvent(&created,
new QKeyEvent(QEvent::KeyPress, Qt::Key_F33, Qt::NoModifier));
QCoreApplication::sendPostedEvents(&created);
QCOMPARE(created.k(), QStringLiteral("unknown"));
}
void tst_qmltc::privateProperties()
{
QQmlEngine e;
PREPEND_NAMESPACE(privatePropertySubclass) created(&e);
QCOMPARE(created.dummy(), u"bar"_s);
QCOMPARE(created.strAlias(), u"foobar"_s);
QCOMPARE(created.smthAlias(), 42);
auto privateCreated = static_cast<PrivatePropertyTypePrivate *>(QObjectPrivate::get(&created));
QVERIFY(privateCreated);
QCOMPARE(privateCreated->foo(), u"Smth is: 42"_s);
ValueTypeGroup vt = privateCreated->vt();
QCOMPARE(vt.count(), 11);
TestTypeGrouped *group = privateCreated->getGroup();
QCOMPARE(group->getCount(), 43);
QCOMPARE(group->getStr(), created.strAlias());
}
void tst_qmltc::calqlatrBits()
{
QQmlEngine e;
PREPEND_NAMESPACE(calqlatrBits) created(&e);
QQmlListReference children(&created, "data");
QCOMPARE(children.count(), 2);
auto context = e.contextForObject(&created);
QQuickText *textItem = qobject_cast<QQuickText *>(context->objectForName(u"textItem"_s));
QVERIFY(textItem);
// NB: not at index 0 due to AnimationController and strange logic of
// appending items (when these items are not QQuickItem based?)
QCOMPARE(children.at(1), textItem);
QCOMPARE(textItem->wrapMode(), QQuickText::WrapAnywhere);
QQuickAnimationController *controller =
qobject_cast<QQuickAnimationController *>(context->objectForName(u"controller"_s));
QVERIFY(controller);
QCOMPARE(children.at(0), controller);
QQuickNumberAnimation *anim = qobject_cast<QQuickNumberAnimation *>(controller->animation());
// sanity
QVERIFY(anim);
QCOMPARE(anim->target(), textItem);
QCOMPARE(anim->property(), u"scale"_s);
QCOMPARE(anim->duration(), 50);
QCOMPARE(anim->from(), 1);
QCOMPARE(anim->to(), 0.5);
QVERIFY(!anim->isRunning());
QCOMPARE(anim->easing().type(), QEasingCurve::InOutQuad);
controller->completeToEnd();
QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
QSignalSpy scaleChangedSpy(textItem, &QQuickItem::scaleChanged);
controller->completeToBeginning();
QTRY_VERIFY(scaleChangedSpy.size() > 0);
}
void tst_qmltc::trickyPropertyChangeAndSignalHandlers()
{
QQmlEngine e;
PREPEND_NAMESPACE(propertyChangeAndSignalHandlers) created(&e);
// sanity
QCOMPARE(created.aChangedCount1(), 0);
QCOMPARE(created.bChangedCount1(), 0);
QCOMPARE(created.cChangedCount1(), 0);
QCOMPARE(created.dChangedCount1(), 0);
QCOMPARE(created.cChangedCount2(), 0);
QCOMPARE(created.dChangedCount2(), 0);
QCOMPARE(created.cChangedCount3(), 0);
QCOMPARE(created.dChangedCount3(), 0);
QCOMPARE(created.dChangedStr3(), QString());
QCOMPARE(created.cChangedCount4(), 0);
QCOMPARE(created.dChangedCount4(), 0);
QCOMPARE(created.dChangedStr4(), QString());
QQmlContext *ctx = e.contextForObject(&created);
QVERIFY(ctx);
TypeWithProperties *one = qobject_cast<TypeWithProperties *>(ctx->objectForName(u"one"_s));
QVERIFY(one);
TypeWithProperties *two = qobject_cast<TypeWithProperties *>(ctx->objectForName(u"two"_s));
QVERIFY(two);
TypeWithProperties *three = qobject_cast<TypeWithProperties *>(ctx->objectForName(u"three"_s));
QVERIFY(three);
TypeWithProperties *four = qobject_cast<TypeWithProperties *>(ctx->objectForName(u"four"_s));
QVERIFY(four);
one->setA(10);
QCOMPARE(created.aChangedCount1(), 1);
one->setB(u"1"_s);
QCOMPARE(created.bChangedCount1(), 1);
one->setC(2.5);
QCOMPARE(created.cChangedCount1(), 1);
one->setD(-10);
QCOMPARE(created.dChangedCount1(), 1);
two->setC(44.5);
QCOMPARE(created.cChangedCount2(), 1);
three->setC(42.0);
QCOMPARE(created.cChangedCount3(), 42);
three->setD(10);
QCOMPARE(created.dChangedCount3(), 10);
QCOMPARE(created.dChangedStr3(), u"d changed"_s);
four->setC(1.5);
QCOMPARE(created.cChangedCount4(), 2); // cChangedCount4 is int, so 0.5 is truncated
four->setD(84);
// Note: due to signal-over-property-change-handler preference, we bind to
// signal in the case when the property is both bindable and notifiable. in
// this test, it would mean that we get proper dChanged*4 values intead of
// `undefined` junk
QCOMPARE(created.dChangedCount4(), 42);
QCOMPARE(created.dChangedStr4(), u"d changed!"_s);
created.changeProperties1();
QCOMPARE(created.aChangedCount1(), 2);
QCOMPARE(created.bChangedCount1(), 2);
QCOMPARE(created.cChangedCount1(), 2);
created.changeProperties2();
QCOMPARE(created.cChangedCount2(), 2);
created.changeProperties3(22);
QCOMPARE(created.cChangedCount3(), 22);
TypeWithProperties *five = qobject_cast<TypeWithProperties *>(ctx->objectForName(u"five"_s));
QVERIFY(five);
const Qt::MouseButtons a = Qt::RightButton | Qt::MiddleButton;
const Qt::MouseButton b = Qt::LeftButton;
emit five->signalWithEnum(a, b);
QCOMPARE(Qt::MouseButton(five->property("mouseButtonA").toInt()), a);
QCOMPARE(Qt::MouseButtons(five->property("mouseButtonB").toInt()), b);
}
void tst_qmltc::valueTypeListProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(valueTypeListProperty) created(&e);
QList<int> intsRef = created.arrayOfInts();
QCOMPARE(intsRef.size(), 4);
QList<int> intsGroundTruth = { 1, 0, 42, -5 };
QCOMPARE(intsRef, intsGroundTruth);
QCOMPARE(created.arrayOfInts().at(2), 42);
created.incrementPlease();
QCOMPARE(created.arrayOfInts().at(2), 43);
QList<QFont> arrayOfFonts = created.arrayOfFonts();
QCOMPARE(arrayOfFonts.size(), 2);
QCOMPARE(arrayOfFonts[0].family(), u"Arial"_s);
QCOMPARE(arrayOfFonts[1].family(), u"Comic Sans"_s);
QList<QColor> arrayOfColors = created.arrayOfColors();
QCOMPARE(arrayOfColors.size(), 3);
QVERIFY(arrayOfColors[0].red() > 0);
QCOMPARE(arrayOfColors[0].green(), 0);
QCOMPARE(arrayOfColors[0].blue(), 0);
QCOMPARE(arrayOfColors[1].red(), 0);
QVERIFY(arrayOfColors[1].green() > 0);
QCOMPARE(arrayOfColors[1].blue(), 0);
QCOMPARE(arrayOfColors[2].red(), 0);
QCOMPARE(arrayOfColors[2].green(), 0);
QVERIFY(arrayOfColors[2].blue() > 0);
QQmlListReference arrayOfInts2(&created, "arrayOfInts");
QVERIFY(!arrayOfInts2.isValid());
QQmlListReference arrayOfFonts2(&created, "arrayOfFonts");
QVERIFY(!arrayOfFonts2.isValid());
QQmlListReference arrayOfColors2(&created, "arrayOfColors");
QVERIFY(!arrayOfColors2.isValid());
}
void tst_qmltc::translations()
{
{
QQmlEngine e;
PREPEND_NAMESPACE(translations) created(&e);
QCOMPARE(created.alsoTranslated(), u"Bye bye!"_s);
QCOMPARE(created.text(), u"Bye bye!"_s);
QCOMPARE(created.hardcodedContext(), u"Bye bye!"_s);
QCOMPARE(created.anotherContext(), u"Bye bye!"_s);
QCOMPARE(created.toBeTranslatedLater(), u"Bye bye!"_s);
QCOMPARE(created.toBeTranslatedLaterWithHardcodedContext(), u"Bye bye!"_s);
QCOMPARE(created.translatedN(), u"The solution is 42"_s);
QCOMPARE(created.translatedNWithContextAndAmbiguation(), u"The solution has 42 degrees"_s);
QCOMPARE(created.translatedNWithContextAndAmbiguation2(), u"The solution has 43 degrees"_s);
QCOMPARE(created.combination(), u"Bye bye!"_s);
QTranslator translator;
QVERIFY(translator.load(u"translations_ge.qm"_s, u":/i18n"_s));
QVERIFY(qApp->installTranslator(&translator));
e.retranslate();
QCOMPARE(created.text(), u"Tschüssi!"_s);
QCOMPARE(created.alsoTranslated(), u"Tschüssi!"_s);
QCOMPARE(created.hardcodedContext(), u"Tschüssi!"_s);
QCOMPARE(created.anotherContext(), u"Bis später!"_s);
QCOMPARE(created.toBeTranslatedLater(), u"Bye bye!"_s);
QCOMPARE(created.toBeTranslatedLaterWithHardcodedContext(), u"Bye bye!"_s);
QCOMPARE(created.translatedN(), u"Die Lösung ist 42"_s);
QCOMPARE(created.translatedNWithContextAndAmbiguation(),
u"Die Lösung des Problems ist 42 grad."_s);
QCOMPARE(created.translatedNWithContextAndAmbiguation2(), u"Die Lösung ist 43 grad warm"_s);
QCOMPARE(created.combination(), u"Tschüssi!"_s);
}
{
QQmlEngine e;
PREPEND_NAMESPACE(translationsById) created(&e);
QCOMPARE(created.alsoTranslated(), u"ID1"_s);
QCOMPARE(created.text(), u"ID1"_s);
QCOMPARE(created.toBeTranslatedLater(), u"ID1"_s);
QCOMPARE(created.translatedN(), u"ID2"_s);
QTranslator translator;
QVERIFY(translator.load(u"translationsById_ge.qm"_s, u":/i18n"_s));
QVERIFY(qApp->installTranslator(&translator));
e.retranslate();
QCOMPARE(created.text(), u"Tschüssi!"_s);
QCOMPARE(created.alsoTranslated(), u"Tschüssi!"_s);
QCOMPARE(created.toBeTranslatedLater(), u"ID1"_s);
QCOMPARE(created.translatedN(), u"Ich sehe 5 Äpfeln"_s);
}
}
void tst_qmltc::repeaterCrash()
{
QQmlEngine e;
PREPEND_NAMESPACE(repeaterCrash) fromQmltc(&e);
QQmlComponent component(&e, u"qrc:/qt/qml/QmltcTests/repeaterCrash.qml"_s);
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QQuickItem> fromEngine(qobject_cast<QQuickItem *>(component.create()));
QVERIFY(fromEngine);
const int size = 7;
const auto listFromEngine = fromEngine->childItems();
const auto listFromQmltc = fromQmltc.childItems();
QCOMPARE(listFromEngine.size(), size);
QCOMPARE(listFromQmltc.size(), size);
for (int i = 0; i < size; i++) {
// the repeater itself has no objName property
if (i == 5)
continue;
const QVariant nameFromEngine = listFromEngine.at(i)->property("objName");
const QVariant nameFromQmltc = listFromQmltc.at(i)->property("objName");
QVERIFY(nameFromEngine.isValid());
QVERIFY(nameFromQmltc.isValid());
QCOMPARE(nameFromQmltc.toString(), nameFromEngine.toString());
}
}
void tst_qmltc::generalizedGroupedProperty()
{
QQmlEngine e;
{
PREPEND_NAMESPACE(generalizedGroupedProperty) fromQmltc(&e);
QCOMPARE(fromQmltc.getGroup()->getCount(), 5);
fromQmltc.setMyInt(42);
QCOMPARE(fromQmltc.getGroup()->getCount(), 42);
fromQmltc.getGroup()->setCount(55);
QCOMPARE(fromQmltc.getGroup()->getCount(), 55);
QCOMPARE(fromQmltc.myInt(), 42);
QCOMPARE(fromQmltc.getGroup()->getFormula(), 8);
QCOMPARE(fromQmltc.getGroup()->getStr(), u"Hello World!"_s);
qmlExecuteDeferred(&fromQmltc);
QCOMPARE(fromQmltc.getGroup()->getCount(), 55);
QCOMPARE(fromQmltc.getGroup()->getFormula(), 8);
QCOMPARE(fromQmltc.getGroup()->getStr(), u"Hello World!"_s);
}
{
QQmlComponent component(&e, u"qrc:/qt/qml/QmltcTests/generalizedGroupedProperty.qml"_s);
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> fromEngine(component.create());
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("count"), 5);
fromEngine->setProperty("myInt", 43);
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("count"), 43);
fromEngine->property("group").value<QObject *>()->setProperty("count", 56);
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("count"), 56);
QCOMPARE(fromEngine->property("myInt").value<int>(), 43);
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("formula").value<int>(),
8);
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("str").toString(),
u"Hello World!"_s);
qmlExecuteDeferred(fromEngine.data());
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("count"), 56);
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("formula").value<int>(),
8);
QCOMPARE(fromEngine->property("group").value<QObject *>()->property("str").toString(),
u"Hello World!"_s);
}
}
void tst_qmltc::appendToQQmlListProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(appendToQQmlListProperty) fromQmltc(&e);
QQmlComponent component(&e, u"qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"_s);
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> fromEngine(component.create());
auto itemFromEngine = fromEngine->property("myItem").value<QQuickItem *>();
auto itemFromQmltc = fromQmltc.myItem();
QCOMPARE(itemFromEngine->children().size(), 3);
QCOMPARE(itemFromQmltc->children().size(), 3);
QCOMPARE(itemFromEngine->children().at(0)->property("hello"), u"hello1"_s);
QCOMPARE(itemFromEngine->children().at(1)->property("hello"), u"hello2"_s);
QCOMPARE(itemFromEngine->children().at(2)->property("hello"), u"I am a Rectangle."_s);
QCOMPARE(itemFromQmltc->children().at(0)->property("hello"), u"hello1"_s);
QCOMPARE(itemFromQmltc->children().at(1)->property("hello"), u"hello2"_s);
QCOMPARE(itemFromQmltc->children().at(2)->property("hello"), u"I am a Rectangle."_s);
QVariantList referenceComponentList = { QVariant(u"Hello"_s), QVariant(42), QVariant(4.0) };
QCOMPARE(fromQmltc.myComponentList().toList(), referenceComponentList);
QCOMPARE(fromEngine->property("myComponentList").toList(), referenceComponentList);
QList<int> referenceValueTypeList = { 12489, 10, 42 };
QVariantList referenceValueTypeList2 = { 12489, 10, 42 };
QCOMPARE(fromQmltc.myValueTypeList().toList(), referenceValueTypeList);
QCOMPARE(fromEngine->property("myValueTypeList").toList(), referenceValueTypeList2);
QQmlListReference qtObjectsFromQmltc(&fromQmltc, "myQtObjectList");
QVERIFY(qtObjectsFromQmltc.isValid());
QQmlListReference qtObjectsFromEngine(fromEngine.data(), "myQtObjectList");
QVERIFY(qtObjectsFromEngine.isValid());
QCOMPARE(qtObjectsFromQmltc.size(), 3);
QCOMPARE(qtObjectsFromEngine.size(), 3);
QCOMPARE(qtObjectsFromQmltc.at(0)->property("hello"), u"Guten Morgen!"_s);
QCOMPARE(qtObjectsFromQmltc.at(1)->property("hello"), u"I am a Rectangle."_s);
QCOMPARE(qtObjectsFromQmltc.at(2)->property("hello"), u"Moin!"_s);
QCOMPARE(qtObjectsFromEngine.at(0)->property("hello"), u"Guten Morgen!"_s);
QCOMPARE(qtObjectsFromEngine.at(1)->property("hello"), u"I am a Rectangle."_s);
QCOMPARE(qtObjectsFromEngine.at(2)->property("hello"), u"Moin!"_s);
QQmlListReference qtHWFromQmltc(&fromQmltc, "myHelloWorldList");
QQmlListReference qtHWFromEngine(fromEngine.data(), "myHelloWorldList");
QCOMPARE(qtHWFromQmltc.size(), 3);
QCOMPARE(qtHWFromEngine.size(), 3);
QCOMPARE(qtHWFromQmltc.at(0)->property("hello"), u"Good morning1"_s);
QCOMPARE(qtHWFromQmltc.at(1)->property("hello"), u"Good morning2"_s);
QCOMPARE(qtHWFromQmltc.at(2)->property("hello"), u"Good morning3"_s);
QCOMPARE(qtHWFromEngine.at(0)->property("hello"), u"Good morning1"_s);
QCOMPARE(qtHWFromEngine.at(1)->property("hello"), u"Good morning2"_s);
QCOMPARE(qtHWFromEngine.at(2)->property("hello"), u"Good morning3"_s);
// make sure that extensions are handled correctly, as they require a slightly different code
// path to be generated
QQmlListReference extendedFromQmltc(fromQmltc.extended().value<QObject *>(), "myList");
QVERIFY(extendedFromQmltc.isValid());
QQmlListReference extendedFromEngine(fromEngine->property("extended").value<QObject *>(),
"myList");
QVERIFY(extendedFromEngine.isValid());
QCOMPARE(extendedFromQmltc.size(), 3);
QCOMPARE(extendedFromEngine.size(), 3);
}
// test classes to access protected member typecount
class myInlineComponentA : public PREPEND_NAMESPACE(inlineComponents_A)
{
friend class tst_qmltc;
};
class myInlineComponentB : public PREPEND_NAMESPACE(inlineComponents_B)
{
friend class tst_qmltc;
};
class myInlineComponentMyComponent : public PREPEND_NAMESPACE(inlineComponents_MyComponent)
{
friend class tst_qmltc;
};
class myInlineComponentAPlus : public PREPEND_NAMESPACE(inlineComponents_APlus)
{
friend class tst_qmltc;
};
class myInlineComponentAPlusPlus : public PREPEND_NAMESPACE(inlineComponents_APlusPlus)
{
friend class tst_qmltc;
};
void tst_qmltc::inlineComponents()
{
QQmlEngine e;
PREPEND_NAMESPACE(inlineComponents) createdByQmltc(&e);
QQmlComponent component(&e);
component.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/inlineComponents.qml"_s));
QVERIFY(!component.isError());
QScopedPointer<QObject> createdByComponent(component.create());
const QString testData = u"Major"_s;
const QString testData2 = u"Tom"_s;
// check typecounts of inline components
QCOMPARE(myInlineComponentA::q_qmltc_typeCount(), 1u);
QCOMPARE(myInlineComponentB::q_qmltc_typeCount(), 1u);
QCOMPARE(myInlineComponentAPlus::q_qmltc_typeCount(), 2u);
QCOMPARE(myInlineComponentAPlusPlus::q_qmltc_typeCount(), 3u);
QCOMPARE(myInlineComponentMyComponent::q_qmltc_typeCount(), 2u);
// test properties with component types, see if they are generated
// and if they contain one of the properties of the component
{
auto firstFromQmltc = createdByQmltc.myMyComponent();
auto secondFromQmltc = createdByQmltc.myMyComponentAsQtObject();
QVERIFY(firstFromQmltc);
QVERIFY(secondFromQmltc);
QCOMPARE(firstFromQmltc->myX(), u"SharedX"_s);
QCOMPARE(secondFromQmltc->property("myX"), u"SharedX"_s);
QCOMPARE(createdByQmltc.signalTriggered(), 0);
emit createdByQmltc.myMyComponentSignal(firstFromQmltc);
QCOMPARE(createdByQmltc.signalTriggered(), 123);
auto firstFromComponent = createdByComponent->property("myMyComponent").value<QObject *>();
auto secondFromComponent =
createdByComponent->property("myMyComponentAsQtObject").value<QObject *>();
QVERIFY(firstFromComponent);
QVERIFY(secondFromComponent);
QCOMPARE(firstFromComponent->property("myX"), u"SharedX"_s);
QCOMPARE(secondFromComponent->property("myX"), u"SharedX"_s);
QCOMPARE(createdByComponent->property("signalTriggered"), 0);
const QMetaObject *createdByComponentMetaObject = createdByComponent->metaObject();
QVERIFY(createdByComponentMetaObject);
const int indexOfMethod =
createdByComponentMetaObject->indexOfMethod("myMyComponentSignal(QObject*)");
QVERIFY(indexOfMethod != -1);
auto method = createdByComponentMetaObject->method(indexOfMethod);
QVERIFY(method.isValid());
QVERIFY(method.invoke(createdByComponent.get(), Q_ARG(QObject *, firstFromComponent)));
QCOMPARE(createdByComponent->property("signalTriggered"), 123);
}
// test if nonrecursive components behave well
{
auto *myMyComponentFromQmltc = (PREPEND_NAMESPACE(
inlineComponents_MyComponent_3) *)createdByQmltc.myMyComponentComponent();
auto *myMyComponentFromQmltc2 = (PREPEND_NAMESPACE(
inlineComponents_MyComponent_4) *)createdByQmltc.myMyComponentComponent2();
QVERIFY(myMyComponentFromQmltc);
QVERIFY(myMyComponentFromQmltc2);
auto *myMyComponentFromComponent =
createdByComponent->property("myMyComponentComponent").value<QObject *>();
// second MyComponent
auto *myMyComponentFromComponent2 =
createdByComponent->property("myMyComponentComponent2").value<QObject *>();
QVERIFY(myMyComponentFromComponent);
QVERIFY(myMyComponentFromComponent2);
// test writing and reading own properties on myMyComponentComponent
QCOMPARE(myMyComponentFromQmltc->myY(), u"NotSharedY1"_s);
myMyComponentFromQmltc->setMyY(testData);
QCOMPARE(myMyComponentFromQmltc->myY(), testData);
QCOMPARE(myMyComponentFromComponent->property("myY"), u"NotSharedY1"_s);
QVERIFY(myMyComponentFromComponent->setProperty("myY", testData));
QCOMPARE(myMyComponentFromComponent->property("myY"), testData);
// test writing and reading own properties on myMyComponentComponent2
QCOMPARE(myMyComponentFromQmltc2->myY2(), u"NotSharedY2"_s);
myMyComponentFromQmltc2->setMyY2(testData2);
QCOMPARE(myMyComponentFromQmltc2->myY2(), testData2);
QCOMPARE(myMyComponentFromComponent2->property("myY2"), u"NotSharedY2"_s);
QVERIFY(myMyComponentFromComponent2->setProperty("myY2", testData2));
QCOMPARE(myMyComponentFromComponent2->property("myY2"), testData2);
// test writing and reading inline component properties on myMyComponentComponent
QCOMPARE(myMyComponentFromQmltc->myX(), u"MyComponent1"_s);
myMyComponentFromQmltc->setMyX(testData);
QCOMPARE(myMyComponentFromQmltc->myX(), testData);
QCOMPARE(myMyComponentFromComponent->property("myX"), u"MyComponent1"_s);
QVERIFY(myMyComponentFromComponent->setProperty("myX", testData));
QCOMPARE(myMyComponentFromComponent->property("myX"), testData);
// test writing and reading inline component properties on myMyComponentComponent2
QCOMPARE(myMyComponentFromQmltc2->myX(), u"MyComponent2"_s);
myMyComponentFromQmltc2->setMyX(testData2);
QCOMPARE(myMyComponentFromQmltc2->myX(), testData2);
// check that the other x property was not overridden
QCOMPARE(myMyComponentFromQmltc->myX(), testData);
QCOMPARE(myMyComponentFromComponent2->property("myX"), u"MyComponent2"_s);
QVERIFY(myMyComponentFromComponent2->setProperty("myX", testData2));
QCOMPARE(myMyComponentFromComponent2->property("myX"), testData2);
// check that the other x property was not overridden
QCOMPARE(myMyComponentFromComponent->property("myX"), testData);
// check if the literal binding is working on myProperty
QCOMPARE(myMyComponentFromQmltc->myProperty(), u"check literal binding"_s);
QCOMPARE(myMyComponentFromQmltc2->myProperty(), u"check literal binding"_s);
QCOMPARE(myMyComponentFromComponent->property("myProperty"), u"check literal binding"_s);
QCOMPARE(myMyComponentFromComponent2->property("myProperty"), u"check literal binding"_s);
// check the Item in the MyComponent component
QCOMPARE(myMyComponentFromQmltc->children().size(), 1);
QCOMPARE(myMyComponentFromQmltc2->children().size(), 1);
auto *childFromQmltc =
(PREPEND_NAMESPACE(inlineComponents_MyComponent_Item) *)myMyComponentFromQmltc
->children()
.front();
auto *childFromQmltc2 =
(PREPEND_NAMESPACE(inlineComponents_MyComponent_Item) *)myMyComponentFromQmltc2
->children()
.front();
QVERIFY(childFromQmltc);
QVERIFY(childFromQmltc2);
QCOMPARE(myMyComponentFromComponent->children().size(), 1);
QCOMPARE(myMyComponentFromComponent2->children().size(), 1);
auto *childFromComponent = myMyComponentFromComponent->children().front();
auto *childFromComponent2 = myMyComponentFromComponent2->children().front();
QVERIFY(childFromComponent);
QVERIFY(childFromComponent2);
// test writing and reading inline components childrens
// .. also tests at the same time if the literal bindings inside inline components are
// set correctly
QCOMPARE(childFromQmltc->myZ(), u"SharedZ"_s);
QCOMPARE(childFromQmltc2->myZ(), u"SharedZ"_s);
childFromQmltc->setMyZ(testData);
childFromQmltc2->setMyZ(testData2);
QCOMPARE(childFromQmltc->myZ(), testData);
QCOMPARE(childFromQmltc2->myZ(), testData2);
QCOMPARE(childFromComponent->property("myZ"), u"SharedZ"_s);
QCOMPARE(childFromComponent2->property("myZ"), u"SharedZ"_s);
QVERIFY(childFromComponent->setProperty("myZ", testData));
QVERIFY(childFromComponent2->setProperty("myZ", testData2));
QCOMPARE(childFromComponent->property("myZ"), testData);
QCOMPARE(childFromComponent2->property("myZ"), testData2);
// verify aliases inside of inline components:
childFromQmltc->setAliasToMyX(testData);
childFromQmltc2->setAliasToMyX(testData2);
QCOMPARE(myMyComponentFromQmltc->myX(), testData);
QCOMPARE(childFromQmltc->aliasToMyX(), testData);
QCOMPARE(myMyComponentFromQmltc2->myX(), testData2);
QCOMPARE(childFromQmltc2->aliasToMyX(), testData2);
childFromQmltc2->setAliasToMyX(testData2);
QCOMPARE(myMyComponentFromQmltc2->myX(), testData2);
QCOMPARE(childFromQmltc2->aliasToMyX(), testData2);
QVERIFY(childFromComponent->setProperty("aliasToMyX", testData));
QVERIFY(childFromComponent2->setProperty("aliasToMyX", testData2));
QCOMPARE(myMyComponentFromComponent->property("myX"), testData);
QCOMPARE(childFromComponent->property("aliasToMyX"), testData);
QCOMPARE(myMyComponentFromComponent2->property("myX"), testData2);
QCOMPARE(childFromComponent2->property("aliasToMyX"), testData2);
QVERIFY(childFromComponent2->setProperty("aliasToMyX", testData2));
QCOMPARE(myMyComponentFromComponent2->property("myX"), testData2);
QCOMPARE(childFromComponent2->property("aliasToMyX"), testData2);
}
// test if recursive components are behaving well
{
auto *myAFromQmltc =
(PREPEND_NAMESPACE(inlineComponents_A_1) *)createdByQmltc.myAComponent();
auto *innerBFromQmltc = (PREPEND_NAMESPACE(inlineComponents_A_B) *)myAFromQmltc->b();
QVERIFY(innerBFromQmltc);
auto *innerAFromQmltc = (PREPEND_NAMESPACE(inlineComponents_A_B_A) *)innerBFromQmltc->a();
QVERIFY(innerAFromQmltc);
constexpr bool typeNotCompiledAsQQmlComponent =
std::is_same_v<decltype(myAFromQmltc->b()),
PREPEND_NAMESPACE(inlineComponents_B) *>;
constexpr bool typeNotCompiledAsQQmlComponent2 =
std::is_same_v<decltype(innerBFromQmltc->a()),
PREPEND_NAMESPACE(inlineComponents_A) *>;
QVERIFY(typeNotCompiledAsQQmlComponent);
QVERIFY(typeNotCompiledAsQQmlComponent2);
auto *myAFromComponent = createdByComponent->property("myAComponent").value<QObject *>();
auto *innerBFromComponent = myAFromComponent->property("b").value<QObject *>();
QVERIFY(innerBFromComponent);
auto *innerAFromComponent = innerBFromComponent->property("a").value<QObject *>();
QVERIFY(innerAFromComponent);
QCOMPARE(myAFromComponent->property("data"), u"Hello From Outside!"_s);
QCOMPARE(innerAFromComponent->property("data"), u"Hello From Inside!"_s);
}
// test if ids in inlineComponents are not getting mixed up with those from the root component
{
auto *conflictingComponentTomFromQmltc =
createdByQmltc.tom()
.value<PREPEND_NAMESPACE(inlineComponents_ConflictingComponent_1) *>();
auto *conflictingComponentJerryFromQmltc =
createdByQmltc.jerry()
.value<PREPEND_NAMESPACE(inlineComponents_ConflictingComponent_2) *>();
auto *conflictingComponentTomFromComponent =
createdByComponent->property("tom").value<QObject *>();
auto *conflictingComponentJerryFromComponent =
createdByComponent->property("jerry").value<QObject *>();
QCOMPARE(conflictingComponentTomFromQmltc->output(), u"Tom: outer"_s);
QCOMPARE(conflictingComponentJerryFromQmltc->output(), u"Jerry: outer"_s);
QCOMPARE(createdByQmltc.output(), u"inner"_s);
QCOMPARE(conflictingComponentTomFromComponent->property("output"), u"Tom: outer"_s);
QCOMPARE(conflictingComponentJerryFromComponent->property("output"), u"Jerry: outer"_s);
QCOMPARE(createdByComponent->property("output"), u"inner"_s);
QVERIFY(conflictingComponentTomFromQmltc->children()[0]->setProperty("conflicting",
u"Tomtom"_s));
QCOMPARE(conflictingComponentTomFromQmltc->output(), u"Tom: Tomtom"_s);
QCOMPARE(conflictingComponentJerryFromQmltc->output(), u"Jerry: outer"_s);
QCOMPARE(createdByQmltc.output(), u"inner"_s);
QVERIFY(conflictingComponentTomFromComponent->children()[0]->setProperty("conflicting",
u"Tomtom"_s));
QCOMPARE(conflictingComponentTomFromComponent->property("output"), u"Tom: Tomtom"_s);
QCOMPARE(conflictingComponentJerryFromComponent->property("output"), u"Jerry: outer"_s);
QCOMPARE(createdByComponent->property("output"), u"inner"_s);
QVERIFY(createdByQmltc.setProperty("conflicting", u"Rootroot"_s));
QCOMPARE(conflictingComponentTomFromQmltc->output(), u"Tom: Tomtom"_s);
QCOMPARE(conflictingComponentJerryFromQmltc->output(), u"Jerry: outer"_s);
QCOMPARE(createdByQmltc.output(), u"Rootroot"_s);
QVERIFY(createdByComponent->setProperty("conflicting", u"Rootroot"_s));
QCOMPARE(conflictingComponentTomFromComponent->property("output"), u"Tom: Tomtom"_s);
QCOMPARE(conflictingComponentJerryFromComponent->property("output"), u"Jerry: outer"_s);
QCOMPARE(createdByComponent->property("output"), u"Rootroot"_s);
}
// check 'empty' components that just get stuff through inheritance
QCOMPARE(createdByQmltc.property("empty").value<QObject *>()->property("objectName"),
u"EmptyComponentObject"_s);
QCOMPARE(createdByComponent->property("empty").value<QObject *>()->property("objectName"),
u"EmptyComponentObject"_s);
// check that inline components can "hide" other imports
auto myRectangleFromQmltc =
createdByQmltc.property("inlineComponentFoundBeforeOtherImports").value<QObject *>();
QVERIFY(myRectangleFromQmltc);
QCOMPARE(myRectangleFromQmltc->property("myData"), u"Not from QtQuick.Rectangle"_s);
auto myRectangleFromComponent =
createdByComponent->property("inlineComponentFoundBeforeOtherImports")
.value<QObject *>();
QVERIFY(myRectangleFromComponent);
QCOMPARE(myRectangleFromComponent->property("myData"), u"Not from QtQuick.Rectangle"_s);
// check that inline components are resolved in the correct order
{
auto componentFromQmltc = createdByQmltc.inlineComponentOrder()
.value<PREPEND_NAMESPACE(inlineComponents_IC2_1) *>();
auto componentFromComponent =
createdByComponent->property("inlineComponentOrder").value<QObject *>();
QCOMPARE(componentFromQmltc->color(), QColorConstants::Blue);
QCOMPARE(componentFromComponent->property("color"), QColorConstants::Blue);
}
// check that inline components can be used in lists
{
QQmlListReference childrenFromQmltc(&createdByQmltc, "componentList");
QQmlListReference childrenFromComponent(createdByComponent.data(), "componentList");
QQmlListReference testList(&createdByQmltc, "testList");
QCOMPARE(testList.size(), 3);
QCOMPARE(childrenFromComponent.size(), 2);
QCOMPARE(childrenFromQmltc.size(), 2);
QCOMPARE(childrenFromQmltc.at(0)->property("age"), 65);
QCOMPARE(childrenFromComponent.at(0)->property("age"), 65);
QCOMPARE(childrenFromQmltc.at(1)->property("age"), 62);
QCOMPARE(childrenFromComponent.at(1)->property("age"), 62);
}
}
void tst_qmltc::aliases()
{
QQmlEngine e;
PREPEND_NAMESPACE(aliases) fromQmltc(&e);
QQmlComponent component(&e);
component.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/aliases.qml"_s));
QVERIFY(!component.isError());
QScopedPointer<QObject> fromComponent(component.create());
const QString testString = u"myTestString"_s;
QCOMPARE(fromQmltc.aliasToAlias(), u"Hello World!"_s);
QCOMPARE(fromComponent->property("aliasToAlias"), u"Hello World!"_s);
fromQmltc.setAliasToAlias(testString);
QVERIFY(fromComponent->setProperty("aliasToAlias", testString));
QCOMPARE(fromQmltc.aliasToAlias(), testString);
QCOMPARE(fromComponent->property("aliasToAlias"), testString);
QCOMPARE(fromQmltc.aliasToOtherFile(), u"Set me!"_s);
QCOMPARE(fromComponent->property("aliasToOtherFile"), u"Set me!"_s);
fromQmltc.setAliasToOtherFile(testString);
QVERIFY(fromComponent->setProperty("aliasToOtherFile", testString));
QCOMPARE(fromQmltc.aliasToOtherFile(), testString);
QCOMPARE(fromComponent->property("aliasToOtherFile"), testString);
}
void tst_qmltc::inlineComponentsFromDifferentFiles()
{
// check that inline components can be imported from different files
QQmlEngine e;
PREPEND_NAMESPACE(inlineComponentsFromDifferentFiles) createdByQmltc(&e);
QQmlComponent component(&e);
component.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/inlineComponentsFromDifferentFiles.qml"_s));
QVERIFY(!component.isError());
QScopedPointer<QObject> createdByComponent(component.create());
QCOMPARE(createdByQmltc.fromModule1()->objName(), u"IC1"_s);
QCOMPARE(createdByComponent->property("fromModule1")
.value<QObject *>()
->property("objName")
.toString(),
u"IC1"_s);
QCOMPARE(createdByQmltc.fromModule2().value<QObject *>()->property("objName").toString(),
u"IC1"_s);
QCOMPARE(createdByComponent->property("fromModule2")
.value<QObject *>()
->property("objName")
.toString(),
u"IC1"_s);
QCOMPARE(createdByQmltc.fromOtherFile1()->objName(), u"IC1"_s);
QCOMPARE(createdByComponent->property("fromOtherFile1")
.value<QObject *>()
->property("objName")
.toString(),
u"IC1"_s);
QCOMPARE(createdByQmltc.fromOtherFile2()->objName(), u"IC2"_s);
QCOMPARE(createdByComponent->property("fromOtherFile2")
.value<QObject *>()
->property("objName")
.toString(),
u"IC2"_s);
QCOMPARE(createdByQmltc.fromOtherFile3()->objName(), u"IC3"_s);
QCOMPARE(createdByComponent->property("fromOtherFile3")
.value<QObject *>()
->property("objName")
.toString(),
u"IC3"_s);
QCOMPARE(createdByQmltc.reExported()->objName(), u"IC100"_s);
QCOMPARE(createdByComponent->property("reExported")
.value<QObject *>()
->property("objName")
.toString(),
u"IC100"_s);
// test how good/bad inline components mix up with enums (they have very similar syntax)
// hard code enum values for better test readability
const int dog = 4;
QCOMPARE(createdByQmltc.looksLikeEnumIsEnum(), dog);
QCOMPARE(
createdByQmltc.looksLikeEnumIsInlineComponent().value<QObject *>()->property("objName"),
u"IC1"_s);
QCOMPARE(createdByComponent->property("looksLikeEnumIsEnum"), dog);
QCOMPARE(createdByComponent->property("looksLikeEnumIsInlineComponent")
.value<QObject *>()
->property("objName"),
u"IC1"_s);
}
void tst_qmltc::singletons()
{
QQmlEngine e;
PREPEND_NAMESPACE(singletons) createdByQmltc(&e);
// make sure also that singletons are not shared between engines
QQmlEngine e2;
PREPEND_NAMESPACE(singletons) createdByQmltcSecondEngine(&e2);
QQmlComponent component(&e);
component.loadUrl(QUrl(u"qrc:/qt/qml/QmltcTests/singletons.qml"_s));
QVERIFY(!component.isError());
QScopedPointer<QObject> createdByComponent(component.create());
// read default value, write one of the singletons and check that all singletons were written!
{
QCOMPARE(createdByQmltc.cppSingleton1(), 42);
QCOMPARE(createdByQmltcSecondEngine.cppSingleton1(), 42);
QCOMPARE(createdByComponent->property("cppSingleton1"), 42);
QCOMPARE(createdByQmltc.cppSingleton2(), 42);
QCOMPARE(createdByQmltcSecondEngine.cppSingleton2(), 42);
QCOMPARE(createdByComponent->property("cppSingleton2"), 42);
// change the singletonvalue
createdByQmltc.writeSingletonType();
QCOMPARE(createdByComponent->property("cppSingleton1"), 100);
QCOMPARE(createdByQmltcSecondEngine.cppSingleton1(), 42);
QCOMPARE(createdByQmltc.cppSingleton1(), 100);
QCOMPARE(createdByComponent->property("cppSingleton2"), 100);
QCOMPARE(createdByQmltcSecondEngine.cppSingleton2(), 42);
QCOMPARE(createdByQmltc.cppSingleton2(), 100);
}
// same schema for singletons defined in qml
{
QCOMPARE(createdByQmltc.qmlSingleton1(), 42);
QCOMPARE(createdByQmltcSecondEngine.qmlSingleton1(), 42);
QCOMPARE(createdByComponent->property("qmlSingleton1"), 42);
QCOMPARE(createdByQmltc.qmlSingleton2(), 42);
QCOMPARE(createdByQmltcSecondEngine.qmlSingleton2(), 42);
QCOMPARE(createdByComponent->property("qmlSingleton2"), 42);
// change the singletonvalue
createdByQmltc.writeSingletonThing();
// read the others to see if value can be found in all instances
QCOMPARE(createdByQmltc.qmlSingleton1(), 100);
QCOMPARE(createdByQmltcSecondEngine.qmlSingleton1(), 42);
QCOMPARE(createdByComponent->property("qmlSingleton1"), 100);
QCOMPARE(createdByQmltc.qmlSingleton2(), 100);
QCOMPARE(createdByQmltcSecondEngine.qmlSingleton2(), 42);
QCOMPARE(createdByComponent->property("qmlSingleton2"), 100);
}
}
void tst_qmltc::constSignalParameters()
{
QQmlEngine e;
PREPEND_NAMESPACE(mySignals) fromQmltc(&e);
int primitive = 123;
QFont defaultGadget;
QFont gadget;
gadget.setBold(true);
QQuickItem myItem;
myItem.setObjectName("New Name");
// by value
fromQmltc.setPrimitive(123);
emit fromQmltc.signalWithPrimitive(primitive);
QCOMPARE(fromQmltc.primitive(), primitive);
fromQmltc.setGadget(defaultGadget);
emit fromQmltc.signalWithGadget(gadget);
QCOMPARE(fromQmltc.gadget(), gadget);
// by const ref
fromQmltc.setPrimitive(123);
emit fromQmltc.signalWithConstReferenceToPrimitive(primitive);
QCOMPARE(fromQmltc.primitive(), primitive);
fromQmltc.setGadget(defaultGadget);
emit fromQmltc.signalWithConstReferenceToGadget(gadget);
QCOMPARE(fromQmltc.gadget(), gadget);
// by pointer
fromQmltc.setObject(nullptr);
emit fromQmltc.signalWithPointer(&myItem);
QCOMPARE(fromQmltc.object(), &myItem);
fromQmltc.setObject(nullptr);
emit fromQmltc.signalWithPointerToConst(&myItem);
QCOMPARE(fromQmltc.object(), &myItem);
fromQmltc.setObject(nullptr);
emit fromQmltc.signalWithPointerToConst2(&myItem);
QCOMPARE(fromQmltc.object(), &myItem);
fromQmltc.setObject(nullptr);
emit fromQmltc.signalWithConstPointer(&myItem);
QCOMPARE(fromQmltc.object(), &myItem);
fromQmltc.setObject(nullptr);
emit fromQmltc.signalWithConstPointerToConst(&myItem);
QCOMPARE(fromQmltc.object(), &myItem);
fromQmltc.setObject(nullptr);
emit fromQmltc.signalWithConstPointerToConst2(&myItem);
QCOMPARE(fromQmltc.object(), &myItem);
}
void tst_qmltc::cppNamespaces()
{
// see if qmltc works correctly with c++ namespaced types
QQmlEngine e;
PREPEND_NAMESPACE(NamespacedTypes) createdByQmltc(&e);
QCOMPARE(createdByQmltc.myObject()->property("value"), 123);
createdByQmltc.f();
QCOMPARE(createdByQmltc.myObject()->property("value"), 55);
}
void tst_qmltc::namespacedName()
{
// cmake script should be able to auto-fill the namespace of the generated modules, and to
// replace . with ::
QQmlEngine e;
NamespaceTest::Subfolder::Type t(&e);
QCOMPARE(t.data(), u"Hello from namespace"_s);
}
void tst_qmltc::checkExportsAreCompiling()
{
QQmlEngine e;
QmltcExportedTests::HelloExportedWorld w(&e);
QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s);
}
void tst_qmltc::checkExportsNoFileName()
{
QQmlEngine e;
QmltcExportedNoFileNameTest::HelloExportedWorldNoFileName w(&e);
QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s);
}
#if QT_CONFIG(qml_labs) && QT_CONFIG(qml_table_model)
void tst_qmltc::qmlTableModel()
{
QQmlEngine e;
PREPEND_NAMESPACE(QmlTableModel) createdByQmltc(&e);
// check that the tableModel is not default constructed
QVariant model = createdByQmltc.model();
QVERIFY(model.isValid());
QQmlTableModel *tableModel = model.value<QQmlTableModel *>();
QVERIFY(tableModel);
QCOMPARE(tableModel->property("testName").toString(), u"MyTableModel"_s);
}
#endif
void tst_qmltc::urlToString()
{
QQmlEngine e;
PREPEND_NAMESPACE(stringToUrl) createdByQmltc(&e);
// check that the tableModel is not default constructed
QUrl first = createdByQmltc.iconLoader()->source();
QUrl second = createdByQmltc.iconLoader2()->source();
QCOMPARE(first, QUrl(u"qrc:/qt/qml/path/to/font.ttf"_s));
QCOMPARE(second, QUrl(u"qrc:/qt/qml/path/to/font2.ttf"_s));
}
void tst_qmltc::signalConnections()
{
QQmlEngine e;
PREPEND_NAMESPACE(signalConnections) createdByQmltc(&e);
QVERIFY(createdByQmltc.objectName().isEmpty());
createdByQmltc.setCycleFirst(true);
QTRY_VERIFY(!createdByQmltc.cycleFirst());
QCOMPARE(createdByQmltc.objectName(), QLatin1String("first"));
createdByQmltc.setObjectName(QLatin1String("none"));
createdByQmltc.setCycleEnabled(true);
QTRY_VERIFY(!createdByQmltc.cycleEnabled());
createdByQmltc.setCycleFirst(true);
QTRY_VERIFY(!createdByQmltc.cycleFirst());
QCOMPARE(createdByQmltc.objectName(), QLatin1String("none"));
createdByQmltc.setCycleEnabled(true);
QTRY_VERIFY(!createdByQmltc.cycleEnabled());
createdByQmltc.setCycleSecond(true);
QTRY_VERIFY(!createdByQmltc.cycleSecond());
QCOMPARE(createdByQmltc.objectName(), QLatin1String("second"));
}
void tst_qmltc::hpp()
{
QQmlEngine e;
PREPEND_NAMESPACE(hpp) createdByQmltc(&e);
QCOMPARE(createdByQmltc.objectName(), QLatin1String("hpp"));
}
template<typename T>
void checkOverlayAttached(T *createdByQmltc)
{
QObject *attached = qmlAttachedPropertiesObject<QQuickOverlay>(createdByQmltc);
QVERIFY(attached);
QQuickOverlayAttached *overlay = qobject_cast<QQuickOverlayAttached *>(attached);
QVERIFY(overlay);
QCOMPARE(overlay->objectName(), QLatin1String("foos"));
QQmlComponent *modal = overlay->modal();
QVERIFY(modal);
QScopedPointer<QObject> overlayInstance(modal->create());
QVERIFY(!overlayInstance.isNull());
QQuickRectangle *rectangle = qobject_cast<QQuickRectangle *>(overlayInstance.data());
QVERIFY(rectangle);
QCOMPARE(rectangle->color(), QColor::fromString("grey"_L1));
}
void tst_qmltc::attachedComponentProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(attachedComponentProperty) createdByQmltc(&e);
checkOverlayAttached(&createdByQmltc);
QQmlComponent *b = createdByQmltc.b();
QVERIFY(b);
QScopedPointer<QObject> bInstance(b->create());
QQuickRectangle *bRectangle = qobject_cast<QQuickRectangle *>(bInstance.data());
QVERIFY(bRectangle);
QCOMPARE(bRectangle->color(), QColor::fromString("green"_L1));
}
void tst_qmltc::attachedNamespacedProperty()
{
QQmlEngine e;
PREPEND_NAMESPACE(attachedNamespacedProperty) createdByQmltc(&e);
checkOverlayAttached(&createdByQmltc);
}
void tst_qmltc::newLineTranslation()
{
QQmlEngine e;
PREPEND_NAMESPACE(newLineTranslation) createdByQmltc(&e);
QCOMPARE(createdByQmltc.objectName(), "Hello World \n"_L1);
}
void tst_qmltc::nestedWithId()
{
QQmlEngine e;
PREPEND_NAMESPACE(myMatryoshkaItems) createdByQmltc(&e);
QQmlContext *context = qmlContext(&createdByQmltc);
QObject *inner = context->objectForName("inner"_L1);
QVERIFY(inner);
QVERIFY(inner != &createdByQmltc);
QVERIFY(qobject_cast<QmltcTests::MyBaseItem *>(inner));
QVERIFY(!qobject_cast<QmltcTests::MyDerivedItem *>(inner));
}
void tst_qmltc::typedMethodCallOnIdLookup()
{
QQmlEngine e;
PREPEND_NAMESPACE(typedMethodCallOnIdLookup) created(&e);
QCOMPARE(created.property("result").toString(), u"[Hello]"_s);
}
void tst_qmltc::ensureWarningCategoryOverridesSync()
{
const auto overrides = QQmltc::categoryOverrides();
const auto builtins = QQmlJSLogger::builtinCategories();
QCOMPARE(overrides.size(), builtins.size());
}
QTEST_MAIN(tst_qmltc)