aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-07-12 16:08:17 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-07-19 10:52:31 +0200
commitef057772c41e84e9813f0ed113fc55224013404c (patch)
tree80d1a7247825bcfccb577ce2ab6c0f248d435439 /tests/auto
parentb335d8311f7c0be9261305d5b41bbcbf94cd3d27 (diff)
Fix array-like methods on V4 sequences
We need to properly convert value type lists on assignment and we need to add the "length" property to the own properties. Furthermore, the V4 sequence methods were confused about integer type ranges. We teach them about qsizetype, properly range check everything, and drop the artificial limitation to INT_MAX. Pick-to: 6.4 Task-number: QTBUG-82443 Change-Id: Ie5af1130c9e78e412c171e6fa26a28a6a7a5d498 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tests/auto')
-rw-r--r--tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp14
-rw-r--r--tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml198
-rw-r--r--tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml47
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp163
5 files changed, 424 insertions, 10 deletions
diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml
index a3f306f717..b1d422b6e9 100644
--- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml
+++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml
@@ -30,15 +30,19 @@ Item {
var tooBigIndex = msco.tooBigIndex;
var negativeIndex = msco.negativeIndex;
- // shouldn't be able to set the length > maxIndex.
- msco.intListProperty.length = tooBigIndex;
+ // We cannot test this anymore since INT_MAX + <a bit> is actually supported on 64bit.
+ // Trying to do this just wastes a lot of memory and takes forever.
+ // msco.intListProperty.length = tooBigIndex;
+
if (msco.intListProperty.length != expectedLength)
success = false;
if (!verifyExpected(msco.intListProperty, 4))
success = false;
- // shouldn't be able to set any index > maxIndex.
- msco.intListProperty[tooBigIndex] = 12;
+ // We cannot test this anymore since INT_MAX + <a bit> is actually supported on 64bit.
+ // Trying to do this just wastes a lot of memory and takes forever.
+ // msco.intListProperty[tooBigIndex] = 12;
+
if (msco.intListProperty.length != expectedLength)
success = false;
if (!verifyExpected(msco.intListProperty, 4))
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 9aacb05043..033de56695 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -5948,17 +5948,19 @@ void tst_qqmlecmascript::sequenceConversionIndexes()
{
// ensure that we gracefully fail if unsupported index values are specified.
// Qt container classes only support non-negative, signed integer index values.
+
+ // Since Qt6, on 64bit the maximum length is beyond what we can encode in a 32bit integer.
+ // Therefore we cannot test the overflow anymore.
+
QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
QQmlEngine engine;
QQmlComponent component(&engine, qmlFile);
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
- QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
- QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
- QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
- QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
- QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
- QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
+
+ const QString w = qmlFile.toString() + QLatin1String(":59: Index out of range during length set");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w));
+
QMetaObject::invokeMethod(object.data(), "indexedAccess");
QVERIFY(object->property("success").toBool());
QMetaObject::invokeMethod(object.data(), "indexOf");
diff --git a/tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml b/tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml
new file mode 100644
index 0000000000..9553ed6087
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml
@@ -0,0 +1,198 @@
+import QtQml
+
+QtObject {
+ property rect self: Qt.rect(9, 9, 9, 9)
+
+ property rect r1: Qt.rect(1, 2, 3, 4)
+ property rect r2: Qt.rect(5, 6, 7, 8)
+ property rect r3: Qt.rect(9, 10, 11, 12)
+
+ function jsArray() { return [r1, r2, r3] }
+ property list<rect> v4Sequence: [r1, r2, r3]
+
+ property string jsArrayToString: jsArray().toString()
+ property string v4SequenceToString: v4Sequence.toString()
+
+ property string jsArrayToLocaleString: jsArray().toLocaleString()
+ property string v4SequenceToLocaleString: v4Sequence.toLocaleString()
+
+ property list<rect> v4SequenceConcat: v4Sequence.concat(v4Sequence)
+ property list<rect> jsArrayConcat: jsArray().concat(jsArray())
+
+ property rect v4SequenceFind: v4Sequence.find(element => element.x === 1)
+ property rect jsArrayFind: jsArray().find(element => element.x === 1)
+
+ property int v4SequenceFindIndex: v4Sequence.findIndex(element => element === r2)
+ property int jsArrayFindIndex: jsArray().findIndex(element => element === r2)
+
+ property bool v4SequenceIncludes: v4Sequence.includes(r3)
+ property bool jsArrayIncludes: jsArray().includes(r3)
+
+ property string v4SequenceJoin: v4Sequence.join()
+ property string jsArrayJoin: jsArray().join()
+
+ property bool entriesMatch: {
+ var iterator = v4Sequence.entries();
+ for (var [index, element] of jsArray().entries()) {
+ var v = iterator.next().value;
+ if (index !== v[0] || element !== v[1]) {
+ console.log(index, v[0], element, v[1]);
+ return false;
+ }
+ }
+
+ var iterator = jsArray().entries();
+ for (var [index, element] of v4Sequence.entries()) {
+ var v = iterator.next().value;
+ if (index !== v[0] || element !== v[1]) {
+ console.log(index, v[0], element, v[1]);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ property bool keysMatch: {
+ var iterator = v4Sequence.keys();
+ for (var index of jsArray().keys()) {
+ var v = iterator.next().value;
+ if (index !== v) {
+ console.log(index, v);
+ return false;
+ }
+ }
+
+ var iterator = jsArray().keys();
+ for (var index of v4Sequence.keys()) {
+ var v = iterator.next().value;
+ if (index !== v) {
+ console.log(index, v);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ property bool valuesMatch: {
+ var iterator = v4Sequence.values();
+ for (var obj of jsArray().values()) {
+ var v = iterator.next().value;
+ if (obj !== v) {
+ console.log(obj, v);
+ return false;
+ }
+ }
+
+ var iterator = jsArray().values();
+ for (var obj of v4Sequence.values()) {
+ var v = iterator.next().value;
+ if (obj !== v) {
+ console.log(obj, v);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ property list<rect> v4SequencePop: v4Sequence
+ property rect v4SequencePopped
+
+ property list<rect> jsArrayPop
+ property rect jsArrayPopped
+
+ property list<rect> v4SequencePush: v4Sequence
+ property int v4SequencePushed
+
+ property list<rect> jsArrayPush
+ property int jsArrayPushed
+
+ property list<rect> v4SequenceReverse: v4Sequence
+ property list<rect> jsArrayReverse: jsArray().reverse()
+
+ property list<rect> v4SequenceShift: v4Sequence
+ property rect v4SequenceShifted
+
+ property list<rect> jsArrayShift
+ property rect jsArrayShifted
+
+ property list<rect> v4SequenceSplice: v4Sequence
+ property list<rect> v4SequenceSpliced
+
+ property list<rect> jsArraySplice
+ property list<rect> jsArraySpliced
+
+ property list<rect> v4SequenceUnshift: v4Sequence
+ property int v4SequenceUnshifted
+
+ property list<rect> jsArrayUnshift
+ property int jsArrayUnshifted
+
+ property int v4SequenceIndexOf: v4Sequence.indexOf(r2)
+ property int jsArrayIndexOf: jsArray().indexOf(r2)
+
+ property int v4SequenceLastIndexOf: v4Sequence.lastIndexOf(r3)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(r3)
+
+ property bool v4SequenceEvery: v4Sequence.every((element) => element != null)
+ property bool jsArrayEvery: jsArray().every((element) => element != null)
+
+ property bool v4SequenceSome: v4Sequence.some((element) => element.x === 1)
+ property bool jsArraySome: jsArray().some((element) => element.x === 1)
+
+ property string v4SequenceForEach
+ property string jsArrayForEach
+
+ property list<int> v4SequenceMap: v4Sequence.map(((element) => element.x))
+ property list<int> jsArrayMap: jsArray().map(((element) => element.x))
+
+ property list<rect> v4SequenceFilter: v4Sequence.filter((element) => element.x != 1)
+ property list<rect> jsArrayFilter: jsArray().filter((element) => element.x != 1)
+
+ property string v4SequenceReduce: v4Sequence.reduce((element, v) => v + '-' + element.x + 'v', "")
+ property string jsArrayReduce: jsArray().reduce((element, v) => v + '-' + element.x + 'v', "")
+
+ property string v4SequenceReduceRight: v4Sequence.reduceRight((element, v) => v + '-' + element.x + 'v', "")
+ property string jsArrayReduceRight: jsArray().reduceRight((element, v) => v + '-' + element.x + 'v', "")
+
+ property list<string> jsArrayOwnPropertyNames: Object.getOwnPropertyNames(jsArray())
+ property list<string> v4SequenceOwnPropertyNames: Object.getOwnPropertyNames(v4Sequence)
+
+ property list<rect> v4SequenceSort1: v4Sequence
+ property list<rect> jsArraySort1: jsArray().sort()
+
+ property list<rect> v4SequenceSort2: v4Sequence
+ property list<rect> jsArraySort2: jsArray().sort((a, b) => (a.x - b.x))
+
+ Component.onCompleted: {
+ v4SequenceReverse.reverse();
+
+ v4SequencePopped = v4SequencePop.pop();
+ var a = jsArray();
+ jsArrayPopped = a.pop();
+ jsArrayPop = a;
+
+ v4SequencePushed = v4SequencePush.push(self);
+ a = jsArray();
+ jsArrayPushed = a.push(self);
+ jsArrayPush = a;
+
+ v4SequenceShifted = v4SequenceShift.shift();
+ a = jsArray();
+ jsArrayShifted = a.shift();
+ jsArrayShift = a;
+
+ v4SequenceUnshifted = v4SequenceUnshift.unshift(self);
+ a = jsArray();
+ jsArrayUnshifted = a.unshift(self);
+ jsArrayUnshift = a;
+
+ v4Sequence.forEach((element) => { v4SequenceForEach += "-" + element.x + "-" });
+ jsArray().forEach((element) => { jsArrayForEach += "-" + element.x + "-" });
+
+ v4SequenceSort1.sort();
+ v4SequenceSort2.sort((a, b) => (a.x - b.x))
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml b/tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml
new file mode 100644
index 0000000000..001f092c0a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml
@@ -0,0 +1,47 @@
+import QtQml
+
+QtObject {
+ property rect self: Qt.rect(9, 9, 9, 9)
+
+ required property real i
+ required property real j
+ required property real k
+
+ property rect r1: Qt.rect(1, 2, 3, 4)
+ property rect r2: Qt.rect(5, 6, 7, 8)
+ property rect r3: Qt.rect(9, 10, 11, 12)
+
+ function jsArray() { return [r1, r2, r3] }
+ property list<rect> v4Sequence: [r1, r2, r3]
+
+ property list<rect> v4SequenceCopyWithin: v4Sequence
+ property list<rect> jsArrayCopyWithin: jsArray().copyWithin(i, j, k)
+
+ property list<rect> v4SequenceFill: v4Sequence
+ property list<rect> jsArrayFill: jsArray().fill(self, i, Math.min(j, 1024))
+
+ property list<rect> v4SequenceSlice: v4Sequence.slice(i, j)
+ property list<rect> jsArraySlice: jsArray().slice(i, j)
+
+ property list<rect> v4SequenceSplice: v4Sequence
+ property list<rect> v4SequenceSpliced
+
+ property list<rect> jsArraySplice
+ property list<rect> jsArraySpliced
+
+ property int v4SequenceIndexOf: v4Sequence.indexOf(r2, i)
+ property int jsArrayIndexOf: jsArray().indexOf(r2, i)
+
+ property int v4SequenceLastIndexOf: v4Sequence.lastIndexOf(r3, i)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(r3, i)
+
+ Component.onCompleted: {
+ v4SequenceCopyWithin.copyWithin(i, j, k);
+ v4SequenceFill.fill(self, i, Math.min(j, 1024));
+
+ v4SequenceSpliced = v4SequenceSplice.splice(i, j, self, self, self);
+ var a = jsArray();
+ jsArraySpliced = a.splice(i, j, self, self, self);
+ jsArraySplice = a;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index ebc677c74f..8bed8a9880 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -11,6 +11,7 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qscopeguard.h>
+#include <QtCore/qrandom.h>
#include <QtGui/qevent.h>
#include <QSignalSpy>
#include <QFont>
@@ -388,6 +389,9 @@ private slots:
void leakingAttributesQmlForeign();
void attachedOwnProperties();
void bindableOnly();
+ void v4SequenceMethods();
+ void v4SequenceMethodsWithParams_data();
+ void v4SequenceMethodsWithParams();
private:
QQmlEngine engine;
@@ -7410,6 +7414,165 @@ void tst_qqmllanguage::bindableOnly()
QCOMPARE(o->property("a").toInt(), 5);
}
+static void listsEqual(QObject *object, const char *method)
+{
+ const QByteArray v4SequencePropertyName = QByteArray("v4Sequence") + method;
+ const QByteArray jsArrayPropertyName = QByteArray("jsArray") + method;
+
+ const QList<QRectF> v4SequenceProperty
+ = object->property(v4SequencePropertyName.constData()).value<QList<QRectF>>();
+ const QList<QRectF> jsArrayProperty
+ = object->property(jsArrayPropertyName.constData()).value<QList<QRectF>>();
+
+ const qsizetype v4SequenceCount = v4SequenceProperty.count();
+ QCOMPARE(v4SequenceCount, jsArrayProperty.count());
+
+ for (qsizetype i = 0; i < v4SequenceCount; ++i)
+ QCOMPARE(v4SequenceProperty.at(i), jsArrayProperty.at(i));
+}
+
+void tst_qqmllanguage::v4SequenceMethods()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("v4SequenceMethods.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("v4SequenceToString"), object->property("jsArrayToString"));
+ QCOMPARE(object->property("v4SequenceToLocaleString"), 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("v4SequenceFind"), object->property("jsArrayFind"));
+ QCOMPARE(object->property("v4SequenceFind").value<QRectF>().x(), 1.0);
+
+ QCOMPARE(object->property("v4SequenceFindIndex"), object->property("jsArrayFindIndex"));
+ QCOMPARE(object->property("v4SequenceFindIndex").toInt(), 1);
+
+ QCOMPARE(object->property("v4SequenceIncludes"), object->property("jsArrayIncludes"));
+ QVERIFY(object->property("v4SequenceIncludes").toBool());
+
+ QCOMPARE(object->property("v4SequenceJoin"), object->property("jsArrayJoin"));
+ QVERIFY(object->property("v4SequenceJoin").toString().contains(QStringLiteral("1")));
+
+ QCOMPARE(object->property("v4SequencePopped"), object->property("jsArrayPopped"));
+ QVERIFY(object->property("v4SequencePopped").value<QRectF>().x() != 1.0);
+
+ QCOMPARE(object->property("v4SequencePushed"), object->property("jsArrayPushed"));
+ QCOMPARE(object->property("v4SequencePushed").toInt(), 4);
+
+ QCOMPARE(object->property("v4SequenceShifted"), object->property("jsArrayShifted"));
+ QCOMPARE(object->property("v4SequenceShifted").value<QRectF>().x(), 1.0);
+
+ QCOMPARE(object->property("v4SequenceUnshifted"), object->property("jsArrayUnshifted"));
+ QCOMPARE(object->property("v4SequenceUnshifted").toInt(), 4);
+
+ QCOMPARE(object->property("v4SequenceIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("v4SequenceIndexOf").toInt(), 1);
+
+ QCOMPARE(object->property("v4SequenceLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("v4SequenceLastIndexOf").toInt(), 2);
+
+ QCOMPARE(object->property("v4SequenceEvery"), object->property("jsArrayEvery"));
+ QVERIFY(object->property("v4SequenceEvery").toBool());
+
+ QCOMPARE(object->property("v4SequenceSome"), object->property("jsArrayEvery"));
+ QVERIFY(object->property("v4SequenceSome").toBool());
+
+ QCOMPARE(object->property("v4SequenceForEach"), object->property("jsArrayForEach"));
+ QCOMPARE(object->property("v4SequenceForEach").toString(), QStringLiteral("-1--5--9-"));
+
+ QCOMPARE(object->property("v4SequenceMap").toStringList(), object->property("jsArrayMap").toStringList());
+ QCOMPARE(object->property("v4SequenceReduce").toString(), object->property("jsArrayReduce").toString());
+
+ QCOMPARE(object->property("v4SequenceOwnPropertyNames").toStringList(),
+ object->property("jsArrayOwnPropertyNames").toStringList());
+}
+
+void tst_qqmllanguage::v4SequenceMethodsWithParams_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_qqmllanguage::v4SequenceMethodsWithParams()
+{
+ QFETCH(double, i);
+ QFETCH(double, j);
+ QFETCH(double, k);
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("v4SequenceMethodsWithParams.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("v4SequenceIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("v4SequenceLastIndexOf"), object->property("jsArrayLastIndexOf"));
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"