diff options
Diffstat (limited to 'tests/auto/corelib/serialization/json/tst_qtjson.cpp')
-rw-r--r-- | tests/auto/corelib/serialization/json/tst_qtjson.cpp | 1061 |
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" |