summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/serialization/json
diff options
context:
space:
mode:
authorSona Kurazyan <sona.kurazyan@qt.io>2020-08-07 14:57:11 +0200
committerSona Kurazyan <sona.kurazyan@qt.io>2020-08-13 19:36:58 +0200
commit1df02b5f980b01a4e42f32061f1cba696b6a22e9 (patch)
treed4962719806e06bba93831d5ee08d12124ac5bcc /tests/auto/corelib/serialization/json
parent5b76414b43ee0ffabf292b4c6535f15e42613f1b (diff)
Fix conversions to JSON from QVariant
After reimplementing Qt JSON support on top of CBOR, there were unintended behavior changes when converting QVariant{, List, Map} to QJson{Value, Array, List} due to reusing the code for converting QVariant* types to CBOR types, and from CBOR types to corresponding JSON types. In particular, conversions from QVariant containing QByteArray to JSON has been affected: according to RFC 7049, when converting from CBOR to JSON, raw byte array data must be encoded in base64url when converting to a JSON string. As a result QVariant* types containing QByteArray data ended up base64url-encoded when converted to JSON, instead of converting using QString::fromUtf8() as before. There were also differences when converting QRegularExpression. Reverted the behavior changes by adding a flag to internal methods for converting CBOR to JSON, to distinguish whether the conversion is done from QVariant* or CBOR types. These methods now will fall back to the old behavior, if the conversion is done using QJson*::fromVariant*(). Additionally fixed QJsonValue::fromVariant conversion for NaN and infinities: they should always convert to QJsonValue::Null. This works correctly when converting from variant to QJsonArray/QJsonObject, but has been wrong for QJsonValue. Added more tests to verify the expected behavior. [ChangeLog][Important Behavior Changes] Restored pre-5.15.0 behavior when converting from QVariant* to QJson* types. Unforeseen consequences of changes in 5.15.0 caused QByteArray data to be base64url-encoded; the handling of QRegularExpression was also unintentionally changed. These conversions are now reverted to the prior behavior. Additionally fixed QJsonValue::fromVariant conversions for NaN and infinities: they should always convert to QJsonValue::Null. Fixes: QTBUG-84739 Change-Id: Iaee667d00e5363906eedbb67948b7b39c9d0bc78 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'tests/auto/corelib/serialization/json')
-rw-r--r--tests/auto/corelib/serialization/json/tst_qtjson.cpp153
1 files changed, 153 insertions, 0 deletions
diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
index b11d8c996d..ac1ef76f8c 100644
--- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp
+++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
@@ -163,6 +163,10 @@ private Q_SLOTS:
void streamVariantSerialization();
void escapeSurrogateCodePoints_data();
void escapeSurrogateCodePoints();
+
+ void fromToVariantConversions_data();
+ void fromToVariantConversions();
+
private:
QString testDataDir;
};
@@ -3301,5 +3305,154 @@ 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);
+}
+
+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);
+ }
+}
+
QTEST_MAIN(tst_QtJson)
#include "tst_qtjson.moc"