diff options
Diffstat (limited to 'tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp')
-rw-r--r-- | tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 255 |
1 files changed, 191 insertions, 64 deletions
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index e522f280a3..d738a7f68d 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -1,13 +1,15 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickanimation_p.h> #include <QtQuick/private/qquicklistview_p.h> +#include <QtQuick/private/qquickrepeater_p.h> #include <QtQml/private/qqmlengine_p.h> #include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qqmlexpression_p.h> +#include <QtQml/private/qqmlsignalnames_p.h> #include <QQmlComponent> #include <QtCore/qtimer.h> @@ -119,6 +121,7 @@ private slots: void objectOwnershipFlip(); void enumsInListElement(); void protectQObjectFromGC(); + void nestedLists(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -248,8 +251,8 @@ void tst_qqmllistmodel::static_types() QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); if (error.isEmpty()) { QVariant actual = obj->property("test"); @@ -257,8 +260,6 @@ void tst_qqmllistmodel::static_types() QCOMPARE(actual, value); QCOMPARE(actual.toString(), value.toString()); } - - delete obj; } void tst_qqmllistmodel::static_i18n_data() @@ -319,15 +320,13 @@ void tst_qqmllistmodel::static_i18n() QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); QVariant actual = obj->property("test"); QCOMPARE(actual, value); QCOMPARE(actual.toString(), value.toString()); - - delete obj; } void tst_qqmllistmodel::dynamic_i18n_data() @@ -368,16 +367,15 @@ void tst_qqmllistmodel::dynamic_i18n() QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); QVariant actual = obj->property("test"); QCOMPARE(actual, value); QCOMPARE(actual.toString(), value.toString()); - - delete obj; } + void tst_qqmllistmodel::static_nestedElements() { QFETCH(int, elementCount); @@ -406,14 +404,12 @@ void tst_qqmllistmodel::static_nestedElements() QQmlComponent component(&engine); component.setData(componentStr.toUtf8(), QUrl::fromLocalFile("")); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); QVariant count = obj->property("count"); QCOMPARE(count.typeId(), QMetaType::Int); QCOMPARE(count.toInt(), elementCount); - - delete obj; } void tst_qqmllistmodel::static_nestedElements_data() @@ -606,8 +602,8 @@ void tst_qqmllistmodel::enumerate() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("enumerate.qml")); QVERIFY(!component.isError()); - QQuickItem *item = qobject_cast<QQuickItem*>(component.create()); - QVERIFY(item != nullptr); + std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component.create()) }; + QVERIFY(item); QLatin1String expectedStrings[] = { QLatin1String("val1=1Y"), @@ -638,8 +634,6 @@ void tst_qqmllistmodel::enumerate() } QCOMPARE(matchCount, expectedStringCount); - - delete item; } void tst_qqmllistmodel::error_data() @@ -779,9 +773,9 @@ void tst_qqmllistmodel::get() component.setData( "import QtQuick 2.0\n" "ListModel {}\n", QUrl()); - QQmlListModel *model = qobject_cast<QQmlListModel*>(component.create()); + std::unique_ptr<QQmlListModel> model { qobject_cast<QQmlListModel*>(component.create()) }; model->setDynamicRoles(dynamicRoles); - engine.rootContext()->setContextProperty("model", model); + engine.rootContext()->setContextProperty("model", model.get()); RUNEXPR("model.append({roleA: 100})"); RUNEXPR("model.append({roleA: 200, roleB: 400})"); @@ -789,12 +783,12 @@ void tst_qqmllistmodel::get() RUNEXPR("model.append({roleC: {} })"); RUNEXPR("model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); - QSignalSpy spy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QQmlExpression expr(engine.rootContext(), model, expression); + QSignalSpy spy(model.get(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); + QQmlExpression expr(engine.rootContext(), model.get(), expression); expr.evaluate(); QVERIFY(!expr.hasError()); - int role = roleFromName(model, roleName); + int role = roleFromName(model.get(), roleName); QVERIFY(role >= 0); if (roleValue.typeId() == QMetaType::QVariantList) { @@ -810,8 +804,6 @@ void tst_qqmllistmodel::get() QCOMPARE(spyResult.at(0).value<QModelIndex>(), model->index(index, 0, QModelIndex())); QCOMPARE(spyResult.at(1).value<QModelIndex>(), model->index(index, 0, QModelIndex())); // only 1 item is modified at a time QCOMPARE(spyResult.at(2).value<QVector<int> >(), (QVector<int>() << role)); - - delete model; } void tst_qqmllistmodel::get_data() @@ -857,11 +849,11 @@ void tst_qqmllistmodel::get_nested() component.setData( "import QtQuick 2.0\n" "ListModel {}", QUrl()); - QQmlListModel *model = qobject_cast<QQmlListModel*>(component.create()); + std::unique_ptr<QQmlListModel> model { qobject_cast<QQmlListModel*>(component.create()) }; model->setDynamicRoles(dynamicRoles); QVERIFY(component.errorString().isEmpty()); QQmlListModel *childModel; - engine.rootContext()->setContextProperty("model", model); + engine.rootContext()->setContextProperty("model", model.get()); RUNEXPR("model.append({ listRoleA: [\n" "{ roleA: 100 },\n" @@ -908,16 +900,16 @@ void tst_qqmllistmodel::get_nested() for (int i=0; i<testData.size(); i++) { int outerListIndex = testData[i].first; QString outerListRoleName = testData[i].second; - int outerListRole = roleFromName(model, outerListRoleName); + int outerListRole = roleFromName(model.get(), outerListRoleName); QVERIFY(outerListRole >= 0); childModel = qobject_cast<QQmlListModel*>(model->data(outerListIndex, outerListRole).value<QObject*>()); QVERIFY(childModel); QString extendedExpression = QString("get(%1).%2.%3").arg(outerListIndex).arg(outerListRoleName).arg(expression); - QQmlExpression expr(engine.rootContext(), model, extendedExpression); + QQmlExpression expr(engine.rootContext(), model.get(), extendedExpression); - QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); expr.evaluate(); QVERIFY(!expr.hasError()); @@ -935,8 +927,6 @@ void tst_qqmllistmodel::get_nested() QCOMPARE(spyResult.at(1).value<QModelIndex>(), childModel->index(index, 0, QModelIndex())); // only 1 item is modified at a time QCOMPARE(spyResult.at(2).value<QVector<int> >(), (QVector<int>() << role)); } - - delete model; } void tst_qqmllistmodel::get_nested_data() @@ -949,16 +939,14 @@ void tst_qqmllistmodel::crash_model_with_multiple_roles() { QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); - QObject *rootItem = component.create(); + std::unique_ptr<QObject> rootItem { component.create() }; QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != nullptr); + QVERIFY(rootItem); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); QVERIFY(model != nullptr); // used to cause a crash model->setProperty(0, "black", true); - - delete rootItem; } void tst_qqmllistmodel::crash_model_with_unknown_roles() @@ -983,22 +971,19 @@ void tst_qqmllistmodel::crash_model_with_dynamic_roles() // setting a dynamic role to a QObject value, then triggering dtor QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); - QObject *rootItem = component.create(); + std::unique_ptr<QObject> rootItem { component.create() }; qWarning() << component.errorString(); QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); + QVERIFY(rootItem.get() != 0); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); QVERIFY(model != 0); QMetaObject::invokeMethod(model, "appendNewElement"); - QObject *testObj = new QObject; - model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); - delete testObj; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(std::make_unique<QObject>().get())); - // delete the root item, which will cause the model dtor to run - // previously, this would crash as it attempted to delete testObj. - delete rootItem; + // Let root item go out of scope to let the model dtor run. + // Previously, this would crash as it attempted to delete the already-deleted temporary QObject. } { @@ -1006,22 +991,18 @@ void tst_qqmllistmodel::crash_model_with_dynamic_roles() // DynamicRoleModelNode::updateValues() to trigger unsafe qobject_cast QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); - QObject *rootItem = component.create(); + std::unique_ptr<QObject> rootItem { component.create() }; qWarning() << component.errorString(); QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); + QVERIFY(rootItem.get() != 0); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); QVERIFY(model != 0); QMetaObject::invokeMethod(model, "appendNewElement"); - QObject *testObj = new QObject; - model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); - delete testObj; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(std::make_unique<QObject>().get())); QMetaObject::invokeMethod(model, "setElementAgain"); - - delete rootItem; } { @@ -1098,21 +1079,21 @@ void tst_qqmllistmodel::property_changes() expr.evaluate(); QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString())); - QString signalHandler = "on" + QString(roleName[0].toUpper()) + roleName.mid(1, roleName.size()) + "Changed:"; + QString signalHandler = QQmlSignalNames::propertyNameToChangedHandlerName(roleName); QString qml = "import QtQuick 2.0\n" "Connections {\n" "property bool gotSignal: false\n" "target: model.get(" + QString::number(listIndex) + ")\n" - + signalHandler + " gotSignal = true\n" + + signalHandler + ": gotSignal = true\n" "}\n"; QQmlComponent component(&engine); component.setData(qml.toUtf8(), QUrl::fromLocalFile("")); engine.rootContext()->setContextProperty("model", &model); - QObject *connectionsObject = component.create(); + std::unique_ptr<QObject> connectionsObject { component.create() }; QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); - QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>))); expr.setExpression(script_change); expr.evaluate(); @@ -1132,8 +1113,6 @@ void tst_qqmllistmodel::property_changes() expr.setExpression(testExpression); QCOMPARE(expr.evaluate().toBool(), true); - - delete connectionsObject; } void tst_qqmllistmodel::property_changes_data() @@ -1354,10 +1333,8 @@ void tst_qqmllistmodel::empty_element_warning() component.setData(qml.toUtf8(), QUrl::fromLocalFile(QString("dummy.qml"))); QVERIFY(!component.isError()); - QObject *obj = component.create(); - QVERIFY(obj != nullptr); - - delete obj; + std::unique_ptr<QObject> obj { component.create() }; + QVERIFY(obj); } void tst_qqmllistmodel::datetime_data() @@ -1549,6 +1526,8 @@ void tst_qqmllistmodel::modify_through_delegate() " ListElement { name: \"Doe\"; age: 33 }\n" " }\n" " ListView {\n" + " height: 100\n" \ + " width: 100\n" \ " model: testModel\n" " delegate: Item {\n" " Component.onCompleted: model.age = 18;\n" @@ -1929,6 +1908,154 @@ void tst_qqmllistmodel::protectQObjectFromGC() } } +static QVariantList createLast7Days() +{ + QVariantList last7DaysList; + for (int i = 0; i < 7; i++) { + QVariantMap map; + map.insert("_day", i); + last7DaysList.append(map); + } + return last7DaysList; +} + +static QVariantList createWeekChartModels() +{ + QVariantList list; + for (int i = 0; i < 4; i++) { + QVariantMap map; + map.insert("_week", createLast7Days()); + list.append(map); + } + return list; +} + +static QVariantList createVariantModel() +{ + QVariantMap element1; + element1.insert("_headline", "Element 1"); + element1.insert("_weeks", createWeekChartModels()); + + QVariantMap element2; + element2.insert("_headline", "Element 2"); + element2.insert("_weeks", createWeekChartModels()); + + QVariantMap element3; + element3.insert("_headline", "Element 3"); + element3.insert("_weeks", createWeekChartModels()); + + QVariantList list; + list.append(element1); + list.append(element2); + list.append(element3); + + return list; +} + +class Day : public QObject +{ + Q_OBJECT + Q_PROPERTY(int _day READ _day CONSTANT) +public: + Day(int day, QObject *parent = nullptr) : QObject(parent), day(day) {} + int _day() const { return day; } +private: + int day = 0; +}; + +class Week : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<Day> _week READ _week) +public: + Week(QObject *parent = nullptr) : QObject(parent) + { + for (int i = 0; i < 7; ++i) + week.append(new Day(i, this)); + } + + QQmlListProperty<Day> _week() { return QQmlListProperty<Day>(this, &week); } + +private: + QList<Day *> week; +}; + +class Month : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<Week> _weeks READ _weeks) + Q_PROPERTY(QString _headline READ _headline CONSTANT) +public: + + Month(int i, QObject *parent = nullptr) + : QObject(parent) + , headline(QLatin1String("Element ") + QString::number(i)) + { + for (int i = 0; i < 4; ++i) + weeks.append(new Week(this)); + } + + QQmlListProperty<Week> _weeks() { return QQmlListProperty<Week>(this, &weeks); } + QString _headline() const { return headline; } + +private: + QList<Week *> weeks; + QString headline; +}; + +static void verifyLists(const QVariantList &list, QQuickRepeater *topLevel) +{ + QVERIFY(topLevel); + QCOMPARE(topLevel->count(), 3); + + for (int month = 0; month < 3; ++month) { + const QVariantMap monthData = list[month].toMap(); + const QQuickItem *monthItem = topLevel->itemAt(month); + QCOMPARE(monthItem->objectName(), monthData["_headline"].toString()); + const QQuickRepeater *monthRepeater = monthItem->findChild<QQuickRepeater *>("month"); + QVERIFY(monthRepeater); + QCOMPARE(monthRepeater->count(), 4); + const QVariantList weekList = monthData["_weeks"].toList(); + for (int week = 0; week < 4; ++week) { + const QVariantList weekData = weekList[week].toMap()["_week"].toList(); + const QQuickItem *weekItem = monthRepeater->itemAt(week); + QCOMPARE(weekItem->objectName(), QString::number(week)); + const QQuickRepeater *weekRepeater = weekItem->findChild<QQuickRepeater *>("week"); + QVERIFY(weekRepeater); + QCOMPARE(weekRepeater->count(), 7); + for (int day = 0; day < 7; ++day) { + const QVariantMap dayData = weekData[day].toMap(); + const QQuickItem *dayItem = weekRepeater->itemAt(day); + QCOMPARE(dayItem->objectName(), dayData["_day"]); + } + } + } +} + +void tst_qqmllistmodel::nestedLists() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("nestedLists.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QQuickRepeater *topLevel = o->findChild<QQuickRepeater *>("topLevel"); + + const QVariantList list = createVariantModel(); + QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(list))); + verifyLists(list, topLevel); + + const QObjectList objects { + new Month(1, o.data()), + new Month(2, o.data()), + new Month(3, o.data()) + }; + + QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(objects))); + verifyLists(list, topLevel); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" |