summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/serialization/json/tst_qtjson.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/serialization/json/tst_qtjson.cpp')
-rw-r--r--tests/auto/corelib/serialization/json/tst_qtjson.cpp1061
1 files changed, 911 insertions, 150 deletions
diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
index bc3fc1ce24..54ef9be4f2 100644
--- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp
+++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
@@ -1,32 +1,11 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include <QtTest>
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
+#include <QMap>
+#include <QVariantList>
QT_WARNING_DISABLE_DEPRECATED
@@ -35,6 +14,7 @@ QT_WARNING_DISABLE_DEPRECATED
#include "qjsonvalue.h"
#include "qjsondocument.h"
#include "qregularexpression.h"
+#include "private/qnumeric_p.h"
#include <limits>
#define INVALID_UNICODE "\xCE\xBA\xE1"
@@ -48,6 +28,7 @@ class tst_QtJson: public QObject
private Q_SLOTS:
void initTestCase();
+ void compareCompiles();
void testValueSimple();
void testNumbers();
void testNumbers_2();
@@ -57,6 +38,7 @@ private Q_SLOTS:
void testNumberComparisons();
void testObjectSimple();
+ void testObjectTakeDetach();
void testObjectSmallKeys();
void testObjectInsertCopies();
void testArraySimple();
@@ -67,9 +49,12 @@ private Q_SLOTS:
void testArrayNested();
void testArrayNestedEmpty();
void testArrayComfortOperators();
+ void testArrayEquality_data();
+ void testArrayEquality();
void testObjectNestedEmpty();
void testValueRef();
+ void testValueRefComparison();
void testObjectIteration();
void testArrayIteration();
@@ -82,6 +67,7 @@ private Q_SLOTS:
void nullObject();
void constNullObject();
+ void keySorting_data();
void keySorting();
void undefinedValues();
@@ -101,6 +87,7 @@ private Q_SLOTS:
void toJson();
void toJsonSillyNumericValues();
void toJsonLargeNumericValues();
+ void toJsonDenormalValues();
void fromJson();
void fromJsonErrors();
void parseNumbers();
@@ -114,7 +101,10 @@ private Q_SLOTS:
void testCompaction();
void testDebugStream();
- void parseUnicodeEscapes();
+ void parseEscapes_data();
+ void parseEscapes();
+ void makeEscapes_data();
+ void makeEscapes();
void assignObjects();
void assignArrays();
@@ -128,6 +118,8 @@ private Q_SLOTS:
void objectEquals();
void arrayEquals_data();
void arrayEquals();
+ void documentEquals_data();
+ void documentEquals();
void bom();
void nesting();
@@ -161,6 +153,15 @@ private Q_SLOTS:
void streamVariantSerialization();
void escapeSurrogateCodePoints_data();
void escapeSurrogateCodePoints();
+
+ void fromToVariantConversions_data();
+ void fromToVariantConversions();
+
+ void testIteratorComparison();
+
+ void noLeakOnNameClash_data();
+ void noLeakOnNameClash();
+
private:
QString testDataDir;
};
@@ -172,6 +173,31 @@ void tst_QtJson::initTestCase()
testDataDir = QCoreApplication::applicationDirPath();
}
+void tst_QtJson::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QJsonArray>();
+ QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::iterator>();
+ QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::const_iterator>();
+ QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::iterator,
+ QJsonArray::const_iterator>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonDocument>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject::iterator>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject::const_iterator>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonValue>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonValueConstRef>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonArray, QJsonValue>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValue>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValueConstRef>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValueRef>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonValueConstRef, QJsonValue>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef, QJsonValue>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef, QJsonValueConstRef>();
+ QTestPrivate::testEqualityOperatorsCompile<QJsonObject::iterator,
+ QJsonObject::const_iterator>();
+}
+
void tst_QtJson::testValueSimple()
{
QJsonObject object;
@@ -371,24 +397,24 @@ void tst_QtJson::testNumbers_2()
QJsonDocument jDocument2(QJsonDocument::fromJson(ba));
for (int power = 0; power <= 1075; power++) {
floatValues_1[power] = jDocument2.object().value(QString::number(power)).toDouble();
-#ifdef Q_OS_QNX
- if (power >= 970)
- QEXPECT_FAIL("", "See QTBUG-37066", Abort);
-#endif
QVERIFY2(floatValues[power] == floatValues_1[power], QString("floatValues[%1] != floatValues_1[%1]").arg(power).toLatin1());
}
+ QT_TEST_EQUALITY_OPS(jDocument1, jDocument2, true);
// The last value is below min denorm and should round to 0, everything else should contain a value
QVERIFY2(floatValues_1[1075] == 0, "Value after min denorm should round to 0");
// Validate the last actual value is min denorm
QVERIFY2(floatValues_1[1074] == 4.9406564584124654417656879286822e-324, QString("Min denorm value is incorrect: %1").arg(floatValues_1[1074]).toLatin1());
- // Validate that every value is half the value before it up to 1
- for (int index = 1074; index > 0; index--) {
- QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0").arg(index).toLatin1());
-
- QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1());
+ if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) {
+ // Validate that every value is half the value before it up to 1
+ for (int index = 1074; index > 0; index--) {
+ QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0").arg(index).toLatin1());
+ QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1());
+ }
+ } else {
+ qInfo("Skipping denormal test as this system's double type lacks support");
}
}
@@ -406,6 +432,10 @@ void tst_QtJson::testNumbers_3()
QJsonDocument jDocument2(QJsonDocument::fromJson(ba));
+ QT_TEST_EQUALITY_OPS(jDocument1, jDocument2, true);
+ QT_TEST_EQUALITY_OPS(jDocument1, QJsonDocument(), false);
+ QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(), true);
+
double d1_1(jDocument2.object().value("d1").toDouble());
double d2_1(jDocument2.object().value("d2").toDouble());
QVERIFY(d1_1 != d2_1);
@@ -423,7 +453,8 @@ void tst_QtJson::testNumbers_4()
array << QJsonValue(-9223372036854775808.0);
array << QJsonValue(+18446744073709551616.0);
array << QJsonValue(-18446744073709551616.0);
- const QByteArray json(QJsonDocument(array).toJson());
+ QJsonDocument doc1 = QJsonDocument(array);
+ const QByteArray json(doc1.toJson());
const QByteArray expected =
"[\n"
" 1000000000000000,\n"
@@ -444,7 +475,8 @@ void tst_QtJson::testNumbers_4()
array2 << QJsonValue(Q_INT64_C(-9007199254740992));
array2 << QJsonValue(Q_INT64_C(+9223372036854775807));
array2 << QJsonValue(Q_INT64_C(-9223372036854775807));
- const QByteArray json2(QJsonDocument(array2).toJson());
+ QJsonDocument doc2 = QJsonDocument(array2);
+ const QByteArray json2(doc2.toJson());
const QByteArray expected2 =
"[\n"
" 1000000000000000,\n"
@@ -455,6 +487,8 @@ void tst_QtJson::testNumbers_4()
" -9223372036854775807\n"
"]\n";
QCOMPARE(json2, expected2);
+
+ QT_TEST_EQUALITY_OPS(doc1, doc2, false);
}
void tst_QtJson::testNumberComparisons()
@@ -503,6 +537,7 @@ void tst_QtJson::testObjectSimple()
QJsonValue value(QLatin1String("foo"));
object.insert("value", value);
QCOMPARE(object.value("value"), value);
+ QT_TEST_EQUALITY_OPS(object.value("value"), value, true);
int size = object.size();
object.remove("boolean");
@@ -511,6 +546,7 @@ void tst_QtJson::testObjectSimple()
QJsonValue taken = object.take("value");
QCOMPARE(taken, value);
+ QT_TEST_EQUALITY_OPS(taken, value, true);
QVERIFY2(!object.contains("value"), "key value should have been removed");
QString before = object.value("string").toString();
@@ -567,6 +603,24 @@ void tst_QtJson::testObjectSimple()
QCOMPARE(subvalue.toObject(), subobject);
}
+void tst_QtJson::testObjectTakeDetach()
+{
+ QJsonObject object1, object2;
+ object1["key1"] = 1;
+ object1["key2"] = 2;
+ object2 = object1;
+
+ object1.take("key2");
+ object1.remove("key1");
+ QVERIFY(!object1.contains("key1"));
+ QVERIFY(object2.contains("key1"));
+ QVERIFY(object2.value("key1").isDouble());
+
+ QVERIFY(!object1.contains("key2"));
+ QVERIFY(object2.contains("key2"));
+ QVERIFY(object2.value("key2").isDouble());
+}
+
void tst_QtJson::testObjectSmallKeys()
{
QJsonObject data1;
@@ -622,6 +676,7 @@ void tst_QtJson::testObjectInsertCopies()
QCOMPARE(obj.size(), 2);
QCOMPARE(obj.value("value"), "TEST");
QCOMPARE(obj.value("prop2"), "TEST");
+ QT_TEST_EQUALITY_OPS(rv, obj["value"].toObject(), true);
}
{
QJsonObject obj;
@@ -754,15 +809,20 @@ void tst_QtJson::testValueObject()
void tst_QtJson::testValueArray()
{
QJsonArray array;
+ QJsonArray otherArray = {"wrong value"};
+ QJsonValue value(array);
+ QCOMPARE(value.toArray(), array);
+ QCOMPARE(value.toArray(otherArray), array);
+
array.append(999.);
array.append(QLatin1String("test"));
array.append(true);
-
- QJsonValue value(array);
+ value = array;
// if we don't modify the original JsonArray, toArray()
// on the JsonValue should return the same object (non-detached).
QCOMPARE(value.toArray(), array);
+ QCOMPARE(value.toArray(otherArray), array);
// if we modify the original array, it should detach
array.append(QLatin1String("test"));
@@ -772,14 +832,28 @@ void tst_QtJson::testValueArray()
void tst_QtJson::testObjectNested()
{
QJsonObject inner, outer;
+ QJsonObject otherObject = {{"wrong key", "wrong value"}};
+ QJsonValue v = inner;
+ QCOMPARE(v.toObject(), inner);
+ QCOMPARE(v.toObject(otherObject), inner);
+ QT_TEST_EQUALITY_OPS(v.toObject(), inner, true);
+ QT_TEST_EQUALITY_OPS(v.toObject(otherObject), inner, true);
+
inner.insert("number", 999.);
outer.insert("nested", inner);
+ QT_TEST_EQUALITY_OPS(outer, inner, false);
// if we don't modify the original JsonObject, value()
// should return the same object (non-detached).
QJsonObject value = outer.value("nested").toObject();
+ v = value;
QCOMPARE(value, inner);
QCOMPARE(value.value("number").toDouble(), 999.);
+ QCOMPARE(v.toObject(), inner);
+ QCOMPARE(v.toObject(otherObject), inner);
+ QT_TEST_EQUALITY_OPS(v.toObject(), inner, true);
+ QT_TEST_EQUALITY_OPS(v.toObject(otherObject), inner, true);
+ QCOMPARE(v["number"].toDouble(), 999.);
// if we modify the original object, it should detach and not
// affect the nested object
@@ -804,6 +878,7 @@ void tst_QtJson::testObjectNested()
QCOMPARE(outer.value("nested").toObject().value("nested").toObject(), twoDeep);
QCOMPARE(outer.value("nested").toObject().value("nested").toObject().value("boolean").toBool(),
true);
+ QT_TEST_EQUALITY_OPS(outer.value("nested").toObject().value("nested").toObject(), twoDeep, true);
}
void tst_QtJson::testArrayNested()
@@ -829,6 +904,7 @@ void tst_QtJson::testArrayNested()
object.insert("boolean", true);
outer.append(object);
QCOMPARE(outer.last().toObject(), object);
+ QT_TEST_EQUALITY_OPS(outer.last().toObject(), object, true);
QCOMPARE(outer.last().toObject().value("boolean").toBool(), true);
// two deep arrays
@@ -848,6 +924,7 @@ void tst_QtJson::testArrayNestedEmpty()
QJsonValue val = object.value("inner");
QJsonArray value = object.value("inner").toArray();
QVERIFY(QJsonDocument(value).isArray());
+ QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(value), false);
QCOMPARE(value.size(), 0);
QCOMPARE(value, inner);
QCOMPARE(value.size(), 0);
@@ -865,14 +942,46 @@ void tst_QtJson::testObjectNestedEmpty()
object.insert("inner2", inner2);
QJsonObject value = object.value("inner").toObject();
QVERIFY(QJsonDocument(value).isObject());
+ QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(value), false);
QCOMPARE(value.size(), 0);
QCOMPARE(value, inner);
+ QT_TEST_EQUALITY_OPS(value, inner, true);
QCOMPARE(value.size(), 0);
object.insert("count", 0.);
QCOMPARE(object.value("inner").toObject().size(), 0);
QCOMPARE(object.value("inner").type(), QJsonValue::Object);
}
+void tst_QtJson::testArrayEquality_data()
+{
+ QTest::addColumn<QJsonArray>("array1");
+ QTest::addColumn<QJsonArray>("array2");
+ QTest::addColumn<bool>("expectedResult");
+ QTest::addRow("QJsonArray(), QJsonArray{665, 666, 667}")
+ << QJsonArray() << QJsonArray{665, 666, 667} << false;
+ QTest::addRow("QJsonArray(), QJsonArray{}")
+ << QJsonArray() << QJsonArray{} <<true;
+ QTest::addRow("QJsonArray(), QJsonArray{123, QLatin1String(\"foo\")}")
+ << QJsonArray() << QJsonArray{123, QLatin1String("foo")} << false;
+ QTest::addRow(
+ "QJsonArray{123,QLatin1String(\"foo\")}, QJsonArray{123,QLatin1String(\"foo\")}")
+ << QJsonArray{123, QLatin1String("foo")}
+ << QJsonArray{123, QLatin1String("foo")}
+ << true;
+}
+
+void tst_QtJson::testArrayEquality()
+{
+ QFETCH(QJsonArray, array1);
+ QFETCH(QJsonArray, array2);
+ QFETCH(bool, expectedResult);
+
+ QJsonValue value = QJsonValue(array1);
+
+ QT_TEST_EQUALITY_OPS(array1, array2, expectedResult);
+ QT_TEST_EQUALITY_OPS(value, array2, expectedResult);
+}
+
void tst_QtJson::testArrayComfortOperators()
{
QJsonArray first;
@@ -912,7 +1021,7 @@ void tst_QtJson::testValueRef()
QCOMPARE(object.value(QLatin1String("null")), QJsonValue());
object[QLatin1String("null")] = 100.;
QCOMPARE(object.value(QLatin1String("null")).type(), QJsonValue::Double);
- QJsonValue val = qAsConst(object)[QLatin1String("null")];
+ QJsonValue val = std::as_const(object)[QLatin1String("null")];
QCOMPARE(val.toDouble(), 100.);
QCOMPARE(object.size(), 2);
@@ -922,21 +1031,79 @@ void tst_QtJson::testValueRef()
QCOMPARE(object.value(QLatin1String("key")), QJsonValue(42));
}
+void tst_QtJson::testValueRefComparison()
+{
+ QJsonValue a0 = 42.;
+ QJsonValue a1 = QStringLiteral("142");
+
+#define CHECK_IMPL(lhs, rhs, ineq) \
+ QCOMPARE(lhs, rhs); \
+ QVERIFY(!(lhs != rhs)); \
+ QVERIFY(lhs != ineq); \
+ QVERIFY(!(lhs == ineq)); \
+ QVERIFY(ineq != rhs); \
+ QVERIFY(!(ineq == rhs)); \
+ /* end */
+
+#define CHECK(lhs, rhs, ineq) \
+ do { \
+ CHECK_IMPL(lhs, rhs, ineq) \
+ CHECK_IMPL(std::as_const(lhs), rhs, ineq) \
+ CHECK_IMPL(lhs, std::as_const(rhs), ineq) \
+ CHECK_IMPL(std::as_const(lhs), std::as_const(rhs), ineq) \
+ } while (0)
+
+ // check that the (in)equality operators aren't ambiguous in C++20:
+ QJsonArray a = {a0, a1};
+
+ static_assert(std::is_same_v<decltype(a[0]), QJsonValueRef>);
+
+ auto r0 = a.begin()[0];
+ auto r1 = a.begin()[1];
+ auto c0 = std::as_const(a).begin()[0];
+ // ref <> ref
+ CHECK(r0, r0, r1);
+ // cref <> ref
+ CHECK(c0, r0, r1);
+ // ref <> cref
+ CHECK(r0, c0, r1);
+ // ref <> val
+ CHECK(r0, a0, r1);
+ // cref <> val
+ CHECK(c0, a0, r1);
+ // val <> ref
+ CHECK(a0, r0, a1);
+ // val <> cref
+ CHECK(a0, c0, a1);
+ // val <> val
+ CHECK(a0, a0, a1);
+
+ QT_TEST_EQUALITY_OPS(r0, r1, false);
+ QT_TEST_EQUALITY_OPS(r0, c0, true);
+ QT_TEST_EQUALITY_OPS(c0, r1, false);
+ QT_TEST_EQUALITY_OPS(a0, c0, true);
+ QT_TEST_EQUALITY_OPS(a0, r1, false);
+
+#undef CHECK
+#undef CHECK_IMPL
+}
+
void tst_QtJson::testObjectIteration()
{
QJsonObject object;
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it)
- QVERIFY(false);
+ QFAIL("Iterator of default-initialized object should be empty");
const QString property = "kkk";
object.insert(property, 11);
object.take(property);
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it)
- QVERIFY(false);
+ QFAIL("Iterator after property add-and-remove should be empty");
- for (int i = 0; i < 10; ++i)
- object[QString::number(i)] = (double)i;
+ // insert in weird order to confirm keys are sorted
+ for (int i : {0, 9, 5, 7, 8, 2, 1, 3, 6, 4})
+ object[QString::number(i)] = double(i);
QCOMPARE(object.size(), 10);
@@ -945,44 +1112,79 @@ void tst_QtJson::testObjectIteration()
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) {
QJsonValue value = it.value();
QCOMPARE((double)it.key().toInt(), value.toDouble());
+ QT_TEST_EQUALITY_OPS(it, QJsonObject::iterator(), false);
}
{
QJsonObject object2 = object;
QCOMPARE(object, object2);
+ QT_TEST_EQUALITY_OPS(object, object2, true);
QJsonValue val = *object2.begin();
- object2.erase(object2.begin());
+ auto next = object2.erase(object2.begin());
QCOMPARE(object.size(), 10);
QCOMPARE(object2.size(), 9);
+ QVERIFY(next == object2.begin());
+ QT_TEST_EQUALITY_OPS(next, object2.begin(), true);
+
+ double d = 1; // we erased the first item
+ for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, d += 1) {
+ QJsonValue value = it.value();
+ QVERIFY(it.value() != val);
+ QCOMPARE(it.value(), d);
+ QCOMPARE(it.value().toDouble(), d);
+ QCOMPARE(it.key().toInt(), value.toDouble());
+ }
+ }
+
+ {
+ QJsonObject object2 = object;
+ QCOMPARE(object, object2);
+ QT_TEST_EQUALITY_OPS(object, object2, true);
- for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) {
+ QJsonValue val = *(object2.end() - 1);
+ auto next = object2.erase(object2.end() - 1);
+ QCOMPARE(object.size(), 10);
+ QCOMPARE(object2.size(), 9);
+ QVERIFY(next == object2.end());
+ double d = 0;
+ for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, d += 1) {
QJsonValue value = it.value();
QVERIFY(it.value() != val);
- QCOMPARE((double)it.key().toInt(), value.toDouble());
+ QCOMPARE(it.value(), d);
+ QCOMPARE(it.value().toDouble(), d);
+ QCOMPARE(it.key().toInt(), value.toDouble());
}
}
{
QJsonObject object2 = object;
QCOMPARE(object, object2);
+ QT_TEST_EQUALITY_OPS(object, object2, true);
QJsonObject::iterator it = object2.find(QString::number(5));
QJsonValue val = *it;
- object2.erase(it);
+ auto next = object2.erase(it);
QCOMPARE(object.size(), 10);
QCOMPARE(object2.size(), 9);
+ QCOMPARE(*next, 6);
- for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) {
+ int i = 0;
+ for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, ++i) {
+ if (i == 5)
+ ++i;
QJsonValue value = it.value();
QVERIFY(it.value() != val);
- QCOMPARE((double)it.key().toInt(), value.toDouble());
+ QCOMPARE(it.value(), i);
+ QCOMPARE(it.value().toInt(), i);
+ QCOMPARE(it.key().toInt(), value.toDouble());
}
}
{
QJsonObject::Iterator it = object.begin();
it += 5;
+ QT_TEST_ALL_COMPARISON_OPS(it, object.begin(), Qt::strong_ordering::greater);
QCOMPARE(QJsonValue(it.value()).toDouble(), 5.);
it -= 3;
QCOMPARE(QJsonValue(it.value()).toDouble(), 2.);
@@ -997,10 +1199,14 @@ void tst_QtJson::testObjectIteration()
it += 5;
QCOMPARE(QJsonValue(it.value()).toDouble(), 5.);
it -= 3;
+ QT_TEST_ALL_COMPARISON_OPS(object.constBegin(), it, Qt::strong_ordering::less);
QCOMPARE(QJsonValue(it.value()).toDouble(), 2.);
QJsonObject::ConstIterator it2 = it + 5;
+ QT_TEST_EQUALITY_OPS(it, it2, false);
QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.);
it2 = it - 1;
+ QT_TEST_ALL_COMPARISON_OPS(it2, it, Qt::strong_ordering::less);
+ QT_TEST_ALL_COMPARISON_OPS(it2, it - 2, Qt::strong_ordering::greater);
QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.);
}
@@ -1009,6 +1215,17 @@ void tst_QtJson::testObjectIteration()
it = object.erase(it);
QCOMPARE(object.size() , 0);
QCOMPARE(it, object.end());
+ QT_TEST_ALL_COMPARISON_OPS(it, object.end(), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(it, object.constEnd(), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(it, object.begin(),
+ Qt::strong_ordering::equal); // because object is empty
+ QT_TEST_ALL_COMPARISON_OPS(it, object.constBegin(), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(QJsonObject::Iterator(),
+ QJsonObject::Iterator(), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(QJsonObject::ConstIterator(),
+ QJsonObject::Iterator(), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(QJsonObject::ConstIterator(),
+ QJsonObject::ConstIterator(), Qt::strong_ordering::equal);
}
void tst_QtJson::testArrayIteration()
@@ -1022,7 +1239,11 @@ void tst_QtJson::testArrayIteration()
int i = 0;
for (QJsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) {
QJsonValue value = (*it);
+ QJsonArray::iterator it1 = it;
QCOMPARE((double)i, value.toDouble());
+ QT_TEST_EQUALITY_OPS(QJsonArray::iterator(), QJsonArray::iterator(), true);
+ QT_TEST_EQUALITY_OPS(QJsonArray::iterator(), it, false);
+ QT_TEST_EQUALITY_OPS(it1, it, true);
}
QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble());
@@ -1032,14 +1253,38 @@ void tst_QtJson::testArrayIteration()
QCOMPARE(array, array2);
QJsonValue val = *array2.begin();
- array2.erase(array2.begin());
+ auto next = array2.erase(array2.begin());
QCOMPARE(array.size(), 10);
QCOMPARE(array2.size(), 9);
+ QVERIFY(next == array2.begin());
i = 1;
- for (QJsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) {
+ for (auto it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) {
QJsonValue value = (*it);
- QCOMPARE((double)i, value.toDouble());
+ QCOMPARE(value.toInt(), i);
+ QCOMPARE(value.toDouble(), i);
+ QCOMPARE(it->toInt(), i);
+ QCOMPARE(it->toDouble(), i);
+ }
+ }
+
+ {
+ QJsonArray array2 = array;
+ QCOMPARE(array, array2);
+
+ QJsonValue val = array2.last();
+ auto next = array2.erase(array2.end() - 1);
+ QCOMPARE(array.size(), 10);
+ QCOMPARE(array2.size(), 9);
+ QVERIFY(next == array2.end());
+
+ i = 0;
+ for (auto it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) {
+ QJsonValue value = (*it);
+ QCOMPARE(value.toInt(), i);
+ QCOMPARE(value.toDouble(), i);
+ QCOMPARE(it->toInt(), i);
+ QCOMPARE(it->toDouble(), i);
}
}
@@ -1053,6 +1298,13 @@ void tst_QtJson::testArrayIteration()
QCOMPARE(QJsonValue(*it2).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(QJsonValue(*it2).toDouble(), 1.);
+ QT_TEST_EQUALITY_OPS(it, it2, false);
+ it = array.begin();
+ QT_TEST_EQUALITY_OPS(it, array.begin(), true);
+ it2 = it + 5;
+ QT_TEST_ALL_COMPARISON_OPS(it2, it, Qt::strong_ordering::greater);
+ it += 5;
+ QT_TEST_EQUALITY_OPS(it, it2, true);
}
{
@@ -1072,6 +1324,26 @@ void tst_QtJson::testArrayIteration()
it = array.erase(it);
QCOMPARE(array.size() , 0);
QCOMPARE(it, array.end());
+ QT_TEST_EQUALITY_OPS(it, array.end(), true);
+
+ {
+ int i = 0;
+ for (QJsonArray::const_iterator it = array.constBegin();
+ it != array.constEnd(); ++it, ++i) {
+ QJsonArray::const_iterator it1 = it;
+ QT_TEST_EQUALITY_OPS(QJsonArray::const_iterator(), QJsonArray::const_iterator(), true);
+ QT_TEST_EQUALITY_OPS(QJsonArray::const_iterator(), it, false);
+ QT_TEST_EQUALITY_OPS(it1, it, true);
+ }
+ }
+
+ {
+ QJsonArray::iterator nonConstIt = array.begin();
+ QJsonArray::const_iterator it = array.constBegin();
+ QT_TEST_EQUALITY_OPS(nonConstIt, it, true);
+ it+=1;
+ QT_TEST_ALL_COMPARISON_OPS(nonConstIt, it, Qt::strong_ordering::less);
+ }
}
void tst_QtJson::testObjectFind()
@@ -1085,14 +1357,12 @@ void tst_QtJson::testObjectFind()
QJsonObject::iterator it = object.find(QLatin1String("1"));
QCOMPARE((*it).toDouble(), 1.);
it = object.find(QString("11"));
- QCOMPARE((*it).type(), QJsonValue::Undefined);
QCOMPARE(it, object.end());
QJsonObject::const_iterator cit = object.constFind(QLatin1String("1"));
QCOMPARE((*cit).toDouble(), 1.);
cit = object.constFind(QString("11"));
- QCOMPARE((*it).type(), QJsonValue::Undefined);
- QCOMPARE(it, object.end());
+ QCOMPARE(cit, object.constEnd());
}
void tst_QtJson::testDocument()
@@ -1167,6 +1437,8 @@ void tst_QtJson::testDocument()
QCOMPARE(doc5.isObject(), false);
QCOMPARE(doc5.array().size(), 1);
QCOMPARE(doc5.array().at(0), QJsonValue(23));
+
+ QT_TEST_EQUALITY_OPS(doc2, doc3, true);
}
void tst_QtJson::nullValues()
@@ -1257,21 +1529,55 @@ void tst_QtJson::constNullObject()
QCOMPARE(nullObject["foo"], QJsonValue(QJsonValue::Undefined));
}
-void tst_QtJson::keySorting()
+void tst_QtJson::keySorting_data()
{
+ QTest::addColumn<QString>("json");
+ QTest::addColumn<QStringList>("sortedKeys");
+
+ QStringList list = {"A", "B"};
+ QTest::newRow("sorted-ascii-2") << R"({ "A": false, "B": true })" << list;
const char *json = "{ \"B\": true, \"A\": false }";
- QJsonDocument doc = QJsonDocument::fromJson(json);
+ QTest::newRow("unsorted-ascii-2") << json << list;
+
+ list = QStringList{"A", "B", "C", "D", "E"};
+ QTest::newRow("sorted-ascii-5") << R"({"A": 1, "B": 2, "C": 3, "D": 4, "E": 5})" << list;
+ QTest::newRow("unsorted-ascii-5") << R"({"A": 1, "C": 3, "D": 4, "B": 2, "E": 5})" << list;
+ QTest::newRow("inverse-sorted-ascii-5") << R"({"E": 5, "D": 4, "C": 3, "B": 2, "A": 1})" << list;
+
+ list = QStringList{"á", "é", "í", "ó", "ú"};
+ QTest::newRow("sorted-latin1") << R"({"á": 1, "é": 2, "í": 3, "ó": 4, "ú": 5})" << list;
+ QTest::newRow("unsorted-latin1") << R"({"á": 1, "í": 3, "ó": 4, "é": 2, "ú": 5})" << list;
+ QTest::newRow("inverse-sorted-latin1") << R"({"ú": 5, "ó": 4, "í": 3, "é": 2, "á": 1})" << list;
+
+ QTest::newRow("sorted-escaped-latin1") << R"({"\u00e1": 1, "\u00e9": 2, "\u00ed": 3, "\u00f3": 4, "\u00fa": 5})" << list;
+ QTest::newRow("unsorted-escaped-latin1") << R"({"\u00e1": 1, "\u00ed": 3, "\u00f3": 4, "\u00e9": 2, "\u00fa": 5})" << list;
+ QTest::newRow("inverse-sorted-escaped-latin1") << R"({"\u00fa": 5, "\u00f3": 4, "\u00ed": 3, "\u00e9": 2, "\u00e1": 1})" << list;
+
+ list = QStringList{"A", "α", "Я", "€", "测"};
+ QTest::newRow("sorted") << R"({"A": 1, "α": 2, "Я": 3, "€": 4, "测": 5})" << list;
+ QTest::newRow("unsorted") << R"({"A": 1, "Я": 3, "€": 4, "α": 2, "测": 5})" << list;
+ QTest::newRow("inverse-sorted") << R"({"测": 5, "€": 4, "Я": 3, "α": 2, "A": 1})" << list;
+
+ QTest::newRow("sorted-escaped") << R"({"A": 1, "\u03b1": 2, "\u042f": 3, "\u20ac": 4, "\u6d4b": 5})" << list;
+ QTest::newRow("unsorted-escaped") << R"({"A": 1, "\u042f": 3, "\u20ac": 4, "\u03b1": 2, "\u6d4b": 5})" << list;
+ QTest::newRow("inverse-sorted-escaped") << R"({"\u6d4b": 5, "\u20ac": 4, "\u042f": 3, "\u03b1": 2, "A": 1})" << list;
+}
+
+void tst_QtJson::keySorting()
+{
+ QFETCH(QString, json);
+ QFETCH(QStringList, sortedKeys);
+ QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
QCOMPARE(doc.isObject(), true);
QJsonObject o = doc.object();
- QCOMPARE(o.size(), 2);
+ QCOMPARE(o.size(), sortedKeys.size());
+ QCOMPARE(o.keys(), sortedKeys);
QJsonObject::const_iterator it = o.constBegin();
- QCOMPARE(it.key(), QLatin1String("A"));
- ++it;
- QCOMPARE(it.key(), QLatin1String("B"));
-
- QCOMPARE(o.keys(), QStringList() << QLatin1String("A") << QLatin1String("B"));
+ QStringList::const_iterator it2 = sortedKeys.constBegin();
+ for ( ; it != o.constEnd(); ++it, ++it2)
+ QCOMPARE(it.key(), *it2);
}
void tst_QtJson::undefinedValues()
@@ -1359,7 +1665,7 @@ void tst_QtJson::fromVariant_data()
jsonObject["null"] = QJsonValue::Null;
jsonObject["default"] = QJsonValue();
- QTest::newRow("default") << QVariant() << QJsonValue(QJsonValue::Null);
+ QTest::newRow("default") << QVariant() << QJsonValue();
QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QJsonValue(QJsonValue::Null);
QTest::newRow("bool") << QVariant(boolValue) << QJsonValue(boolValue);
QTest::newRow("int") << QVariant(intValue) << QJsonValue(intValue);
@@ -1389,6 +1695,14 @@ static QVariant normalizedVariant(const QVariant &v)
out << normalizedVariant(v);
return out;
}
+ case QMetaType::QStringList: {
+ const QStringList in = v.toStringList();
+ QVariantList out;
+ out.reserve(in.size());
+ for (const QString &v : in)
+ out << v;
+ return out;
+ }
case QMetaType::QVariantMap: {
const QVariantMap in = v.toMap();
QVariantMap out;
@@ -1398,7 +1712,7 @@ static QVariant normalizedVariant(const QVariant &v)
}
case QMetaType::QVariantHash: {
const QVariantHash in = v.toHash();
- QVariantHash out;
+ QVariantMap out;
for (auto it = in.begin(); it != in.end(); ++it)
out.insert(it.key(), normalizedVariant(it.value()));
return out;
@@ -1496,7 +1810,8 @@ void tst_QtJson::fromVariantHash()
void tst_QtJson::toVariantMap()
{
- QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().type()), QMetaType::QVariantMap); // QTBUG-32524
+ QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().typeId()),
+ QMetaType::QVariantMap); // QTBUG-32524
QJsonObject object;
QVariantMap map = object.toVariantMap();
@@ -1516,7 +1831,7 @@ void tst_QtJson::toVariantMap()
QCOMPARE(map.size(), 3);
QCOMPARE(map.value("Key"), QVariant(QString("Value")));
QCOMPARE(map.value("null"), QVariant::fromValue(nullptr));
- QCOMPARE(map.value("Array").type(), QVariant::List);
+ QCOMPARE(map.value("Array").typeId(), QMetaType::QVariantList);
QVariantList list = map.value("Array").toList();
QCOMPARE(list.size(), 4);
QCOMPARE(list.at(0), QVariant(true));
@@ -1545,7 +1860,7 @@ void tst_QtJson::toVariantHash()
QCOMPARE(hash.size(), 3);
QCOMPARE(hash.value("Key"), QVariant(QString("Value")));
QCOMPARE(hash.value("null"), QVariant::fromValue(nullptr));
- QCOMPARE(hash.value("Array").type(), QVariant::List);
+ QCOMPARE(hash.value("Array").typeId(), QMetaType::QVariantList);
QVariantList list = hash.value("Array").toList();
QCOMPARE(list.size(), 4);
QCOMPARE(list.at(0), QVariant(true));
@@ -1556,7 +1871,8 @@ void tst_QtJson::toVariantHash()
void tst_QtJson::toVariantList()
{
- QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().type()), QMetaType::QVariantList); // QTBUG-32524
+ QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().typeId()),
+ QMetaType::QVariantList); // QTBUG-32524
QJsonArray array;
QVariantList list = array.toVariantList();
@@ -1576,7 +1892,7 @@ void tst_QtJson::toVariantList()
QCOMPARE(list.size(), 3);
QCOMPARE(list[0], QVariant(QString("Value")));
QCOMPARE(list[1], QVariant::fromValue(nullptr));
- QCOMPARE(list[2].type(), QVariant::List);
+ QCOMPARE(list[2].typeId(), QMetaType::QVariantList);
QVariantList vlist = list[2].toList();
QCOMPARE(vlist.size(), 4);
QCOMPARE(vlist.at(0), QVariant(true));
@@ -1698,16 +2014,13 @@ void tst_QtJson::toJsonLargeNumericValues()
QJsonArray array;
array.append(QJsonValue(1.234567)); // actual precision bug in Qt 5.0.0
array.append(QJsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE
- array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE
array.append(QJsonValue(std::numeric_limits<double>::min()));
array.append(QJsonValue(std::numeric_limits<double>::max()));
array.append(QJsonValue(std::numeric_limits<double>::epsilon()));
- array.append(QJsonValue(std::numeric_limits<double>::denorm_min()));
array.append(QJsonValue(0.0));
array.append(QJsonValue(-std::numeric_limits<double>::min()));
array.append(QJsonValue(-std::numeric_limits<double>::max()));
array.append(QJsonValue(-std::numeric_limits<double>::epsilon()));
- array.append(QJsonValue(-std::numeric_limits<double>::denorm_min()));
array.append(QJsonValue(-0.0));
array.append(QJsonValue(9007199254740992LL)); // JS Number max integer
array.append(QJsonValue(-9007199254740992LL)); // JS Number min integer
@@ -1721,27 +2034,21 @@ void tst_QtJson::toJsonLargeNumericValues()
" 1.234567,\n"
" 1.7976931348623157e+308,\n"
#ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then
- " 4.9406564584124654e-324,\n"
" 2.2250738585072014e-308,\n"
" 1.7976931348623157e+308,\n"
" 2.2204460492503131e-16,\n"
- " 4.9406564584124654e-324,\n"
" 0,\n"
" -2.2250738585072014e-308,\n"
" -1.7976931348623157e+308,\n"
" -2.2204460492503131e-16,\n"
- " -4.9406564584124654e-324,\n"
#else
- " 5e-324,\n"
" 2.2250738585072014e-308,\n"
" 1.7976931348623157e+308,\n"
" 2.220446049250313e-16,\n"
- " 5e-324,\n"
" 0,\n"
" -2.2250738585072014e-308,\n"
" -1.7976931348623157e+308,\n"
" -2.220446049250313e-16,\n"
- " -5e-324,\n"
#endif
" 0,\n"
" 9007199254740992,\n"
@@ -1749,20 +2056,50 @@ void tst_QtJson::toJsonLargeNumericValues()
" ]\n"
"}\n";
-#ifdef Q_OS_QNX
- QEXPECT_FAIL("", "See QTBUG-37066", Continue);
-#endif
QCOMPARE(json, expected);
QJsonDocument doc;
doc.setObject(object);
json = doc.toJson();
-#ifdef Q_OS_QNX
- QEXPECT_FAIL("", "See QTBUG-37066", Continue);
-#endif
QCOMPARE(json, expected);
}
+void tst_QtJson::toJsonDenormalValues()
+{
+ if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) {
+ QJsonObject object;
+ QJsonArray array;
+ array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE
+ array.append(QJsonValue(std::numeric_limits<double>::denorm_min()));
+ array.append(QJsonValue(-std::numeric_limits<double>::denorm_min()));
+ object.insert("Array", array);
+
+ QByteArray json = QJsonDocument(object).toJson();
+ QByteArray expected =
+ "{\n"
+ " \"Array\": [\n"
+#ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then
+ " 4.9406564584124654e-324,\n"
+ " 4.9406564584124654e-324,\n"
+ " -4.9406564584124654e-324\n"
+#else
+ " 5e-324,\n"
+ " 5e-324,\n"
+ " -5e-324\n"
+#endif
+ " ]\n"
+ "}\n";
+
+ QCOMPARE(json, expected);
+ QJsonDocument doc;
+ doc.setObject(object);
+ json = doc.toJson();
+ QCOMPARE(json, expected);
+ } else {
+ QSKIP("Skipping 'denorm' as this type lacks denormals on this system");
+ }
+}
+
void tst_QtJson::fromJson()
{
{
@@ -2085,12 +2422,12 @@ void tst_QtJson::parseNumbers()
QCOMPARE(val.toDouble(), (double)numbers[i].n);
}
}
+ // test number parsing
+ struct Numbers {
+ const char *str;
+ double n;
+ };
{
- // test number parsing
- struct Numbers {
- const char *str;
- double n;
- };
Numbers numbers [] = {
{ "0", 0 },
{ "1", 1 },
@@ -2106,8 +2443,6 @@ void tst_QtJson::parseNumbers()
{ "1.1e10", 1.1e10 },
{ "1.1e308", 1.1e308 },
{ "-1.1e308", -1.1e308 },
- { "1.1e-308", 1.1e-308 },
- { "-1.1e-308", -1.1e-308 },
{ "1.1e+308", 1.1e+308 },
{ "-1.1e+308", -1.1e+308 },
{ "1.e+308", 1.e+308 },
@@ -2119,10 +2454,6 @@ void tst_QtJson::parseNumbers()
json += numbers[i].str;
json += " ]";
QJsonDocument doc = QJsonDocument::fromJson(json);
-#ifdef Q_OS_QNX
- if (0 == QString::compare(numbers[i].str, "1.1e-308"))
- QEXPECT_FAIL("", "See QTBUG-37066", Abort);
-#endif
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
@@ -2133,6 +2464,29 @@ void tst_QtJson::parseNumbers()
QCOMPARE(val.toDouble(), numbers[i].n);
}
}
+ if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) {
+ Numbers numbers [] = {
+ { "1.1e-308", 1.1e-308 },
+ { "-1.1e-308", -1.1e-308 }
+ };
+ int size = sizeof(numbers)/sizeof(Numbers);
+ for (int i = 0; i < size; ++i) {
+ QByteArray json = "[ ";
+ json += numbers[i].str;
+ json += " ]";
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QVERIFY(!doc.isEmpty());
+ QCOMPARE(doc.isArray(), true);
+ QCOMPARE(doc.isObject(), false);
+ QJsonArray array = doc.array();
+ QCOMPARE(array.size(), 1);
+ QJsonValue val = array.at(0);
+ QCOMPARE(val.type(), QJsonValue::Double);
+ QCOMPARE(val.toDouble(), numbers[i].n);
+ }
+ } else {
+ qInfo("Skipping denormal test as this system's double type lacks support");
+ }
}
void tst_QtJson::parseStrings()
@@ -2222,7 +2576,7 @@ void tst_QtJson::parseDuplicateKeys()
void tst_QtJson::testParser()
{
QFile file(testDataDir + "/test.json");
- file.open(QFile::ReadOnly);
+ QVERIFY(file.open(QFile::ReadOnly));
QByteArray testJson = file.readAll();
QJsonDocument doc = QJsonDocument::fromJson(testJson);
@@ -2278,6 +2632,13 @@ void tst_QtJson::testCompaction()
}
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value(QLatin1String("foo")).toString(), QLatin1String("bar"));
+
+ QJsonObject obj2;
+
+ QT_TEST_EQUALITY_OPS(obj, obj2, false);
+ QT_TEST_EQUALITY_OPS(QJsonObject(), obj2, true);
+ obj2 = obj;
+ QT_TEST_EQUALITY_OPS(obj, obj2, true);
}
void tst_QtJson::testDebugStream()
@@ -2367,20 +2728,128 @@ void tst_QtJson::testDebugStream()
}
}
-void tst_QtJson::parseUnicodeEscapes()
+void tst_QtJson::parseEscapes_data()
+{
+ QTest::addColumn<QByteArray>("json");
+ QTest::addColumn<QString>("result");
+
+ auto addUnicodeRow = [](char32_t u) {
+ char buf[32]; // more than enough
+ char *ptr = buf;
+ const QString result = QString::fromUcs4(&u, 1);
+ for (QChar c : result)
+ ptr += snprintf(ptr, std::end(buf) - ptr, "\\u%04x", c.unicode());
+ QTest::addRow("U+%04X", u) << "[\"" + QByteArray(buf) + "\"]" << result;
+ };
+
+ char singleCharJson[] = R"(["\x"])";
+ Q_ASSERT(singleCharJson[3] == 'x');
+ auto makeSingleCharEscape = [&singleCharJson](char c) {
+ singleCharJson[3] = char(c);
+ return QByteArray(singleCharJson, std::size(singleCharJson) - 1);
+ };
+
+ QTest::addRow("quote") << makeSingleCharEscape('"') << "\"";
+ QTest::addRow("backslash") << makeSingleCharEscape('\\') << "\\";
+ QTest::addRow("slash") << makeSingleCharEscape('/') << "/";
+ QTest::addRow("backspace") << makeSingleCharEscape('b') << "\b";
+ QTest::addRow("form-feed") << makeSingleCharEscape('f') << "\f";
+ QTest::addRow("newline") << makeSingleCharEscape('n') << "\n";
+ QTest::addRow("carriage-return") << makeSingleCharEscape('r') << "\r";
+ QTest::addRow("tab") << makeSingleCharEscape('t') << "\t";
+
+ // we're not going to exhaustively test all Unicode possibilities
+ for (char16_t c = 0; c < 0x21; ++c)
+ addUnicodeRow(c);
+ addUnicodeRow(u'\u007f');
+ addUnicodeRow(u'\u0080');
+ addUnicodeRow(u'\u00ff');
+ addUnicodeRow(u'\u0100');
+ addUnicodeRow(char16_t(0xd800));
+ addUnicodeRow(char16_t(0xdc00));
+ addUnicodeRow(u'\ufffe');
+ addUnicodeRow(u'\uffff');
+ addUnicodeRow(U'\U00010000');
+ addUnicodeRow(U'\U00100000');
+ addUnicodeRow(U'\U0010ffff');
+
+ QTest::addRow("mojibake-utf8") << QByteArrayLiteral(R"(["A\u00e4\u00C4"])")
+ << QStringLiteral(u"A\u00e4\u00C4");
+
+ // characters for which, preceded by backslash, it is a valid (recognized)
+ // escape sequence (should match the above list)
+ static const char validEscapes[] = "\"\\/bfnrtu";
+ for (int i = 0; i <= 0xff; ++i) {
+ if (i && strchr(validEscapes, i))
+ continue;
+ QTest::addRow("invalid-uchar-0x%02x", i) << makeSingleCharEscape(i) << QString(char16_t(i));
+ }
+}
+
+void tst_QtJson::parseEscapes()
{
- const QByteArray json = "[ \"A\\u00e4\\u00C4\" ]";
+ QFETCH(QByteArray, json);
+ QFETCH(QString, result);
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonArray array = doc.array();
- QString result = QLatin1String("A");
- result += QChar(0xe4);
- result += QChar(0xc4);
-
QCOMPARE(array.first().toString(), result);
}
+void tst_QtJson::makeEscapes_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QByteArray>("result");
+
+ auto addUnicodeRow = [](char16_t c) {
+ char buf[32]; // more than enough
+ snprintf(buf, std::size(buf), "\\u%04x", c);
+ QTest::addRow("U+%04X", c) << QString(c) << QByteArray(buf);
+ };
+
+
+ QTest::addRow("quote") << "\"" << QByteArray(R"(\")");
+ QTest::addRow("backslash") << "\\" << QByteArray(R"(\\)");
+ //QTest::addRow("slash") << "/" << QByteArray(R"(\/)"); // does not get escaped
+ QTest::addRow("backspace") << "\b" << QByteArray(R"(\b)");
+ QTest::addRow("form-feed") << "\f" << QByteArray(R"(\f)");
+ QTest::addRow("newline") << "\n" << QByteArray(R"(\n)");
+ QTest::addRow("carriage-return") << "\r" << QByteArray(R"(\r)");
+ QTest::addRow("tab") << "\t" << QByteArray(R"(\t)");
+
+ // control characters other than the above
+ for (char16_t c = 0; c < 0x20; ++c) {
+ if (c && strchr("\b\f\n\r\t", c))
+ continue;
+ addUnicodeRow(c);
+ }
+ // unpaired surrogates
+ addUnicodeRow(char16_t(0xd800));
+ addUnicodeRow(char16_t(0xdc00));
+
+ QString improperlyPaired;
+ improperlyPaired.append(char16_t(0xdc00));
+ improperlyPaired.append(char16_t(0xd800));
+ QTest::addRow("inverted-surrogates") << improperlyPaired << QByteArray("\\udc00\\ud800");
+}
+
+void tst_QtJson::makeEscapes()
+{
+ QFETCH(QString, input);
+ QFETCH(QByteArray, result);
+
+ QJsonArray array = { input };
+ QByteArray json = QJsonDocument(array).toJson(QJsonDocument::Compact);
+
+ QVERIFY(json.startsWith("[\""));
+ result.prepend("[\"");
+ QVERIFY(json.endsWith("\"]"));
+ result.append("\"]");
+
+ QCOMPARE(json, result);
+}
+
void tst_QtJson::assignObjects()
{
const char *json =
@@ -2449,57 +2918,57 @@ void tst_QtJson::testDetachBug()
void tst_QtJson::valueEquals()
{
QCOMPARE(QJsonValue(), QJsonValue());
- QVERIFY(QJsonValue() != QJsonValue(QJsonValue::Undefined));
- QVERIFY(QJsonValue() != QJsonValue(true));
- QVERIFY(QJsonValue() != QJsonValue(1.));
- QVERIFY(QJsonValue() != QJsonValue(QJsonArray()));
- QVERIFY(QJsonValue() != QJsonValue(QJsonObject()));
+ QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonValue::Undefined), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(true), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(1.), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonArray()), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonObject()), false);
QCOMPARE(QJsonValue(true), QJsonValue(true));
- QVERIFY(QJsonValue(true) != QJsonValue(false));
- QVERIFY(QJsonValue(true) != QJsonValue(QJsonValue::Undefined));
- QVERIFY(QJsonValue(true) != QJsonValue());
- QVERIFY(QJsonValue(true) != QJsonValue(1.));
- QVERIFY(QJsonValue(true) != QJsonValue(QJsonArray()));
- QVERIFY(QJsonValue(true) != QJsonValue(QJsonObject()));
+ QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(false), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonValue::Undefined), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(1.), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonArray()), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonObject()), false);
QCOMPARE(QJsonValue(1), QJsonValue(1));
- QVERIFY(QJsonValue(1) != QJsonValue(2));
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(2), false);
QCOMPARE(QJsonValue(1), QJsonValue(1.));
- QVERIFY(QJsonValue(1) != QJsonValue(1.1));
- QVERIFY(QJsonValue(1) != QJsonValue(QJsonValue::Undefined));
- QVERIFY(QJsonValue(1) != QJsonValue());
- QVERIFY(QJsonValue(1) != QJsonValue(true));
- QVERIFY(QJsonValue(1) != QJsonValue(QJsonArray()));
- QVERIFY(QJsonValue(1) != QJsonValue(QJsonObject()));
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(1.1), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonValue::Undefined), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(true), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonArray()), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonObject()), false);
QCOMPARE(QJsonValue(1.), QJsonValue(1.));
- QVERIFY(QJsonValue(1.) != QJsonValue(2.));
- QVERIFY(QJsonValue(1.) != QJsonValue(QJsonValue::Undefined));
- QVERIFY(QJsonValue(1.) != QJsonValue());
- QVERIFY(QJsonValue(1.) != QJsonValue(true));
- QVERIFY(QJsonValue(1.) != QJsonValue(QJsonArray()));
- QVERIFY(QJsonValue(1.) != QJsonValue(QJsonObject()));
+ QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(2.), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonValue::Undefined), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(true), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonArray()), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonObject()), false);
QCOMPARE(QJsonValue(QJsonArray()), QJsonValue(QJsonArray()));
QJsonArray nonEmptyArray;
nonEmptyArray.append(true);
- QVERIFY(QJsonValue(QJsonArray()) != nonEmptyArray);
- QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonValue::Undefined));
- QVERIFY(QJsonValue(QJsonArray()) != QJsonValue());
- QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(true));
- QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(1.));
- QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonObject()));
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), nonEmptyArray, false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(QJsonValue::Undefined), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(true), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(1.), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(QJsonObject()), false);
QCOMPARE(QJsonValue(QJsonObject()), QJsonValue(QJsonObject()));
QJsonObject nonEmptyObject;
nonEmptyObject.insert("Key", true);
- QVERIFY(QJsonValue(QJsonObject()) != nonEmptyObject);
- QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonValue::Undefined));
- QVERIFY(QJsonValue(QJsonObject()) != QJsonValue());
- QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(true));
- QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(1.));
- QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonArray()));
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), nonEmptyObject, false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(QJsonValue::Undefined), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(true), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(1.), false);
+ QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(QJsonArray()), false);
QCOMPARE(QJsonValue("foo"), QJsonValue(QLatin1String("foo")));
QCOMPARE(QJsonValue("foo"), QJsonValue(QString("foo")));
@@ -2575,6 +3044,12 @@ void tst_QtJson::objectEquals()
QCOMPARE(QJsonValue(left) != QJsonValue(right), !result);
QCOMPARE(QJsonValue(right) == QJsonValue(left), result);
QCOMPARE(QJsonValue(right) != QJsonValue(left), !result);
+
+ // The same, but from a QJsonDocument perspective
+ QCOMPARE(QJsonDocument(left) == QJsonDocument(right), result);
+ QCOMPARE(QJsonDocument(left) != QJsonDocument(right), !result);
+ QCOMPARE(QJsonDocument(right) == QJsonDocument(left), result);
+ QCOMPARE(QJsonDocument(right) != QJsonDocument(left), !result);
}
void tst_QtJson::arrayEquals_data()
@@ -2628,12 +3103,65 @@ void tst_QtJson::arrayEquals()
QCOMPARE(QJsonValue(left) != QJsonValue(right), !result);
QCOMPARE(QJsonValue(right) == QJsonValue(left), result);
QCOMPARE(QJsonValue(right) != QJsonValue(left), !result);
+
+ // The same but from QJsonDocument perspective
+ QCOMPARE(QJsonDocument(left) == QJsonDocument(right), result);
+ QCOMPARE(QJsonDocument(left) != QJsonDocument(right), !result);
+ QCOMPARE(QJsonDocument(right) == QJsonDocument(left), result);
+ QCOMPARE(QJsonDocument(right) != QJsonDocument(left), !result);
+}
+
+void tst_QtJson::documentEquals_data()
+{
+ QTest::addColumn<QJsonDocument>("left");
+ QTest::addColumn<QJsonDocument>("right");
+ QTest::addColumn<bool>("result");
+
+ QTest::newRow("two defaults") << QJsonDocument() << QJsonDocument() << true;
+
+ QJsonDocument emptyobj(QJsonObject{});
+ QJsonDocument emptyarr(QJsonArray{});
+ QTest::newRow("emptyarray vs default") << emptyarr << QJsonDocument() << false;
+ QTest::newRow("emptyobject vs default") << emptyobj << QJsonDocument() << false;
+ QTest::newRow("emptyarray vs emptyobject") << emptyarr << emptyobj << false;
+
+ QJsonDocument array1(QJsonArray{1});
+ QJsonDocument array2(QJsonArray{2});
+ QTest::newRow("emptyarray vs emptyarray") << emptyarr << emptyarr << true;
+ QTest::newRow("emptyarray vs array") << emptyarr << array1 << false;
+ QTest::newRow("array vs array") << array1 << array1 << true;
+ QTest::newRow("array vs otherarray") << array1 << array2 << false;
+
+ QJsonDocument object1(QJsonObject{{"hello", "world"}});
+ QJsonDocument object2(QJsonObject{{"hello", 2}});
+ QTest::newRow("emptyobject vs emptyobject") << emptyobj << emptyobj << true;
+ QTest::newRow("emptyobject vs object") << emptyobj << object1 << false;
+ QTest::newRow("object vs object") << object1 << object1 << true;
+ QTest::newRow("object vs otherobject") << object1 << object2 << false;
+
+ QTest::newRow("object vs array") << array1 << object1 << false;
+}
+
+void tst_QtJson::documentEquals()
+{
+ QFETCH(QJsonDocument, left);
+ QFETCH(QJsonDocument, right);
+ QFETCH(bool, result);
+
+ QCOMPARE(left == right, result);
+ QCOMPARE(right == left, result);
+
+ // invariants checks
+ QCOMPARE(left, left);
+ QCOMPARE(right, right);
+ QCOMPARE(left != right, !result);
+ QCOMPARE(right != left, !result);
}
void tst_QtJson::bom()
{
QFile file(testDataDir + "/bom.json");
- file.open(QFile::ReadOnly);
+ QVERIFY(file.open(QFile::ReadOnly));
QByteArray json = file.readAll();
// Import json document into a QJsonDocument
@@ -2954,7 +3482,7 @@ void tst_QtJson::documentFromVariant()
// As JSON arrays they should be equal.
QCOMPARE(da1.array(), da2.array());
-
+ QT_TEST_EQUALITY_OPS(da1, da2, true);
QMap <QString, QVariant> map;
map["key"] = string;
@@ -2970,6 +3498,7 @@ void tst_QtJson::documentFromVariant()
// As JSON objects they should be equal.
QCOMPARE(do1.object(), do2.object());
+ QT_TEST_EQUALITY_OPS(do1, do2, true);
}
void tst_QtJson::parseErrorOffset_data()
@@ -3068,6 +3597,7 @@ void tst_QtJson::streamSerializationQJsonDocument()
QDataStream load(buffer);
load >> output;
QCOMPARE(output, document);
+ QT_TEST_EQUALITY_OPS(output, document, true);
}
void tst_QtJson::streamSerializationQJsonArray_data()
@@ -3121,8 +3651,8 @@ void tst_QtJson::streamSerializationQJsonValue_data()
QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}};
QTest::newRow("object") << QJsonValue{QJsonObject{{"foo", 665}, {"bar", 666}}};
// test json escape sequence
- QTest::newRow("array with 0xD800") << QJsonValue(QJsonArray{QString(0xD800)});
- QTest::newRow("array with 0xDF06,0xD834") << QJsonValue(QJsonArray{QString(0xDF06).append(0xD834)});
+ QTest::newRow("array with 0xD800") << QJsonValue(QJsonArray{QString(QChar(0xD800))});
+ QTest::newRow("array with 0xDF06,0xD834") << QJsonValue(QJsonArray{QString(QChar(0xDF06)).append(QChar(0xD834))});
}
void tst_QtJson::streamSerializationQJsonValue()
@@ -3215,8 +3745,8 @@ void tst_QtJson::escapeSurrogateCodePoints_data()
{
QTest::addColumn<QString>("str");
QTest::addColumn<QByteArray>("escStr");
- QTest::newRow("0xD800") << QString(0xD800) << QByteArray("\\ud800");
- QTest::newRow("0xDF06,0xD834") << QString(0xDF06).append(0xD834) << QByteArray("\\udf06\\ud834");
+ QTest::newRow("0xD800") << QString(QChar(0xD800)) << QByteArray("\\ud800");
+ QTest::newRow("0xDF06,0xD834") << QString(QChar(0xDF06)).append(QChar(0xD834)) << QByteArray("\\udf06\\ud834");
}
void tst_QtJson::escapeSurrogateCodePoints()
@@ -3232,5 +3762,236 @@ void tst_QtJson::escapeSurrogateCodePoints()
QVERIFY(buffer.contains(escStr));
}
+void tst_QtJson::fromToVariantConversions_data()
+{
+ QTest::addColumn<QVariant>("variant");
+ QTest::addColumn<QJsonValue>("json");
+ QTest::addColumn<QVariant>("jsonToVariant");
+
+ QByteArray utf8("\xC4\x90\xC4\x81\xC5\xA3\xC3\xA2"); // Đāţâ
+ QDateTime dt = QDateTime::currentDateTimeUtc();
+ QUuid uuid = QUuid::createUuid();
+
+ constexpr qlonglong maxInt = std::numeric_limits<qlonglong>::max();
+ constexpr qlonglong minInt = std::numeric_limits<qlonglong>::min();
+ constexpr double maxDouble = std::numeric_limits<double>::max();
+ constexpr double minDouble = std::numeric_limits<double>::min();
+
+ QTest::newRow("default") << QVariant() << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+ QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+ QTest::newRow("bool") << QVariant(true) << QJsonValue(true) << QVariant(true);
+ QTest::newRow("int pos") << QVariant(123) << QJsonValue(123) << QVariant(qlonglong(123));
+ QTest::newRow("int neg") << QVariant(-123) << QJsonValue(-123) << QVariant(qlonglong(-123));
+ QTest::newRow("int big pos") << QVariant((1ll << 55) +1) << QJsonValue((1ll << 55) + 1)
+ << QVariant(qlonglong((1ll << 55) + 1));
+ QTest::newRow("int big neg") << QVariant(-(1ll << 55) + 1) << QJsonValue(-(1ll << 55) + 1)
+ << QVariant(qlonglong(-(1ll << 55) + 1));
+ QTest::newRow("int max") << QVariant(maxInt) << QJsonValue(maxInt) << QVariant(maxInt);
+ QTest::newRow("int min") << QVariant(minInt) << QJsonValue(minInt) << QVariant(minInt);
+ QTest::newRow("double pos") << QVariant(123.) << QJsonValue(123.) << QVariant(qlonglong(123.));
+ QTest::newRow("double neg") << QVariant(-123.) << QJsonValue(-123.)
+ << QVariant(qlonglong(-123.));
+ QTest::newRow("double big") << QVariant(maxDouble - 1000) << QJsonValue(maxDouble - 1000)
+ << QVariant(maxDouble - 1000);
+ QTest::newRow("double max") << QVariant(maxDouble) << QJsonValue(maxDouble)
+ << QVariant(maxDouble);
+ QTest::newRow("double min") << QVariant(minDouble) << QJsonValue(minDouble)
+ << QVariant(minDouble);
+ QTest::newRow("double big neg") << QVariant(1000 - maxDouble) << QJsonValue(1000 - maxDouble)
+ << QVariant(1000 - maxDouble);
+ QTest::newRow("double max neg") << QVariant(-maxDouble) << QJsonValue(-maxDouble)
+ << QVariant(-maxDouble);
+ QTest::newRow("double min neg") << QVariant(-minDouble) << QJsonValue(-minDouble)
+ << QVariant(-minDouble);
+
+ QTest::newRow("string null") << QVariant(QString()) << QJsonValue(QString())
+ << QVariant(QString());
+ QTest::newRow("string empty") << QVariant(QString("")) << QJsonValue(QString(""))
+ << QVariant(QString(""));
+ QTest::newRow("string ascii") << QVariant(QString("Data")) << QJsonValue(QString("Data"))
+ << QVariant(QString("Data"));
+ QTest::newRow("string utf8") << QVariant(QString(utf8)) << QJsonValue(QString(utf8))
+ << QVariant(QString(utf8));
+
+ QTest::newRow("bytearray null") << QVariant(QByteArray()) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+ QTest::newRow("bytearray empty") << QVariant(QByteArray()) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+ QTest::newRow("bytearray ascii") << QVariant(QByteArray("Data")) << QJsonValue(QString("Data"))
+ << QVariant(QString("Data"));
+ QTest::newRow("bytearray utf8") << QVariant(utf8) << QJsonValue(QString(utf8))
+ << QVariant(QString(utf8));
+
+ QTest::newRow("datetime") << QVariant(dt) << QJsonValue(dt.toString(Qt::ISODateWithMs))
+ << QVariant(dt.toString(Qt::ISODateWithMs));
+ QTest::newRow("url") << QVariant(QUrl("http://example.com/{q}"))
+ << QJsonValue("http://example.com/%7Bq%7D")
+ << QVariant(QString("http://example.com/%7Bq%7D"));
+ QTest::newRow("uuid") << QVariant(QUuid(uuid))
+ << QJsonValue(uuid.toString(QUuid::WithoutBraces))
+ << QVariant(uuid.toString(QUuid::WithoutBraces));
+ QTest::newRow("regexp") << QVariant(QRegularExpression(".")) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+
+ QTest::newRow("inf") << QVariant(qInf()) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+ QTest::newRow("-inf") << QVariant(-qInf()) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+ QTest::newRow("NaN") << QVariant(qQNaN()) << QJsonValue(QJsonValue::Null)
+ << QVariant::fromValue(nullptr);
+
+ static_assert(std::numeric_limits<double>::digits <= 63,
+ "double is too big on this platform, this test would fail");
+ constexpr quint64 Threshold = Q_UINT64_C(1) << 63;
+ const qulonglong ulongValue = qulonglong(Threshold) + 1;
+ const double uLongToDouble = Threshold;
+ QTest::newRow("ulonglong") << QVariant(ulongValue) << QJsonValue(uLongToDouble)
+ << QVariant(uLongToDouble);
+}
+
+void tst_QtJson::fromToVariantConversions()
+{
+ QFETCH(QVariant, variant);
+ QFETCH(QJsonValue, json);
+ QFETCH(QVariant, jsonToVariant);
+
+ QVariant variantFromJson(json);
+ QVariant variantFromJsonArray(QJsonArray { json });
+ QVariant variantFromJsonObject(QVariantMap { { "foo", variant } });
+
+ QJsonObject object { QPair<QString, QJsonValue>("foo", json) };
+
+ // QJsonValue <> QVariant
+ {
+ QCOMPARE(QJsonValue::fromVariant(variant), json);
+
+ // test the same for QVariant from QJsonValue/QJsonArray/QJsonObject
+ QCOMPARE(QJsonValue::fromVariant(variantFromJson), json);
+ QCOMPARE(QJsonValue::fromVariant(variantFromJsonArray), QJsonArray { json });
+ QCOMPARE(QJsonValue::fromVariant(variantFromJsonObject), object);
+
+ // QJsonValue to variant
+ QCOMPARE(json.toVariant(), jsonToVariant);
+ QCOMPARE(json.toVariant().userType(), jsonToVariant.userType());
+
+ // variant to QJsonValue
+ QCOMPARE(QVariant(json).toJsonValue(), json);
+ }
+
+ // QJsonArray <> QVariantList
+ {
+ QCOMPARE(QJsonArray::fromVariantList(QVariantList { variant }), QJsonArray { json });
+
+ // test the same for QVariantList from QJsonValue/QJsonArray/QJsonObject
+ QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJson }),
+ QJsonArray { json });
+ QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJsonArray }),
+ QJsonArray {{ QJsonArray { json } }});
+ QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJsonObject }),
+ QJsonArray { object });
+
+ // QJsonArray to variant
+ QCOMPARE(QJsonArray { json }.toVariantList(), QVariantList { jsonToVariant });
+ // variant to QJsonArray
+ QCOMPARE(QVariant(QJsonArray { json }).toJsonArray(), QJsonArray { json });
+ }
+
+ // QJsonObject <> QVariantMap
+ {
+ QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "foo", variant } }), object);
+
+ // test the same for QVariantMap from QJsonValue/QJsonArray/QJsonObject
+ QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "foo", variantFromJson } }), object);
+
+ QJsonObject nestedArray { QPair<QString, QJsonArray>("bar", QJsonArray { json }) };
+ QJsonObject nestedObject { QPair<QString, QJsonObject>("bar", object) };
+ QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "bar", variantFromJsonArray } }),
+ nestedArray);
+ QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "bar", variantFromJsonObject } }),
+ nestedObject);
+
+ // QJsonObject to variant
+ QCOMPARE(object.toVariantMap(), QVariantMap({ { "foo", jsonToVariant } }));
+ // variant to QJsonObject
+ QCOMPARE(QVariant(object).toJsonObject(), object);
+ }
+}
+
+void tst_QtJson::testIteratorComparison()
+{
+ QJsonObject t = QJsonObject::fromVariantHash({
+ { QStringLiteral("a"), QVariant(12) },
+ { QStringLiteral("b"), QVariant(13) }
+ });
+
+ QVERIFY(t.begin() == t.begin());
+ QVERIFY(t.begin() <= t.begin());
+ QVERIFY(t.begin() >= t.begin());
+ QVERIFY(!(t.begin() != t.begin()));
+ QVERIFY(!(t.begin() < t.begin()));
+ QVERIFY(!(t.begin() > t.begin()));
+
+ QVERIFY(!(t.begin() == t.end()));
+ QVERIFY(t.begin() <= t.end());
+ QVERIFY(!(t.begin() >= t.end()));
+ QVERIFY(t.begin() != t.end());
+ QVERIFY(t.begin() < t.end());
+ QVERIFY(!(t.begin() > t.end()));
+
+ QVERIFY(!(t.end() == t.begin()));
+ QVERIFY(!(t.end() <= t.begin()));
+ QVERIFY(t.end() >= t.begin());
+ QVERIFY(t.end() != t.begin());
+ QVERIFY(!(t.end() < t.begin()));
+ QVERIFY(t.end() > t.begin());
+}
+
+void tst_QtJson::noLeakOnNameClash_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QByteArray>("result");
+ QTest::addRow("simple")
+ << QStringLiteral("simple.duplicates.json")
+ << QByteArray(R"({"": 0})");
+ QTest::addRow("test")
+ << QStringLiteral("test.duplicates.json")
+ << QByteArray(R"([
+ "JSON Test Pattern pass1", {"a": ["array with 1 element"]}, {}, [], -42, true,
+ false, null, {"a": "A key can be any string"}, 0.5, 98.6, 99.44, 1066, 10, 1,
+ 0.1, 1, 2, 2, "rosebud", {"a": "bar"}, {"a": {"a": 2000}}, {"a": {"a": 2000}},
+ {"a": {"a": 2000}}, {"a": {"a": 2000}}
+ ])");
+ QTest::addRow("test3")
+ << QStringLiteral("test3.duplicates.json")
+ << QByteArray(R"({"a": [{"a": "212 555-1234"}, {"a": "646 555-4567"}]})");
+}
+
+void tst_QtJson::noLeakOnNameClash()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QByteArray, result);
+
+ QFile file(testDataDir + u'/' + fileName);
+ QVERIFY(file.open(QFile::ReadOnly));
+ QByteArray testJson = file.readAll();
+ QVERIFY(!testJson.isEmpty());
+
+ QJsonParseError error;
+
+ // Retains the last one of each set of duplicate keys.
+ QJsonDocument doc = QJsonDocument::fromJson(testJson, &error);
+ QVERIFY2(!doc.isNull(), qPrintable(error.errorString()));
+ QJsonDocument expected = QJsonDocument::fromJson(result, &error);
+ QVERIFY2(!expected.isNull(), qPrintable(error.errorString()));
+
+ QCOMPARE(doc, expected);
+ QT_TEST_EQUALITY_OPS(doc, expected, true);
+
+ // It should not leak.
+ // In particular it should not forget to deref the container for the inner objects.
+}
+
QTEST_MAIN(tst_QtJson)
#include "tst_qtjson.moc"