aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp')
-rw-r--r--tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp312
1 files changed, 270 insertions, 42 deletions
diff --git a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
index 92025cfb11..76711a00e6 100644
--- a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
+++ b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QUrl>
@@ -36,14 +11,15 @@
#include <QtQml/qqmlprivate.h>
#include <QtQml/qqmlproperty.h>
#include <QDebug>
+#include <QtCore/qrandom.h>
#include <private/qquickstate_p.h>
-#include "../../shared/util.h"
+#include <QtQuickTestUtils/private/qmlutils_p.h>
class tst_qqmllistreference : public QQmlDataTest
{
Q_OBJECT
public:
- tst_qqmllistreference() {}
+ tst_qqmllistreference() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
private:
void modeData();
@@ -77,6 +53,14 @@ private slots:
void engineTypes();
void variantToList();
void listProperty();
+ void compositeListProperty();
+ void nullItems();
+ void jsArrayMethods();
+ void jsArrayMethodsWithParams_data();
+ void jsArrayMethodsWithParams();
+ void listIgnoresNull_data() { modeData(); }
+ void listIgnoresNull();
+ void consoleLogSyntheticList();
};
class TestType : public QObject
@@ -92,14 +76,20 @@ public:
SyntheticClearAndReplace,
SyntheticRemoveLast,
SyntheticRemoveLastAndReplace,
- AutomaticPointer
+ AutomaticPointer,
+ IgnoreNullValues,
};
static void append(QQmlListProperty<TestType> *p, TestType *v) {
reinterpret_cast<QList<TestType *> *>(p->data)->append(v);
}
+ static void appendNoNullValues(QQmlListProperty<TestType> *p, TestType *v) {
+ if (!v)
+ return;
+ reinterpret_cast<QList<TestType *> *>(p->data)->append(v);
+ }
static qsizetype count(QQmlListProperty<TestType> *p) {
- return reinterpret_cast<QList<TestType *> *>(p->data)->count();
+ return reinterpret_cast<QList<TestType *> *>(p->data)->size();
}
static TestType *at(QQmlListProperty<TestType> *p, qsizetype idx) {
return reinterpret_cast<QList<TestType *> *>(p->data)->at(idx);
@@ -140,6 +130,10 @@ public:
case AutomaticPointer:
property = QQmlListProperty<TestType>(this, &data);
break;
+ case IgnoreNullValues:
+ property = QQmlListProperty<TestType>(this, &data, appendNoNullValues, count, at, clear,
+ replace, removeLast);
+ break;
}
}
@@ -161,6 +155,7 @@ void tst_qqmllistreference::modeData()
QTest::addRow("SyntheticClearAndReplace") << TestType::SyntheticClearAndReplace;
QTest::addRow("SyntheticRemoveLast") << TestType::SyntheticRemoveLast;
QTest::addRow("SyntheticRemoveLastAndReplace") << TestType::SyntheticRemoveLastAndReplace;
+ QTest::addRow("IgnoreNullValues") << TestType::IgnoreNullValues;
}
void tst_qqmllistreference::initTestCase()
@@ -189,7 +184,7 @@ void tst_qqmllistreference::qmllistreference()
QVERIFY(fromVar.isValid());
QCOMPARE(fromVar.count(), 1);
fromVar.append(&tt);
- QCOMPARE(tt.data.count(), 2);
+ QCOMPARE(tt.data.size(), 2);
}
void tst_qqmllistreference::qmllistreference_invalid()
@@ -564,13 +559,13 @@ void tst_qqmllistreference::append()
{
QQmlListReference ref(tt, "data");
QVERIFY(ref.append(tt));
- QCOMPARE(tt->data.count(), 1);
+ QCOMPARE(tt->data.size(), 1);
QCOMPARE(tt->data.at(0), tt);
QVERIFY(!ref.append(&object));
- QCOMPARE(tt->data.count(), 1);
+ QCOMPARE(tt->data.size(), 1);
QCOMPARE(tt->data.at(0), tt);
QVERIFY(ref.append(nullptr));
- QCOMPARE(tt->data.count(), 2);
+ QCOMPARE(tt->data.size(), 2);
QCOMPARE(tt->data.at(0), tt);
QVERIFY(!tt->data.at(1));
delete tt;
@@ -641,7 +636,7 @@ void tst_qqmllistreference::clear()
{
QQmlListReference ref(tt, "data");
QVERIFY(ref.clear());
- QCOMPARE(tt->data.count(), 0);
+ QCOMPARE(tt->data.size(), 0);
delete tt;
QVERIFY(!ref.clear());
}
@@ -737,9 +732,9 @@ void tst_qqmllistreference::removeLast()
{
QQmlListReference ref(tt.get(), "data");
- QCOMPARE(tt->data.count(), 3);
+ QCOMPARE(tt->data.size(), 3);
QVERIFY(ref.removeLast());
- QCOMPARE(tt->data.count(), 2);
+ QCOMPARE(tt->data.size(), 2);
tt.reset();
QVERIFY(!ref.removeLast());
}
@@ -800,6 +795,7 @@ void tst_qqmllistreference::engineTypes()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("engineTypes.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QObject *o = component.create();
QVERIFY(o);
@@ -822,7 +818,7 @@ void tst_qqmllistreference::engineTypes()
const QMetaProperty prop = m->property(index);
const QVariant var = prop.read(o);
- QQmlListReference fromVar(var, &engine);
+ QQmlListReference fromVar(var);
QVERIFY(fromVar.isValid());
QCOMPARE(fromVar.count(), 2);
QCOMPARE(fromVar.listElementType(), ref.listElementType());
@@ -835,13 +831,11 @@ void tst_qqmllistreference::variantToList()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("variantToList.qml"));
- QObject *o = component.create();
+ QScopedPointer<QObject> o(component.create());
QVERIFY(o);
QCOMPARE(o->property("value").userType(), qMetaTypeId<QQmlListReference>());
QCOMPARE(o->property("test").toInt(), 1);
-
- delete o;
}
void tst_qqmllistreference::listProperty()
@@ -865,6 +859,240 @@ void tst_qqmllistreference::listProperty()
QCOMPARE( state2->name(), QStringLiteral("MyState2") );
}
+void tst_qqmllistreference::compositeListProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("compositeListProp.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QQmlComponent item(&engine, testFileUrl("AListItem.qml"));
+ QScopedPointer<QObject> i1(item.create());
+ QScopedPointer<QObject> i2(item.create());
+ QVERIFY(!i1.isNull());
+ QVERIFY(!i2.isNull());
+
+ // We know the element type now.
+ QQmlListReference list1(object.data(), "items");
+ QVERIFY(list1.listElementType() != nullptr);
+ QVERIFY(list1.append(i1.data()));
+ QVERIFY(list1.replace(0, i2.data()));
+}
+
+void tst_qqmllistreference::nullItems()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("nullItems.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QQmlListReference list(object.data(), "items");
+ QCOMPARE(list.count(), 3);
+ QCOMPARE(list.at(0), nullptr);
+ QCOMPARE(list.at(1), nullptr);
+ QVERIFY(list.at(2) != nullptr);
+}
+
+static void listsEqual(QObject *object, const char *method)
+{
+ const QByteArray listPropertyPropertyName = QByteArray("listProperty") + method;
+ const QByteArray jsArrayPropertyName = QByteArray("jsArray") + method;
+
+ const QQmlListReference listPropertyProperty(object, listPropertyPropertyName.constData());
+ const QQmlListReference jsArrayProperty(object, jsArrayPropertyName.constData());
+
+ const qsizetype listPropertyCount = listPropertyProperty.count();
+ QCOMPARE(listPropertyCount, jsArrayProperty.count());
+
+ for (qsizetype i = 0; i < listPropertyCount; ++i)
+ QCOMPARE(listPropertyProperty.at(i), jsArrayProperty.at(i));
+}
+
+void tst_qqmllistreference::jsArrayMethods()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("jsArrayMethods.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("listPropertyToString"), object->property("jsArrayToString"));
+ QCOMPARE(object->property("listPropertyToLocaleString"), object->property("jsArrayToLocaleString"));
+
+ QVERIFY(object->property("entriesMatch").toBool());
+ QVERIFY(object->property("keysMatch").toBool());
+ QVERIFY(object->property("valuesMatch").toBool());
+
+ listsEqual(object.data(), "Concat");
+ listsEqual(object.data(), "Pop");
+ listsEqual(object.data(), "Push");
+ listsEqual(object.data(), "Reverse");
+ listsEqual(object.data(), "Shift");
+ listsEqual(object.data(), "Unshift");
+ listsEqual(object.data(), "Filter");
+ listsEqual(object.data(), "Sort1");
+ listsEqual(object.data(), "Sort2");
+
+ QCOMPARE(object->property("listPropertyFind"), object->property("jsArrayFind"));
+ QCOMPARE(object->property("listPropertyFind").value<QObject *>()->objectName(), QStringLiteral("klaus"));
+
+ QCOMPARE(object->property("listPropertyFindIndex"), object->property("jsArrayFindIndex"));
+ QCOMPARE(object->property("listPropertyFindIndex").toInt(), 1);
+
+ QCOMPARE(object->property("listPropertyIncludes"), object->property("jsArrayIncludes"));
+ QVERIFY(object->property("listPropertyIncludes").toBool());
+
+ QCOMPARE(object->property("listPropertyJoin"), object->property("jsArrayJoin"));
+ QVERIFY(object->property("listPropertyJoin").toString().contains(QStringLiteral("klaus")));
+
+ QCOMPARE(object->property("listPropertyPopped"), object->property("jsArrayPopped"));
+ QVERIFY(object->property("listPropertyPopped").value<QObject *>()->objectName().isEmpty());
+
+ QCOMPARE(object->property("listPropertyPushed"), object->property("jsArrayPushed"));
+ QCOMPARE(object->property("listPropertyPushed").toInt(), 4);
+
+ QCOMPARE(object->property("listPropertyShifted"), object->property("jsArrayShifted"));
+ QCOMPARE(object->property("listPropertyShifted").value<QObject *>()->objectName(), QStringLiteral("klaus"));
+
+ QCOMPARE(object->property("listPropertyUnshifted"), object->property("jsArrayUnshifted"));
+ QCOMPARE(object->property("listPropertyUnshifted").toInt(), 4);
+
+ QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyIndexOf").toInt(), 1);
+
+ QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf").toInt(), 2);
+
+ QCOMPARE(object->property("listPropertyEvery"), object->property("jsArrayEvery"));
+ QVERIFY(object->property("listPropertyEvery").toBool());
+
+ QCOMPARE(object->property("listPropertySome"), object->property("jsArrayEvery"));
+ QVERIFY(object->property("listPropertySome").toBool());
+
+ QCOMPARE(object->property("listPropertyForEach"), object->property("jsArrayForEach"));
+ QCOMPARE(object->property("listPropertyForEach").toString(), QStringLiteral("-klaus-----"));
+
+ QCOMPARE(object->property("listPropertyMap").toStringList(), object->property("jsArrayMap").toStringList());
+ QCOMPARE(object->property("listPropertyReduce").toString(), object->property("jsArrayReduce").toString());
+
+ QCOMPARE(object->property("listPropertyOwnPropertyNames").toStringList(),
+ object->property("jsArrayOwnPropertyNames").toStringList());
+}
+
+void tst_qqmllistreference::jsArrayMethodsWithParams_data()
+{
+ QTest::addColumn<double>("i");
+ QTest::addColumn<double>("j");
+ QTest::addColumn<double>("k");
+
+ const double indices[] = {
+ double(std::numeric_limits<qsizetype>::min()),
+ double(std::numeric_limits<qsizetype>::min()) + 1,
+ double(std::numeric_limits<uint>::min()) - 1,
+ double(std::numeric_limits<uint>::min()),
+ double(std::numeric_limits<uint>::min()) + 1,
+ double(std::numeric_limits<int>::min()),
+ -10, -3, -2, -1, 0, 1, 2, 3, 10,
+ double(std::numeric_limits<int>::max()),
+ double(std::numeric_limits<uint>::max()) - 1,
+ double(std::numeric_limits<uint>::max()),
+ double(std::numeric_limits<uint>::max()) + 1,
+ double(std::numeric_limits<qsizetype>::max() - 1),
+ double(std::numeric_limits<qsizetype>::max()),
+ };
+
+ // We cannot test the full cross product. So, take a random sample instead.
+ const qsizetype numIndices = sizeof(indices) / sizeof(double);
+ qsizetype seed = QRandomGenerator::global()->generate();
+ const int numSamples = 4;
+ for (int i = 0; i < numSamples; ++i) {
+ seed = qHash(i, seed);
+ const double vi = indices[qAbs(seed) % numIndices];
+ for (int j = 0; j < numSamples; ++j) {
+ seed = qHash(j, seed);
+ const double vj = indices[qAbs(seed) % numIndices];
+ for (int k = 0; k < numSamples; ++k) {
+ seed = qHash(k, seed);
+ const double vk = indices[qAbs(seed) % numIndices];
+ const QString tag = QLatin1String("%1/%2/%3")
+ .arg(QString::number(vi), QString::number(vj), QString::number(vk));
+ QTest::newRow(qPrintable(tag)) << vi << vj << vk;
+
+ // output all the tags so that we can find out what combination caused a test to hang.
+ qDebug().noquote() << "scheduling" << tag;
+ }
+ }
+ }
+}
+
+void tst_qqmllistreference::jsArrayMethodsWithParams()
+{
+ QFETCH(double, i);
+ QFETCH(double, j);
+ QFETCH(double, k);
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("jsArrayMethodsWithParams.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.createWithInitialProperties({
+ {QStringLiteral("i"), i},
+ {QStringLiteral("j"), j},
+ {QStringLiteral("k"), k}
+ }));
+ QVERIFY(!object.isNull());
+
+ listsEqual(object.data(), "CopyWithin");
+ listsEqual(object.data(), "Fill");
+ listsEqual(object.data(), "Slice");
+ listsEqual(object.data(), "Splice");
+ listsEqual(object.data(), "Spliced");
+
+ QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
+}
+
+/*!
+ Some of our list implementations ignore attempts to append a null object.
+ This should result in warnings or type errors, and not crash our wrapper
+ code.
+*/
+void tst_qqmllistreference::listIgnoresNull()
+{
+ QFETCH(const TestType::Mode, mode);
+ static TestType::Mode globalMode;
+ globalMode = mode;
+ struct TestItem : public TestType
+ {
+ TestItem() : TestType(globalMode) {}
+ };
+
+ const auto id = qmlRegisterType<TestItem>("Test", 1, 0, "TestItem");
+ const auto unregister = qScopeGuard([id]{
+ QQmlPrivate::qmlunregister(QQmlPrivate::TypeRegistration, id);
+ });
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("listIgnoresNull.qml"));
+
+ // For lists that don't append null values, creating the component shouldn't crash
+ // in the onCompleted handler, but generate type errors and warnings.
+ if (mode == TestType::IgnoreNullValues) {
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* QML TestItem: List didn't append all objects$"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* TypeError: List doesn't append null objects$"));
+ }
+ QScopedPointer<QObject> object( component.create() );
+ QVERIFY(object != nullptr);
+}
+
+void tst_qqmllistreference::consoleLogSyntheticList()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("consoleLogSyntheticList.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QTest::ignoreMessage(
+ QtDebugMsg, QRegularExpression("\\[QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)\\]"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+}
QTEST_MAIN(tst_qqmllistreference)