QCborValueRef: fix using operator[] to convert an empty array to map
The flag IsContainer was not set, causing the QCborContainerPrivate to become confused. This commit also expands and subsumes the existing test for QCborValue (non-Ref). Change-Id: I5e52dc5b093c43a3b678fffd16b6a17c6f4a0676 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
3c7b507d1c
commit
9b8f064cd3
@ -2876,6 +2876,7 @@ QCborValueRef QCborValueRef::operator[](qint64 key)
|
|||||||
auto &e = d->elements[i];
|
auto &e = d->elements[i];
|
||||||
if (e.type == QCborValue::Array && key >= 0 && key < 0x10000) {
|
if (e.type == QCborValue::Array && key >= 0 && key < 0x10000) {
|
||||||
e.container = maybeGrow(e.container, key);
|
e.container = maybeGrow(e.container, key);
|
||||||
|
e.flags |= QtCbor::Element::IsContainer;
|
||||||
return { e.container, qsizetype(key) };
|
return { e.container, qsizetype(key) };
|
||||||
}
|
}
|
||||||
qsizetype size = 0;
|
qsizetype size = 0;
|
||||||
|
@ -88,6 +88,12 @@ private slots:
|
|||||||
void mapEmptyDetach();
|
void mapEmptyDetach();
|
||||||
void mapNonEmptyDetach();
|
void mapNonEmptyDetach();
|
||||||
void mapSimpleInitializerList();
|
void mapSimpleInitializerList();
|
||||||
|
void mapFromArrayLargeIntKey_data() { basics_data(); }
|
||||||
|
void mapFromArrayLargeIntKey();
|
||||||
|
void mapFromArrayNegativeIntKey_data() { basics_data(); }
|
||||||
|
void mapFromArrayNegativeIntKey();
|
||||||
|
void mapFromArrayStringKey_data() { basics_data(); }
|
||||||
|
void mapFromArrayStringKey();
|
||||||
void mapMutation();
|
void mapMutation();
|
||||||
void mapMutateWithCopies();
|
void mapMutateWithCopies();
|
||||||
void mapStringValues();
|
void mapStringValues();
|
||||||
@ -129,6 +135,14 @@ private slots:
|
|||||||
|
|
||||||
void cborValueRef_data();
|
void cborValueRef_data();
|
||||||
void cborValueRef();
|
void cborValueRef();
|
||||||
|
void cborValueRefMutatingArray_data() { cborValueRef_data(); }
|
||||||
|
void cborValueRefMutatingArray();
|
||||||
|
void cborValueRefMutatingMapIntKey_data() { cborValueRef_data(); }
|
||||||
|
void cborValueRefMutatingMapIntKey();
|
||||||
|
void cborValueRefMutatingMapLatin1StringKey_data() { cborValueRef_data(); }
|
||||||
|
void cborValueRefMutatingMapLatin1StringKey();
|
||||||
|
void cborValueRefMutatingMapStringKey_data() { cborValueRef_data(); }
|
||||||
|
void cborValueRefMutatingMapStringKey();
|
||||||
|
|
||||||
void datastreamSerialization_data();
|
void datastreamSerialization_data();
|
||||||
void datastreamSerialization();
|
void datastreamSerialization();
|
||||||
@ -912,6 +926,60 @@ void tst_QCborValue::mapSimpleInitializerList()
|
|||||||
QCOMPARE(i, m.size());
|
QCOMPARE(i, m.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> static void mapFromArray_template(T key)
|
||||||
|
{
|
||||||
|
QFETCH(QCborValue::Type, type);
|
||||||
|
QFETCH(QCborValue, v);
|
||||||
|
if (v.isMap())
|
||||||
|
return; // already a map, nothing will happen
|
||||||
|
|
||||||
|
auto ignoreMessage = [type]() {
|
||||||
|
if (type == QCborValue::Array)
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "Using CBOR array as map forced conversion");
|
||||||
|
};
|
||||||
|
|
||||||
|
// verify forced conversions work
|
||||||
|
// (our only Array row is an empty array, so it doesn't produce the warning)
|
||||||
|
QCborValue v2 = v;
|
||||||
|
QVERIFY(v2[key].isUndefined());
|
||||||
|
QCOMPARE(v2.type(), QCborValue::Map);
|
||||||
|
QCOMPARE(v.type(), type);
|
||||||
|
QCborMap m = v2.toMap();
|
||||||
|
QCOMPARE(m.size(), 1);
|
||||||
|
QCOMPARE(m.begin().key(), QCborValue(key));
|
||||||
|
|
||||||
|
// non-empty array conversions
|
||||||
|
QCborValue va = QCborArray{v};
|
||||||
|
v2 = va;
|
||||||
|
ignoreMessage();
|
||||||
|
QVERIFY(v2[key].isUndefined());
|
||||||
|
QCOMPARE(v2.type(), QCborValue::Map);
|
||||||
|
QCOMPARE(va.type(), QCborValue::Array);
|
||||||
|
m = v2.toMap();
|
||||||
|
QCOMPARE(m.size(), 2);
|
||||||
|
auto it = m.constBegin();
|
||||||
|
QCOMPARE(it.key(), QCborValue(0));
|
||||||
|
QCOMPARE(it.value(), v);
|
||||||
|
++it;
|
||||||
|
QCOMPARE(it.key(), QCborValue(key));
|
||||||
|
QCOMPARE(it.value(), QCborValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::mapFromArrayLargeIntKey()
|
||||||
|
{
|
||||||
|
mapFromArray_template(Q_INT64_C(1) << 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::mapFromArrayNegativeIntKey()
|
||||||
|
{
|
||||||
|
mapFromArray_template(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::mapFromArrayStringKey()
|
||||||
|
{
|
||||||
|
mapFromArray_template(QLatin1String("Hello"));
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QCborValue::arrayMutation()
|
void tst_QCborValue::arrayMutation()
|
||||||
{
|
{
|
||||||
QCborArray a{42};
|
QCborArray a{42};
|
||||||
@ -983,14 +1051,6 @@ void tst_QCborValue::arrayMutation()
|
|||||||
QVERIFY(val.isArray());
|
QVERIFY(val.isArray());
|
||||||
QCOMPARE(val.toArray().size(), 4);
|
QCOMPARE(val.toArray().size(), 4);
|
||||||
QCOMPARE(val[3], 42);
|
QCOMPARE(val[3], 42);
|
||||||
|
|
||||||
// Coerce to map on string key:
|
|
||||||
const QLatin1String any("any");
|
|
||||||
val[any] = any;
|
|
||||||
QVERIFY(val.isMap());
|
|
||||||
QCOMPARE(val.toMap().size(), 5);
|
|
||||||
QVERIFY(val[2].isArray());
|
|
||||||
QCOMPARE(val[2].toArray().size(), 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QCborValue::arrayMutateWithCopies()
|
void tst_QCborValue::arrayMutateWithCopies()
|
||||||
@ -2657,6 +2717,145 @@ void tst_QCborValue::cborValueRef()
|
|||||||
QCOMPARE(ref.toDiagnosticNotation(), v.toDiagnosticNotation());
|
QCOMPARE(ref.toDiagnosticNotation(), v.toDiagnosticNotation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::cborValueRefMutatingArray()
|
||||||
|
{
|
||||||
|
// complements arrayMutation()
|
||||||
|
QFETCH(QCborValue, v);
|
||||||
|
|
||||||
|
{
|
||||||
|
QCborArray origArray = { 123 };
|
||||||
|
QCborArray a = { QCborValue(origArray) };
|
||||||
|
QCborValueRef ref = a[0];
|
||||||
|
QVERIFY(ref.isArray());
|
||||||
|
QVERIFY(!ref.toArray().isEmpty());
|
||||||
|
|
||||||
|
// this will force the array to grow
|
||||||
|
ref[1] = v;
|
||||||
|
|
||||||
|
QCborValue va = a.at(0);
|
||||||
|
QVERIFY(va.isArray());
|
||||||
|
QCOMPARE(va.toArray().size(), 2);
|
||||||
|
QCOMPARE(va.toArray().first(), 123);
|
||||||
|
QCOMPARE(va.toArray().last(), v);
|
||||||
|
|
||||||
|
// ensure the array didn't get modified
|
||||||
|
QCOMPARE(origArray, QCborArray{123});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
QCborArray emptyArray;
|
||||||
|
QCborArray a = { QCborValue(emptyArray) };
|
||||||
|
QCborValueRef ref = a[0];
|
||||||
|
QVERIFY(ref.isArray());
|
||||||
|
QVERIFY(ref.toArray().isEmpty());
|
||||||
|
|
||||||
|
// this will force the array to become non-empty
|
||||||
|
ref[1] = v;
|
||||||
|
|
||||||
|
QCborValue va = a.at(0);
|
||||||
|
QVERIFY(va.isArray());
|
||||||
|
QCOMPARE(va.toArray().size(), 2);
|
||||||
|
QCOMPARE(va.toArray().first(), QCborValue());
|
||||||
|
QCOMPARE(va.toArray().last(), v);
|
||||||
|
|
||||||
|
// ensure the array didn't get modified
|
||||||
|
QCOMPARE(emptyArray, QCborArray());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
QCborArray emptyArray = { 123, 456 };
|
||||||
|
emptyArray.takeFirst();
|
||||||
|
emptyArray.takeFirst();
|
||||||
|
QCborArray a = { QCborValue(emptyArray) };
|
||||||
|
QCborValueRef ref = a[0];
|
||||||
|
QVERIFY(ref.isArray());
|
||||||
|
QVERIFY(ref.toArray().isEmpty());
|
||||||
|
|
||||||
|
// this will force the array to become non-empty
|
||||||
|
ref[1] = v;
|
||||||
|
|
||||||
|
QCborValue va = a.at(0);
|
||||||
|
QVERIFY(va.isArray());
|
||||||
|
QCOMPARE(va.toArray().size(), 2);
|
||||||
|
QCOMPARE(va.toArray().first(), QCborValue());
|
||||||
|
QCOMPARE(va.toArray().last(), v);
|
||||||
|
|
||||||
|
// ensure the array didn't get modified
|
||||||
|
QCOMPARE(emptyArray, QCborArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::cborValueRefMutatingMapIntKey()
|
||||||
|
{
|
||||||
|
// complements mapMutation()
|
||||||
|
QFETCH(QCborValue, v);
|
||||||
|
QCborValue::Type type = v.type();
|
||||||
|
|
||||||
|
auto executeTest = [=](qint64 index) {
|
||||||
|
QCborArray a = { v };
|
||||||
|
QCborValueRef ref = a[0];
|
||||||
|
|
||||||
|
if (type == QCborValue::Array && !v.toArray().isEmpty())
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "Using CBOR array as map forced conversion");
|
||||||
|
ref[index] = v;
|
||||||
|
|
||||||
|
QCborValue vm = a.at(0);
|
||||||
|
QVERIFY(vm.isMap());
|
||||||
|
QCOMPARE(vm.toMap()[index].type(), type);
|
||||||
|
QCOMPARE(vm.toMap()[index], v);
|
||||||
|
|
||||||
|
if (type == QCborValue::Array && !v.toArray().isEmpty())
|
||||||
|
QCOMPARE(vm.toMap()[0], v.toArray()[0]);
|
||||||
|
else if (type == QCborValue::Map && !v.toMap().isEmpty())
|
||||||
|
QCOMPARE(vm.toMap()[0], v.toMap()[0]);
|
||||||
|
};
|
||||||
|
// accessing a negative index causes it to become a map
|
||||||
|
executeTest(-1);
|
||||||
|
if (QTest::currentTestFailed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if the index is bigger than 0x10000, the array becomes a map
|
||||||
|
executeTest(0x10000);
|
||||||
|
if (QTest::currentTestFailed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (type != QCborValue::Array)
|
||||||
|
executeTest(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename String> static void cborValueRefMutatingMapStringKey_template(const String &key)
|
||||||
|
{
|
||||||
|
// complements mapMutation() too
|
||||||
|
QFETCH(QCborValue, v);
|
||||||
|
QCborValue::Type type = v.type();
|
||||||
|
QCborArray a = { v };
|
||||||
|
QCborValueRef ref = a[0];
|
||||||
|
|
||||||
|
if (type == QCborValue::Array && !v.toArray().isEmpty())
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "Using CBOR array as map forced conversion");
|
||||||
|
|
||||||
|
// force conversion to map
|
||||||
|
ref[key] = v;
|
||||||
|
|
||||||
|
QCborValue vm = a.at(0);
|
||||||
|
QVERIFY(vm.isMap());
|
||||||
|
QCOMPARE(vm.toMap()[key].type(), type);
|
||||||
|
QCOMPARE(vm.toMap()[key], v);
|
||||||
|
|
||||||
|
if (type == QCborValue::Array && !v.toArray().isEmpty())
|
||||||
|
QCOMPARE(vm.toMap()[0], v.toArray()[0]);
|
||||||
|
else if (type == QCborValue::Map && !v.toMap().isEmpty())
|
||||||
|
QCOMPARE(vm.toMap()[0], v.toMap()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::cborValueRefMutatingMapLatin1StringKey()
|
||||||
|
{
|
||||||
|
cborValueRefMutatingMapStringKey_template(QLatin1String("other"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCborValue::cborValueRefMutatingMapStringKey()
|
||||||
|
{
|
||||||
|
cborValueRefMutatingMapStringKey_template(QString("other"));
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QCborValue::datastreamSerialization_data()
|
void tst_QCborValue::datastreamSerialization_data()
|
||||||
{
|
{
|
||||||
addCommonCborData();
|
addCommonCborData();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user