Files
qt-creator/tests/auto/json/tst_json.cpp

2532 lines
81 KiB
C++
Raw Normal View History

/****************************************************************************
**
** 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:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <json.h>
#include <QFileInfo>
#include <QTest>
#include <QCryptographicHash>
#include <limits>
#define INVALID_UNICODE "\xCE\xBA\xE1"
#define UNICODE_NON_CHARACTER "\xEF\xBF\xBF"
#define UNICODE_DJE "\320\202" // Character from the Serbian Cyrillic alphabet
using namespace Json;
Q_DECLARE_METATYPE(Json::JsonArray)
Q_DECLARE_METATYPE(Json::JsonObject)
bool contains(const JsonObject::Keys &keys, const std::string &key)
{
return std::find(keys.begin(), keys.end(), key) != keys.end();
}
class tst_Json: public QObject
{
Q_OBJECT
public:
tst_Json(QObject *parent = 0);
private slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
void testValueSimple();
void testNumbers();
void testNumbers_2();
void testNumbers_3();
void testObjectSimple();
void testObjectSmallKeys();
void testArraySimple();
void testValueObject();
void testValueArray();
void testObjectNested();
void testArrayNested();
void testArrayNestedEmpty();
void testObjectNestedEmpty();
void testValueRef();
void testObjectIteration();
void testArrayIteration();
void testObjectFind();
void testDocument();
void nullValues();
void nullArrays();
void nullObject();
void constNullObject();
void keySorting();
void undefinedValues();
void toJson();
void toJsonSillyNumericValues();
void toJsonLargeNumericValues();
void fromJson();
void fromJsonErrors();
void fromBinary();
void toAndFromBinary_data();
void toAndFromBinary();
void parseNumbers();
void parseStrings();
void parseDuplicateKeys();
void testParser();
void compactArray();
void compactObject();
void validation();
void assignToDocument();
void testDuplicateKeys();
void testCompaction();
void testCompactionError();
void parseUnicodeEscapes();
void assignObjects();
void assignArrays();
void testTrailingComma();
void testDetachBug();
void testJsonValueRefDefault();
void valueEquals();
void objectEquals_data();
void objectEquals();
void arrayEquals_data();
void arrayEquals();
void bom();
void nesting();
void longStrings();
void arrayInitializerList();
void objectInitializerList();
void unicodeKeys();
void garbageAtEnd();
void removeNonLatinKey();
private:
QString testDataDir;
};
tst_Json::tst_Json(QObject *parent) : QObject(parent)
{
}
void tst_Json::initTestCase()
{
testDataDir = QFileInfo(QFINDTESTDATA("test.json")).absolutePath();
if (testDataDir.isEmpty())
testDataDir = QCoreApplication::applicationDirPath();
}
void tst_Json::cleanupTestCase()
{
}
void tst_Json::init()
{
}
void tst_Json::cleanup()
{
}
void tst_Json::testValueSimple()
{
JsonObject object;
object.insert("number", 999.);
JsonArray array;
for (int i = 0; i < 10; ++i)
array.append((double)i);
JsonValue value(true);
QCOMPARE(value.type(), JsonValue::Bool);
QCOMPARE(value.toDouble(), 0.);
QCOMPARE(value.toString(), std::string());
QCOMPARE(value.toBool(), true);
QCOMPARE(value.toObject(), JsonObject());
QCOMPARE(value.toArray(), JsonArray());
QCOMPARE(value.toDouble(99.), 99.);
QCOMPARE(value.toString("test"), std::string("test"));
QCOMPARE(value.toObject(object), object);
QCOMPARE(value.toArray(array), array);
value = 999.;
QCOMPARE(value.type(), JsonValue::Double);
QCOMPARE(value.toDouble(), 999.);
QCOMPARE(value.toString(), std::string());
QCOMPARE(value.toBool(), false);
QCOMPARE(value.toBool(true), true);
QCOMPARE(value.toObject(), JsonObject());
QCOMPARE(value.toArray(), JsonArray());
value = "test";
QCOMPARE(value.toDouble(), 0.);
QCOMPARE(value.toString(), std::string("test"));
QCOMPARE(value.toBool(), false);
QCOMPARE(value.toObject(), JsonObject());
QCOMPARE(value.toArray(), JsonArray());
}
void tst_Json::testNumbers()
{
{
int numbers[] = {
0,
-1,
1,
(1<<26),
(1<<27),
(1<<28),
-(1<<26),
-(1<<27),
-(1<<28),
(1<<26) - 1,
(1<<27) - 1,
(1<<28) - 1,
-((1<<26) - 1),
-((1<<27) - 1),
-((1<<28) - 1)
};
int n = sizeof(numbers)/sizeof(int);
JsonArray array;
for (int i = 0; i < n; ++i)
array.append((double)numbers[i]);
std::string serialized = JsonDocument(array).toJson();
JsonDocument json = JsonDocument::fromJson(serialized);
JsonArray array2 = json.array();
QCOMPARE(array.size(), array2.size());
for (int i = 0; i < array.size(); ++i) {
QCOMPARE(array.at(i).type(), JsonValue::Double);
QCOMPARE(array.at(i).toDouble(), (double)numbers[i]);
QCOMPARE(array2.at(i).type(), JsonValue::Double);
QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]);
}
}
{
int64_t numbers[] = {
0,
-1,
1,
(1ll<<54),
(1ll<<55),
(1ll<<56),
-(1ll<<54),
-(1ll<<55),
-(1ll<<56),
(1ll<<54) - 1,
(1ll<<55) - 1,
(1ll<<56) - 1,
-((1ll<<54) - 1),
-((1ll<<55) - 1),
-((1ll<<56) - 1)
};
int n = sizeof(numbers)/sizeof(int64_t);
JsonArray array;
for (int i = 0; i < n; ++i)
array.append((double)numbers[i]);
std::string serialized = JsonDocument(array).toJson();
JsonDocument json = JsonDocument::fromJson(serialized);
JsonArray array2 = json.array();
QCOMPARE(array.size(), array2.size());
for (int i = 0; i < array.size(); ++i) {
QCOMPARE(array.at(i).type(), JsonValue::Double);
QCOMPARE(array.at(i).toDouble(), (double)numbers[i]);
QCOMPARE(array2.at(i).type(), JsonValue::Double);
QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]);
}
}
{
double numbers[] = {
0,
-1,
1,
double(1ll<<54),
double(1ll<<55),
double(1ll<<56),
double(-(1ll<<54)),
double(-(1ll<<55)),
double(-(1ll<<56)),
double((1ll<<54) - 1),
double((1ll<<55) - 1),
double((1ll<<56) - 1),
double(-((1ll<<54) - 1)),
double(-((1ll<<55) - 1)),
double(-((1ll<<56) - 1)),
1.1,
0.1,
-0.1,
-1.1,
1e200,
-1e200
};
int n = sizeof(numbers)/sizeof(double);
JsonArray array;
for (int i = 0; i < n; ++i)
array.append(numbers[i]);
std::string serialized = JsonDocument(array).toJson();
JsonDocument json = JsonDocument::fromJson(serialized);
JsonArray array2 = json.array();
QCOMPARE(array.size(), array2.size());
for (int i = 0; i < array.size(); ++i) {
QCOMPARE(array.at(i).type(), JsonValue::Double);
QCOMPARE(array.at(i).toDouble(), numbers[i]);
QCOMPARE(array2.at(i).type(), JsonValue::Double);
QCOMPARE(array2.at(i).toDouble(), numbers[i]);
}
}
}
void tst_Json::testNumbers_2()
{
// test cases from TC39 test suite for ECMAScript
// http://hg.ecmascript.org/tests/test262/file/d067d2f0ca30/test/suite/ch08/8.5/8.5.1.js
// Fill an array with 2 to the power of (0 ... -1075)
double value = 1;
double floatValues[1076], floatValues_1[1076];
JsonObject jObject;
for (int power = 0; power <= 1075; power++) {
floatValues[power] = value;
jObject.insert(std::to_string(power), JsonValue(floatValues[power]));
// Use basic math operations for testing, which are required to support 'gradual underflow' rather
// than Math.pow etc..., which are defined as 'implementation dependent'.
value = value * 0.5;
}
JsonDocument jDocument1(jObject);
std::string ba(jDocument1.toJson());
JsonDocument jDocument2(JsonDocument::fromJson(ba));
for (int power = 0; power <= 1075; power++) {
floatValues_1[power] = jDocument2.object().value(std::to_string(power)).toDouble();
#ifdef Q_OS_QNX
if (power >= 970)
QEXPECT_FAIL("", "See QTBUG-37066", Abort);
#endif
QVERIFY2(floatValues[power] == floatValues_1[power],
QString::fromLatin1("floatValues[%1] != floatValues_1[%1]").arg(power).toLatin1());
}
// 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::fromLatin1("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::fromLatin1("2**- %1 should not be 0").arg(index).toLatin1());
QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2),
QString::fromLatin1("Value should be double adjacent value at index %1").arg(index).toLatin1());
}
}
void tst_Json::testNumbers_3()
{
// test case from QTBUG-31926
double d1 = 1.123451234512345;
double d2 = 1.123451234512346;
JsonObject jObject;
jObject.insert("d1", JsonValue(d1));
jObject.insert("d2", JsonValue(d2));
JsonDocument jDocument1(jObject);
std::string ba(jDocument1.toJson());
JsonDocument jDocument2(JsonDocument::fromJson(ba));
double d1_1(jDocument2.object().value("d1").toDouble());
double d2_1(jDocument2.object().value("d2").toDouble());
QVERIFY(d1_1 != d2_1);
}
void tst_Json::testObjectSimple()
{
JsonObject object;
object.insert("number", 999.);
QCOMPARE(object.value("number").type(), JsonValue::Double);
QCOMPARE(object.value("number").toDouble(), 999.);
object.insert("string", std::string("test"));
QCOMPARE(object.value("string").type(), JsonValue::String);
QCOMPARE(object.value("string").toString(), std::string("test"));
object.insert("boolean", true);
QCOMPARE(object.value("boolean").toBool(), true);
JsonObject::Keys keys = object.keys();
QVERIFY2(contains(keys, "number"), "key number not found");
QVERIFY2(contains(keys, "string"), "key string not found");
QVERIFY2(contains(keys, "boolean"), "key boolean not found");
// if we put a JsonValue into the JsonObject and retrieve
// it, it should be identical.
JsonValue value("foo");
object.insert("value", value);
QCOMPARE(object.value("value"), value);
int size = object.size();
object.remove("boolean");
QCOMPARE(object.size(), size - 1);
QVERIFY2(!object.contains("boolean"), "key boolean should have been removed");
JsonValue taken = object.take("value");
QCOMPARE(taken, value);
QVERIFY2(!object.contains("value"), "key value should have been removed");
std::string before = object.value("string").toString();
object.insert("string", std::string("foo"));
QVERIFY2(object.value("string").toString() != before, "value should have been updated");
size = object.size();
JsonObject subobject;
subobject.insert("number", 42);
subobject.insert("string", "foobar");
object.insert("subobject", subobject);
QCOMPARE(object.size(), size+1);
JsonValue subvalue = object.take("subobject");
QCOMPARE(object.size(), size);
QCOMPARE(subvalue.toObject(), subobject);
// make object detach by modifying it many times
for (int i = 0; i < 64; ++i)
object.insert("string", "bar");
QCOMPARE(object.size(), size);
QCOMPARE(subvalue.toObject(), subobject);
}
void tst_Json::testObjectSmallKeys()
{
JsonObject data1;
data1.insert("1", 123.);
QVERIFY(data1.contains("1"));
QCOMPARE(data1.value("1").toDouble(), (double)123);
data1.insert("12", 133.);
QCOMPARE(data1.value("12").toDouble(), (double)133);
QVERIFY(data1.contains("12"));
data1.insert("123", 323.);
QCOMPARE(data1.value("12").toDouble(), (double)133);
QVERIFY(data1.contains("123"));
QCOMPARE(data1.value("123").type(), JsonValue::Double);
QCOMPARE(data1.value("123").toDouble(), (double)323);
}
void tst_Json::testArraySimple()
{
JsonArray array;
array.append(999.);
array.append(std::string("test"));
array.append(true);
JsonValue val = array.at(0);
QCOMPARE(array.at(0).toDouble(), 999.);
QCOMPARE(array.at(1).toString(), std::string("test"));
QCOMPARE(array.at(2).toBool(), true);
QCOMPARE(array.size(), 3);
// if we put a JsonValue into the JsonArray and retrieve
// it, it should be identical.
JsonValue value("foo");
array.append(value);
QCOMPARE(array.at(3), value);
int size = array.size();
array.removeAt(2);
--size;
QCOMPARE(array.size(), size);
JsonValue taken = array.takeAt(0);
--size;
QCOMPARE(taken.toDouble(), 999.);
QCOMPARE(array.size(), size);
// check whether null values work
array.append(JsonValue());
++size;
QCOMPARE(array.size(), size);
QCOMPARE(array.last().type(), JsonValue::Null);
QCOMPARE(array.last(), JsonValue());
QCOMPARE(array.first().type(), JsonValue::String);
QCOMPARE(array.first(), JsonValue("test"));
array.prepend(false);
QCOMPARE(array.first().type(), JsonValue::Bool);
QCOMPARE(array.first(), JsonValue(false));
QCOMPARE(array.at(-1), JsonValue(JsonValue::Undefined));
QCOMPARE(array.at(array.size()), JsonValue(JsonValue::Undefined));
array.replace(0, -555.);
QCOMPARE(array.first().type(), JsonValue::Double);
QCOMPARE(array.first(), JsonValue(-555.));
QCOMPARE(array.at(1).type(), JsonValue::String);
QCOMPARE(array.at(1), JsonValue("test"));
}
void tst_Json::testValueObject()
{
JsonObject object;
object.insert("number", 999.);
object.insert("string", "test");
object.insert("boolean", true);
JsonValue value(object);
// if we don't modify the original JsonObject, toObject()
// on the JsonValue should return the same object (non-detached).
QCOMPARE(value.toObject(), object);
// if we modify the original object, it should detach
object.insert("test", JsonValue("test"));
QVERIFY2(value.toObject() != object, "object should have detached");
}
void tst_Json::testValueArray()
{
JsonArray array;
array.append(999.);
array.append("test");
array.append(true);
JsonValue 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);
// if we modify the original array, it should detach
array.append("test");
QVERIFY2(value.toArray() != array, "array should have detached");
}
void tst_Json::testObjectNested()
{
JsonObject inner, outer;
inner.insert("number", 999.);
outer.insert("nested", inner);
// if we don't modify the original JsonObject, value()
// should return the same object (non-detached).
JsonObject value = outer.value("nested").toObject();
QCOMPARE(value, inner);
QCOMPARE(value.value("number").toDouble(), 999.);
// if we modify the original object, it should detach and not
// affect the nested object
inner.insert("number", 555.);
value = outer.value("nested").toObject();
QVERIFY2(inner.value("number").toDouble() != value.value("number").toDouble(),
"object should have detached");
// array in object
JsonArray array;
array.append(123.);
array.append(456.);
outer.insert("array", array);
QCOMPARE(outer.value("array").toArray(), array);
QCOMPARE(outer.value("array").toArray().at(1).toDouble(), 456.);
// two deep objects
JsonObject twoDeep;
twoDeep.insert("boolean", true);
inner.insert("nested", twoDeep);
outer.insert("nested", inner);
QCOMPARE(outer.value("nested").toObject().value("nested").toObject(), twoDeep);
QCOMPARE(outer.value("nested").toObject().value("nested").toObject().value("boolean").toBool(),
true);
}
void tst_Json::testArrayNested()
{
JsonArray inner, outer;
inner.append(999.);
outer.append(inner);
// if we don't modify the original JsonArray, value()
// should return the same array (non-detached).
JsonArray value = outer.at(0).toArray();
QCOMPARE(value, inner);
QCOMPARE(value.at(0).toDouble(), 999.);
// if we modify the original array, it should detach and not
// affect the nested array
inner.append(555.);
value = outer.at(0).toArray();
QVERIFY2(inner.size() != value.size(), "array should have detached");
// objects in arrays
JsonObject object;
object.insert("boolean", true);
outer.append(object);
QCOMPARE(outer.last().toObject(), object);
QCOMPARE(outer.last().toObject().value("boolean").toBool(), true);
// two deep arrays
JsonArray twoDeep;
twoDeep.append(JsonValue("nested"));
inner.append(twoDeep);
outer.append(inner);
QCOMPARE(outer.last().toArray().last().toArray(), twoDeep);
QCOMPARE(outer.last().toArray().last().toArray().at(0).toString(), std::string("nested"));
}
void tst_Json::testArrayNestedEmpty()
{
JsonObject object;
JsonArray inner;
object.insert("inner", inner);
JsonValue val = object.value("inner");
JsonArray value = object.value("inner").toArray();
QCOMPARE(value.size(), 0);
QCOMPARE(value, inner);
QCOMPARE(value.size(), 0);
object.insert("count", 0.);
QCOMPARE(object.value("inner").toArray().size(), 0);
QVERIFY(object.value("inner").toArray().isEmpty());
JsonDocument(object).toBinaryData();
QCOMPARE(object.value("inner").toArray().size(), 0);
}
void tst_Json::testObjectNestedEmpty()
{
JsonObject object;
JsonObject inner;
JsonObject inner2;
object.insert("inner", inner);
object.insert("inner2", inner2);
JsonObject value = object.value("inner").toObject();
QCOMPARE(value.size(), 0);
QCOMPARE(value, inner);
QCOMPARE(value.size(), 0);
object.insert("count", 0.);
QCOMPARE(object.value("inner").toObject().size(), 0);
QCOMPARE(object.value("inner").type(), JsonValue::Object);
JsonDocument(object).toBinaryData();
QVERIFY(object.value("inner").toObject().isEmpty());
QVERIFY(object.value("inner2").toObject().isEmpty());
JsonDocument doc = JsonDocument::fromBinaryData(JsonDocument(object).toBinaryData());
QVERIFY(!doc.isNull());
JsonObject reconstituted(doc.object());
QCOMPARE(reconstituted.value("inner").toObject().size(), 0);
QCOMPARE(reconstituted.value("inner").type(), JsonValue::Object);
QCOMPARE(reconstituted.value("inner2").type(), JsonValue::Object);
}
void tst_Json::testValueRef()
{
JsonArray array;
array.append(1.);
array.append(2.);
array.append(3.);
array.append(4);
array.append(4.1);
array[1] = false;
QCOMPARE(array.size(), 5);
QCOMPARE(array.at(0).toDouble(), 1.);
QCOMPARE(array.at(2).toDouble(), 3.);
QCOMPARE(array.at(3).toInt(), 4);
QCOMPARE(array.at(4).toInt(), 0);
QCOMPARE(array.at(1).type(), JsonValue::Bool);
QCOMPARE(array.at(1).toBool(), false);
JsonObject object;
object["key"] = true;
QCOMPARE(object.size(), 1);
object.insert("null", JsonValue());
QCOMPARE(object.value("null"), JsonValue());
object["null"] = 100.;
QCOMPARE(object.value("null").type(), JsonValue::Double);
JsonValue val = object["null"];
QCOMPARE(val.toDouble(), 100.);
QCOMPARE(object.size(), 2);
array[1] = array[2] = object["key"] = 42;
QCOMPARE(array[1], array[2]);
QCOMPARE(array[2], object["key"]);
QCOMPARE(object.value("key"), JsonValue(42));
}
void tst_Json::testObjectIteration()
{
JsonObject object;
for (JsonObject::iterator it = object.begin(); it != object.end(); ++it)
QVERIFY(false);
const std::string property = "kkk";
object.insert(property, 11);
object.take(property);
for (JsonObject::iterator it = object.begin(); it != object.end(); ++it)
QVERIFY(false);
for (int i = 0; i < 10; ++i)
object[std::to_string(i)] = (double)i;
QCOMPARE(object.size(), 10);
QCOMPARE(object.begin()->toDouble(), object.constBegin()->toDouble());
for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) {
JsonValue value = it.value();
QCOMPARE((double)atoi(it.key().data()), value.toDouble());
}
{
JsonObject object2 = object;
QCOMPARE(object, object2);
JsonValue val = *object2.begin();
object2.erase(object2.begin());
QCOMPARE(object.size(), 10);
QCOMPARE(object2.size(), 9);
for (JsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) {
JsonValue value = it.value();
QVERIFY(it.value() != val);
QCOMPARE((double)atoi(it.key().data()), value.toDouble());
}
}
{
JsonObject object2 = object;
QCOMPARE(object, object2);
JsonObject::iterator it = object2.find(std::to_string(5));
object2.erase(it);
QCOMPARE(object.size(), 10);
QCOMPARE(object2.size(), 9);
}
{
JsonObject::iterator it = object.begin();
it += 5;
QCOMPARE(JsonValue(it.value()).toDouble(), 5.);
it -= 3;
QCOMPARE(JsonValue(it.value()).toDouble(), 2.);
JsonObject::iterator it2 = it + 5;
QCOMPARE(JsonValue(it2.value()).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(JsonValue(it2.value()).toDouble(), 1.);
}
{
JsonObject::const_iterator it = object.constBegin();
it += 5;
QCOMPARE(JsonValue(it.value()).toDouble(), 5.);
it -= 3;
QCOMPARE(JsonValue(it.value()).toDouble(), 2.);
JsonObject::const_iterator it2 = it + 5;
QCOMPARE(JsonValue(it2.value()).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(JsonValue(it2.value()).toDouble(), 1.);
}
JsonObject::iterator it = object.begin();
while (!object.isEmpty())
it = object.erase(it);
QCOMPARE(object.size() , 0);
QCOMPARE(it, object.end());
}
void tst_Json::testArrayIteration()
{
JsonArray array;
for (int i = 0; i < 10; ++i)
array.append(i);
QCOMPARE(array.size(), 10);
int i = 0;
for (JsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) {
JsonValue value = (*it);
QCOMPARE((double)i, value.toDouble());
}
QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble());
{
JsonArray array2 = array;
QCOMPARE(array, array2);
JsonValue val = *array2.begin();
array2.erase(array2.begin());
QCOMPARE(array.size(), 10);
QCOMPARE(array2.size(), 9);
i = 1;
for (JsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) {
JsonValue value = (*it);
QCOMPARE((double)i, value.toDouble());
}
}
{
JsonArray::iterator it = array.begin();
it += 5;
QCOMPARE(JsonValue((*it)).toDouble(), 5.);
it -= 3;
QCOMPARE(JsonValue((*it)).toDouble(), 2.);
JsonArray::iterator it2 = it + 5;
QCOMPARE(JsonValue(*it2).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(JsonValue(*it2).toDouble(), 1.);
}
{
JsonArray::const_iterator it = array.constBegin();
it += 5;
QCOMPARE(JsonValue((*it)).toDouble(), 5.);
it -= 3;
QCOMPARE(JsonValue((*it)).toDouble(), 2.);
JsonArray::const_iterator it2 = it + 5;
QCOMPARE(JsonValue(*it2).toDouble(), 7.);
it2 = it - 1;
QCOMPARE(JsonValue(*it2).toDouble(), 1.);
}
JsonArray::iterator it = array.begin();
while (!array.isEmpty())
it = array.erase(it);
QCOMPARE(array.size() , 0);
QCOMPARE(it, array.end());
}
void tst_Json::testObjectFind()
{
JsonObject object;
for (int i = 0; i < 10; ++i)
object[std::to_string(i)] = i;
QCOMPARE(object.size(), 10);
JsonObject::iterator it = object.find("1");
QCOMPARE((*it).toDouble(), 1.);
it = object.find("11");
QCOMPARE((*it).type(), JsonValue::Undefined);
QCOMPARE(it, object.end());
JsonObject::const_iterator cit = object.constFind("1");
QCOMPARE((*cit).toDouble(), 1.);
cit = object.constFind("11");
QCOMPARE((*it).type(), JsonValue::Undefined);
QCOMPARE(it, object.end());
}
void tst_Json::testDocument()
{
JsonDocument doc;
QCOMPARE(doc.isEmpty(), true);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), false);
JsonObject object;
doc.setObject(object);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
object.insert("Key", "Value");
doc.setObject(object);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QCOMPARE(doc.object(), object);
QCOMPARE(doc.array(), JsonArray());
doc = JsonDocument();
QCOMPARE(doc.isEmpty(), true);
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), false);
JsonArray array;
doc.setArray(array);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
array.append("Value");
doc.setArray(array);
QCOMPARE(doc.isEmpty(), false);
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
QCOMPARE(doc.array(), array);
QCOMPARE(doc.object(), JsonObject());
JsonObject outer;
outer.insert("outerKey", 22);
JsonObject inner;
inner.insert("innerKey", 42);
outer.insert("innter", inner);
JsonArray innerArray;
innerArray.append(23);
outer.insert("innterArray", innerArray);
JsonDocument doc2(outer.value("innter").toObject());
QVERIFY(doc2.object().contains("innerKey"));
QCOMPARE(doc2.object().value("innerKey"), JsonValue(42));
JsonDocument doc3;
doc3.setObject(outer.value("innter").toObject());
QCOMPARE(doc3.isArray(), false);
QCOMPARE(doc3.isObject(), true);
QVERIFY(doc3.object().contains("innerKey"));
QCOMPARE(doc3.object().value("innerKey"), JsonValue(42));
JsonDocument doc4(outer.value("innterArray").toArray());
QCOMPARE(doc4.isArray(), true);
QCOMPARE(doc4.isObject(), false);
QCOMPARE(doc4.array().size(), 1);
QCOMPARE(doc4.array().at(0), JsonValue(23));
JsonDocument doc5;
doc5.setArray(outer.value("innterArray").toArray());
QCOMPARE(doc5.isArray(), true);
QCOMPARE(doc5.isObject(), false);
QCOMPARE(doc5.array().size(), 1);
QCOMPARE(doc5.array().at(0), JsonValue(23));
}
void tst_Json::nullValues()
{
JsonArray array;
array.append(JsonValue());
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0), JsonValue());
JsonObject object;
object.insert("key", JsonValue());
QCOMPARE(object.contains("key"), true);
QCOMPARE(object.size(), 1);
QCOMPARE(object.value("key"), JsonValue());
}
void tst_Json::nullArrays()
{
JsonArray nullArray;
JsonArray nonNull;
nonNull.append("bar");
QCOMPARE(nullArray, JsonArray());
QVERIFY(nullArray != nonNull);
QVERIFY(nonNull != nullArray);
QCOMPARE(nullArray.size(), 0);
QCOMPARE(nullArray.takeAt(0), JsonValue(JsonValue::Undefined));
QCOMPARE(nullArray.first(), JsonValue(JsonValue::Undefined));
QCOMPARE(nullArray.last(), JsonValue(JsonValue::Undefined));
nullArray.removeAt(0);
nullArray.removeAt(-1);
nullArray.append("bar");
nullArray.removeAt(0);
QCOMPARE(nullArray.size(), 0);
QCOMPARE(nullArray.takeAt(0), JsonValue(JsonValue::Undefined));
QCOMPARE(nullArray.first(), JsonValue(JsonValue::Undefined));
QCOMPARE(nullArray.last(), JsonValue(JsonValue::Undefined));
nullArray.removeAt(0);
nullArray.removeAt(-1);
}
void tst_Json::nullObject()
{
JsonObject nullObject;
JsonObject nonNull;
nonNull.insert("foo", "bar");
QCOMPARE(nullObject, JsonObject());
QVERIFY(nullObject != nonNull);
QVERIFY(nonNull != nullObject);
QCOMPARE(nullObject.size(), 0);
QCOMPARE(nullObject.keys(), JsonObject::Keys());
nullObject.remove("foo");
QCOMPARE(nullObject, JsonObject());
QCOMPARE(nullObject.take("foo"), JsonValue(JsonValue::Undefined));
QCOMPARE(nullObject.contains("foo"), false);
nullObject.insert("foo", "bar");
nullObject.remove("foo");
QCOMPARE(nullObject.size(), 0);
QCOMPARE(nullObject.keys(), JsonObject::Keys());
nullObject.remove("foo");
QCOMPARE(nullObject, JsonObject());
QCOMPARE(nullObject.take("foo"), JsonValue(JsonValue::Undefined));
QCOMPARE(nullObject.contains("foo"), false);
}
void tst_Json::constNullObject()
{
const JsonObject nullObject;
JsonObject nonNull;
nonNull.insert("foo", "bar");
QCOMPARE(nullObject, JsonObject());
QVERIFY(nullObject != nonNull);
QVERIFY(nonNull != nullObject);
QCOMPARE(nullObject.size(), 0);
QCOMPARE(nullObject.keys(), JsonObject::Keys());
QCOMPARE(nullObject, JsonObject());
QCOMPARE(nullObject.contains("foo"), false);
QCOMPARE(nullObject["foo"], JsonValue(JsonValue::Undefined));
}
void tst_Json::keySorting()
{
const char *json = "{ \"B\": true, \"A\": false }";
JsonDocument doc = JsonDocument::fromJson(json);
QCOMPARE(doc.isObject(), true);
JsonObject o = doc.object();
QCOMPARE(o.size(), 2);
JsonObject::const_iterator it = o.constBegin();
QCOMPARE(it.key(), std::string("A"));
++it;
QCOMPARE(it.key(), std::string("B"));
JsonObject::Keys keys;
keys.push_back("A");
keys.push_back("B");
QCOMPARE(o.keys(), keys);
}
void tst_Json::undefinedValues()
{
JsonObject object;
object.insert("Key", JsonValue(JsonValue::Undefined));
QCOMPARE(object.size(), 0);
object.insert("Key", "Value");
QCOMPARE(object.size(), 1);
QCOMPARE(object.value("Key").type(), JsonValue::String);
QCOMPARE(object.value("foo").type(), JsonValue::Undefined);
object.insert("Key", JsonValue(JsonValue::Undefined));
QCOMPARE(object.size(), 0);
QCOMPARE(object.value("Key").type(), JsonValue::Undefined);
JsonArray array;
array.append(JsonValue(JsonValue::Undefined));
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0).type(), JsonValue::Null);
QCOMPARE(array.at(1).type(), JsonValue::Undefined);
QCOMPARE(array.at(-1).type(), JsonValue::Undefined);
}
void tst_Json::toJson()
{
// Test JsonDocument::Indented format
{
JsonObject object;
object.insert("\\Key\n", "Value");
object.insert("null", JsonValue());
JsonArray array;
array.append(true);
array.append(999.);
array.append("string");
array.append(JsonValue());
array.append("\\\a\n\r\b\tabcABC\"");
object.insert("Array", array);
std::string json = JsonDocument(object).toJson();
std::string expected =
"{\n"
" \"Array\": [\n"
" true,\n"
" 999,\n"
" \"string\",\n"
" null,\n"
" \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n"
" ],\n"
" \"\\\\Key\\n\": \"Value\",\n"
" \"null\": null\n"
"}\n";
QCOMPARE(json, expected);
JsonDocument doc;
doc.setObject(object);
json = doc.toJson();
QCOMPARE(json, expected);
doc.setArray(array);
json = doc.toJson();
expected =
"[\n"
" true,\n"
" 999,\n"
" \"string\",\n"
" null,\n"
" \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n"
"]\n";
QCOMPARE(json, expected);
}
// Test JsonDocument::Compact format
{
JsonObject object;
object.insert("\\Key\n", "Value");
object.insert("null", JsonValue());
JsonArray array;
array.append(true);
array.append(999.);
array.append("string");
array.append(JsonValue());
array.append("\\\a\n\r\b\tabcABC\"");
object.insert("Array", array);
std::string json = JsonDocument(object).toJson(JsonDocument::Compact);
std::string expected =
"{\"Array\":[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\":\"Value\",\"null\":null}";
QCOMPARE(json, expected);
JsonDocument doc;
doc.setObject(object);
json = doc.toJson(JsonDocument::Compact);
QCOMPARE(json, expected);
doc.setArray(array);
json = doc.toJson(JsonDocument::Compact);
expected = "[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"]";
QCOMPARE(json, expected);
}
}
void tst_Json::toJsonSillyNumericValues()
{
JsonObject object;
JsonArray array;
array.append(JsonValue(std::numeric_limits<double>::infinity())); // encode to: null
array.append(JsonValue(-std::numeric_limits<double>::infinity())); // encode to: null
array.append(JsonValue(std::numeric_limits<double>::quiet_NaN())); // encode to: null
object.insert("Array", array);
std::string json = JsonDocument(object).toJson();
std::string expected =
"{\n"
" \"Array\": [\n"
" null,\n"
" null,\n"
" null\n"
" ]\n"
"}\n";
QCOMPARE(json, expected);
JsonDocument doc;
doc.setObject(object);
json = doc.toJson();
QCOMPARE(json, expected);
}
void tst_Json::toJsonLargeNumericValues()
{
JsonObject object;
JsonArray array;
array.append(JsonValue(1.234567)); // actual precision bug in Qt 5.0.0
array.append(JsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE
array.append(JsonValue(5e-324)); // JS Number.MIN_VALUE
array.append(JsonValue(std::numeric_limits<double>::min()));
array.append(JsonValue(std::numeric_limits<double>::max()));
array.append(JsonValue(std::numeric_limits<double>::epsilon()));
array.append(JsonValue(std::numeric_limits<double>::denorm_min()));
array.append(JsonValue(0.0));
array.append(JsonValue(-std::numeric_limits<double>::min()));
array.append(JsonValue(-std::numeric_limits<double>::max()));
array.append(JsonValue(-std::numeric_limits<double>::epsilon()));
array.append(JsonValue(-std::numeric_limits<double>::denorm_min()));
array.append(JsonValue(-0.0));
array.append(JsonValue(int64_t(9007199254740992LL))); // JS Number max integer
array.append(JsonValue(int64_t(-9007199254740992LL))); // JS Number min integer
object.insert("Array", array);
std::string json = JsonDocument(object).toJson();
std::string expected =
"{\n"
" \"Array\": [\n"
" 1.234567,\n"
" 1.7976931348623157e+308,\n"
// ((4.9406564584124654e-324 == 5e-324) == true)
// I can only think JavaScript has a special formatter to
// emit this value for this IEEE754 bit pattern.
" 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"
" 0,\n"
" 9007199254740992,\n"
" -9007199254740992\n"
" ]\n"
"}\n";
QCOMPARE(json, expected);
JsonDocument doc;
doc.setObject(object);
json = doc.toJson();
QCOMPARE(json, expected);
}
void tst_Json::fromJson()
{
{
std::string json = "[\n true\n]\n";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0).type(), JsonValue::Bool);
QCOMPARE(array.at(0).toBool(), true);
QCOMPARE(doc.toJson(), json);
}
{
//regression test: test if unicode_control_characters are correctly decoded
std::string json = "[\n \"" UNICODE_NON_CHARACTER "\"\n]\n";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0).type(), JsonValue::String);
QCOMPARE(array.at(0).toString(), std::string(UNICODE_NON_CHARACTER));
QCOMPARE(doc.toJson(), json);
}
{
std::string json = "[]";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 0);
}
{
std::string json = "{}";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
JsonObject object = doc.object();
QCOMPARE(object.size(), 0);
}
{
std::string json = "{\n \"Key\": true\n}\n";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
JsonObject object = doc.object();
QCOMPARE(object.size(), 1);
QCOMPARE(object.value("Key"), JsonValue(true));
QCOMPARE(doc.toJson(), json);
}
{
std::string json = "[ null, true, false, \"Foo\", 1, [], {} ]";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 7);
QCOMPARE(array.at(0).type(), JsonValue::Null);
QCOMPARE(array.at(1).type(), JsonValue::Bool);
QCOMPARE(array.at(1).toBool(), true);
QCOMPARE(array.at(2).type(), JsonValue::Bool);
QCOMPARE(array.at(2).toBool(), false);
QCOMPARE(array.at(3).type(), JsonValue::String);
QCOMPARE(array.at(3).toString(), std::string("Foo"));
QCOMPARE(array.at(4).type(), JsonValue::Double);
QCOMPARE(array.at(4).toDouble(), 1.);
QCOMPARE(array.at(5).type(), JsonValue::Array);
QCOMPARE(array.at(5).toArray().size(), 0);
QCOMPARE(array.at(6).type(), JsonValue::Object);
QCOMPARE(array.at(6).toObject().size(), 0);
}
{
std::string json = "{ \"0\": null, \"1\": true, \"2\": false, \"3\": \"Foo\", \"4\": 1, \"5\": [], \"6\": {} }";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
JsonObject object = doc.object();
QCOMPARE(object.size(), 7);
QCOMPARE(object.value("0").type(), JsonValue::Null);
QCOMPARE(object.value("1").type(), JsonValue::Bool);
QCOMPARE(object.value("1").toBool(), true);
QCOMPARE(object.value("2").type(), JsonValue::Bool);
QCOMPARE(object.value("2").toBool(), false);
QCOMPARE(object.value("3").type(), JsonValue::String);
QCOMPARE(object.value("3").toString(), std::string("Foo"));
QCOMPARE(object.value("4").type(), JsonValue::Double);
QCOMPARE(object.value("4").toDouble(), 1.);
QCOMPARE(object.value("5").type(), JsonValue::Array);
QCOMPARE(object.value("5").toArray().size(), 0);
QCOMPARE(object.value("6").type(), JsonValue::Object);
QCOMPARE(object.value("6").toObject().size(), 0);
}
{
std::string compactJson = "{\"Array\": [true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\": \"Value\",\"null\": null}";
JsonDocument doc = JsonDocument::fromJson(compactJson);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
JsonObject object = doc.object();
QCOMPARE(object.size(), 3);
QCOMPARE(object.value("\\Key\n").isString(), true);
QCOMPARE(object.value("\\Key\n").toString(), std::string("Value"));
QCOMPARE(object.value("null").isNull(), true);
QCOMPARE(object.value("Array").isArray(), true);
JsonArray array = object.value("Array").toArray();
QCOMPARE(array.size(), 5);
QCOMPARE(array.at(0).isBool(), true);
QCOMPARE(array.at(0).toBool(), true);
QCOMPARE(array.at(1).isDouble(), true);
QCOMPARE(array.at(1).toDouble(), 999.);
QCOMPARE(array.at(2).isString(), true);
QCOMPARE(array.at(2).toString(), std::string("string"));
QCOMPARE(array.at(3).isNull(), true);
QCOMPARE(array.at(4).isString(), true);
QCOMPARE(array.at(4).toString(), std::string("\\\a\n\r\b\tabcABC\""));
}
}
void tst_Json::fromJsonErrors()
{
{
JsonParseError error;
std::string json = "{\n \n\n";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::UnterminatedObject);
QCOMPARE(error.offset, 8);
}
{
JsonParseError error;
std::string json = "{\n \"key\" 10\n";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::MissingNameSeparator);
QCOMPARE(error.offset, 13);
}
{
JsonParseError error;
std::string json = "[\n \n\n";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 8);
}
{
JsonParseError error;
std::string json = "[\n 1, true\n\n";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::UnterminatedArray);
QCOMPARE(error.offset, 14);
}
{
JsonParseError error;
std::string json = "[\n 1 true\n\n";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::MissingValueSeparator);
QCOMPARE(error.offset, 7);
}
{
JsonParseError error;
std::string json = "[\n nul";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
JsonParseError error;
std::string json = "[\n nulzz";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalValue);
QCOMPARE(error.offset, 10);
}
{
JsonParseError error;
std::string json = "[\n tru";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
JsonParseError error;
std::string json = "[\n trud]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalValue);
QCOMPARE(error.offset, 10);
}
{
JsonParseError error;
std::string json = "[\n fal";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalValue);
QCOMPARE(error.offset, 7);
}
{
JsonParseError error;
std::string json = "[\n falsd]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalValue);
QCOMPARE(error.offset, 11);
}
{
JsonParseError error;
std::string json = "[\n 11111";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::TerminationByNumber);
QCOMPARE(error.offset, 11);
}
{
JsonParseError error;
std::string json = "[\n -1E10000]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalNumber);
QCOMPARE(error.offset, 14);
}
{
/*
JsonParseError error;
std::string json = "[\n -1e-10000]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalNumber);
QCOMPARE(error.offset, 15);
*/
}
{
JsonParseError error;
std::string json = "[\n \"\\u12\"]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalEscapeSequence);
QCOMPARE(error.offset, 11);
}
{
// This is not caught by the new parser as we don't parse
// UTF-8 anymore, but pass it as opaque blob.
// JsonParseError error;
// std::string json = "[\n \"foo" INVALID_UNICODE "bar\"]";
// JsonDocument doc = JsonDocument::fromJson(json, &error);
// QVERIFY(doc.isEmpty());
// QCOMPARE(error.error, JsonParseError::IllegalUTF8String);
// QCOMPARE(error.offset, 12);
}
{
JsonParseError error;
std::string json = "[\n \"";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::UnterminatedString);
QCOMPARE(error.offset, 8);
}
{
JsonParseError error;
std::string json = "[\n \"c" UNICODE_DJE "a\\u12\"]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::IllegalEscapeSequence);
QCOMPARE(error.offset, 15);
}
{
// This is not caught by the new parser as we don't parse
// UTF-8 anymore, but pass it as opaque blob.
// JsonParseError error;
// std::string json = "[\n \"c" UNICODE_DJE "a" INVALID_UNICODE "bar\"]";
// JsonDocument doc = JsonDocument::fromJson(json, &error);
// QVERIFY(doc.isEmpty());
// QCOMPARE(error.error, JsonParseError::IllegalUTF8String);
// QCOMPARE(error.offset, 13);
}
{
JsonParseError error;
std::string json = "[\n \"c" UNICODE_DJE "a ]";
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isEmpty());
QCOMPARE(error.error, JsonParseError::UnterminatedString);
QCOMPARE(error.offset, 14);
}
}
void tst_Json::fromBinary()
{
QFile file(testDataDir + QLatin1String("/test.json"));
file.open(QFile::ReadOnly);
std::string testJson = file.readAll().data();
JsonDocument doc = JsonDocument::fromJson(testJson);
JsonDocument outdoc = JsonDocument::fromBinaryData(doc.toBinaryData());
QVERIFY(!outdoc.isNull());
QCOMPARE(doc, outdoc);
// // Can be used to re-create test.bjson:
// QFile b1file(testDataDir + QLatin1String("/test.bjson.x"));
// b1file.open(QFile::WriteOnly);
// std::string d = doc.toBinaryData();
// b1file.write(d.data(), d.size());
// b1file.close();
QFile bfile(testDataDir + QLatin1String("/test.bjson"));
bfile.open(QFile::ReadOnly);
std::string binary = bfile.readAll().toStdString();
JsonDocument bdoc = JsonDocument::fromBinaryData(binary);
QVERIFY(!bdoc.isNull());
QCOMPARE(doc, bdoc);
}
void tst_Json::toAndFromBinary_data()
{
QTest::addColumn<QString>("filename");
QTest::newRow("test.json") << QString(testDataDir + QLatin1String("/test.json"));
QTest::newRow("test2.json") << QString(testDataDir + QLatin1String("/test2.json"));
}
void tst_Json::toAndFromBinary()
{
QFETCH(QString, filename);
QFile file(filename);
QVERIFY(file.open(QFile::ReadOnly));
std::string data = file.readAll().data();
JsonDocument doc = JsonDocument::fromJson(data);
QVERIFY(!doc.isNull());
JsonDocument outdoc = JsonDocument::fromBinaryData(doc.toBinaryData());
QVERIFY(!outdoc.isNull());
QCOMPARE(doc, outdoc);
}
void tst_Json::parseNumbers()
{
{
// test number parsing
struct Numbers {
const char *str;
int n;
};
Numbers numbers [] = {
{"0", 0},
{"1", 1},
{"10", 10},
{"-1", -1},
{"100000", 100000},
{"-999", -999}
};
int size = sizeof(numbers)/sizeof(Numbers);
for (int i = 0; i < size; ++i) {
std::string json = "[ ";
json += numbers[i].str;
json += " ]";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 1);
JsonValue val = array.at(0);
QCOMPARE(val.type(), JsonValue::Double);
QCOMPARE(val.toDouble(), (double)numbers[i].n);
}
}
{
// test number parsing
struct Numbers {
const char *str;
double n;
};
Numbers numbers [] = {
{"0", 0},
{"1", 1},
{"10", 10},
{"-1", -1},
{"100000", 100000},
{"-999", -999},
{"1.1", 1.1},
{"1e10", 1e10},
{"-1.1", -1.1},
{"-1e10", -1e10},
{"-1E10", -1e10},
{"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},
{"-1.e+308", -1.e+308}
};
int size = sizeof(numbers)/sizeof(Numbers);
for (int i = 0; i < size; ++i) {
std::string json = "[ ";
json += numbers[i].str;
json += " ]";
JsonDocument doc = JsonDocument::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);
JsonArray array = doc.array();
QCOMPARE(array.size(), 1);
JsonValue val = array.at(0);
QCOMPARE(val.type(), JsonValue::Double);
QCOMPARE(val.toDouble(), numbers[i].n);
}
}
}
void tst_Json::parseStrings()
{
const char *strings [] =
{
"Foo",
"abc\\\"abc",
"abc\\\\abc",
"abc\\babc",
"abc\\fabc",
"abc\\nabc",
"abc\\rabc",
"abc\\tabc",
"abc\\u0019abc",
"abc" UNICODE_DJE "abc",
UNICODE_NON_CHARACTER
};
int size = sizeof(strings)/sizeof(const char *);
for (int i = 0; i < size; ++i) {
std::string json = "[\n \"";
json += strings[i];
json += "\"\n]\n";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 1);
JsonValue val = array.at(0);
QCOMPARE(val.type(), JsonValue::String);
QCOMPARE(doc.toJson(), json);
}
struct Pairs {
const char *in;
const char *out;
};
Pairs pairs [] = {
{"abc\\/abc", "abc/abc"},
{"abc\\u0402abc", "abc" UNICODE_DJE "abc"},
{"abc\\u0065abc", "abceabc"},
{"abc\\uFFFFabc", "abc" UNICODE_NON_CHARACTER "abc"}
};
size = sizeof(pairs)/sizeof(Pairs);
for (int i = 0; i < size; ++i) {
std::string json = "[\n \"";
json += pairs[i].in;
json += "\"\n]\n";
std::string out = "[\n \"";
out += pairs[i].out;
out += "\"\n]\n";
JsonDocument doc = JsonDocument::fromJson(json);
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), true);
QCOMPARE(doc.isObject(), false);
JsonArray array = doc.array();
QCOMPARE(array.size(), 1);
JsonValue val = array.at(0);
QCOMPARE(val.type(), JsonValue::String);
QCOMPARE(doc.toJson(), out);
}
}
void tst_Json::parseDuplicateKeys()
{
const char *json = "{ \"B\": true, \"A\": null, \"B\": false }";
JsonDocument doc = JsonDocument::fromJson(json);
QCOMPARE(doc.isObject(), true);
JsonObject o = doc.object();
QCOMPARE(o.size(), 2);
JsonObject::const_iterator it = o.constBegin();
QCOMPARE(it.key(), std::string("A"));
QCOMPARE(it.value(), JsonValue());
++it;
QCOMPARE(it.key(), std::string("B"));
QCOMPARE(it.value(), JsonValue(false));
}
void tst_Json::testParser()
{
QFile file(testDataDir + QLatin1String("/test.json"));
file.open(QFile::ReadOnly);
std::string testJson = file.readAll().data();
JsonDocument doc = JsonDocument::fromJson(testJson);
QVERIFY(!doc.isEmpty());
}
void tst_Json::compactArray()
{
JsonArray array;
array.append("First Entry");
array.append("Second Entry");
array.append("Third Entry");
JsonDocument doc(array);
auto s = doc.toBinaryData().size();
array.removeAt(1);
doc.setArray(array);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
std::string("[\n"
" \"First Entry\",\n"
" \"Third Entry\"\n"
"]\n"));
array.removeAt(0);
doc.setArray(array);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
std::string("[\n"
" \"Third Entry\"\n"
"]\n"));
array.removeAt(0);
doc.setArray(array);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
std::string("[\n"
"]\n"));
}
void tst_Json::compactObject()
{
JsonObject object;
object.insert("Key1", "First Entry");
object.insert("Key2", "Second Entry");
object.insert("Key3", "Third Entry");
JsonDocument doc(object);
auto s = doc.toBinaryData().size();
object.remove("Key2");
doc.setObject(object);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
std::string("{\n"
" \"Key1\": \"First Entry\",\n"
" \"Key3\": \"Third Entry\"\n"
"}\n"));
object.remove("Key1");
doc.setObject(object);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
std::string("{\n"
" \"Key3\": \"Third Entry\"\n"
"}\n"));
object.remove("Key3");
doc.setObject(object);
QVERIFY(s > doc.toBinaryData().size());
s = doc.toBinaryData().size();
QCOMPARE(doc.toJson(),
std::string("{\n"
"}\n"));
}
void tst_Json::validation()
{
// this basically tests that we don't crash on corrupt data
QFile file(testDataDir + QLatin1String("/test.json"));
QVERIFY(file.open(QFile::ReadOnly));
std::string testJson = file.readAll().data();
QVERIFY(!testJson.empty());
JsonDocument doc = JsonDocument::fromJson(testJson);
QVERIFY(!doc.isNull());
std::string binary = doc.toBinaryData();
// only test the first 1000 bytes. Testing the full file takes too long
for (int i = 0; i < 1000; ++i) {
std::string corrupted = binary;
corrupted[i] = char(0xff);
JsonDocument doc = JsonDocument::fromBinaryData(corrupted);
if (doc.isNull())
continue;
std::string json = doc.toJson();
}
QFile file2(testDataDir + QLatin1String("/test3.json"));
file2.open(QFile::ReadOnly);
testJson = file2.readAll().data();
QVERIFY(!testJson.empty());
doc = JsonDocument::fromJson(testJson);
QVERIFY(!doc.isNull());
binary = doc.toBinaryData();
for (size_t i = 0; i < binary.size(); ++i) {
std::string corrupted = binary;
corrupted[i] = char(0xff);
JsonDocument doc = JsonDocument::fromBinaryData(corrupted);
if (doc.isNull())
continue;
std::string json = doc.toJson();
corrupted = binary;
corrupted[i] = 0x00;
doc = JsonDocument::fromBinaryData(corrupted);
if (doc.isNull())
continue;
json = doc.toJson();
}
}
void tst_Json::assignToDocument()
{
{
const char *json = "{ \"inner\": { \"key\": true } }";
JsonDocument doc = JsonDocument::fromJson(json);
JsonObject o = doc.object();
JsonValue inner = o.value("inner");
JsonDocument innerDoc(inner.toObject());
QVERIFY(innerDoc != doc);
QCOMPARE(innerDoc.object(), inner.toObject());
}
{
const char *json = "[ [ true ] ]";
JsonDocument doc = JsonDocument::fromJson(json);
JsonArray a = doc.array();
JsonValue inner = a.at(0);
JsonDocument innerDoc(inner.toArray());
QVERIFY(innerDoc != doc);
QCOMPARE(innerDoc.array(), inner.toArray());
}
}
void tst_Json::testDuplicateKeys()
{
JsonObject obj;
obj.insert("foo", "bar");
obj.insert("foo", "zap");
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("foo").toString(), std::string("zap"));
}
void tst_Json::testCompaction()
{
// modify object enough times to trigger compactionCounter
// and make sure the data is still valid
JsonObject obj;
for (int i = 0; i < 33; ++i) {
obj.remove("foo");
obj.insert("foo", "bar");
}
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("foo").toString(), std::string("bar"));
JsonDocument doc = JsonDocument::fromBinaryData(JsonDocument(obj).toBinaryData());
QVERIFY(!doc.isNull());
QVERIFY(!doc.isEmpty());
QCOMPARE(doc.isArray(), false);
QCOMPARE(doc.isObject(), true);
QCOMPARE(doc.object(), obj);
}
void tst_Json::testCompactionError()
{
JsonObject schemaObject;
schemaObject.insert("_Type", "_SchemaType");
schemaObject.insert("name", "Address");
schemaObject.insert("schema", JsonObject());
{
JsonObject content(schemaObject);
JsonDocument doc(content);
QVERIFY(!doc.isNull());
QByteArray hash = QCryptographicHash::hash(doc.toBinaryData().data(), QCryptographicHash::Md5).toHex();
schemaObject.insert("_Version", hash.data());
}
JsonObject schema;
schema.insert("streetNumber", schema.value("number").toObject());
schemaObject.insert("schema", schema);
{
JsonObject content(schemaObject);
content.remove("_Uuid");
content.remove("_Version");
JsonDocument doc(content);
QVERIFY(!doc.isNull());
QByteArray hash = QCryptographicHash::hash(doc.toBinaryData().data(), QCryptographicHash::Md5).toHex();
schemaObject.insert("_Version", hash.data());
}
}
void tst_Json::parseUnicodeEscapes()
{
const std::string json = "[ \"A\\u00e4\\u00C4\" ]";
JsonDocument doc = JsonDocument::fromJson(json);
JsonArray array = doc.array();
QString result = QLatin1String("A");
result += QChar(0xe4);
result += QChar(0xc4);
std::string expected = result.toUtf8().data();
QCOMPARE(array.first().toString(), expected);
}
void tst_Json::assignObjects()
{
const char *json =
"[ { \"Key\": 1 }, { \"Key\": 2 } ]";
JsonDocument doc = JsonDocument::fromJson(json);
JsonArray array = doc.array();
JsonObject object = array.at(0).toObject();
QCOMPARE(object.value("Key").toDouble(), 1.);
object = array.at(1).toObject();
QCOMPARE(object.value("Key").toDouble(), 2.);
}
void tst_Json::assignArrays()
{
const char *json =
"[ [ 1 ], [ 2 ] ]";
JsonDocument doc = JsonDocument::fromJson(json);
JsonArray array = doc.array();
JsonArray inner = array.at(0).toArray() ;
QCOMPARE(inner.at(0).toDouble(), 1.);
inner= array.at(1).toArray();
QCOMPARE(inner.at(0).toDouble(), 2.);
}
void tst_Json::testTrailingComma()
{
const char *jsons[] = {"{ \"Key\": 1, }", "[ { \"Key\": 1 }, ]"};
for (unsigned i = 0; i < sizeof(jsons)/sizeof(jsons[0]); ++i) {
JsonParseError error;
JsonDocument doc = JsonDocument::fromJson(jsons[i], &error);
QCOMPARE(error.error, JsonParseError::MissingObject);
}
}
void tst_Json::testDetachBug()
{
JsonObject dynamic;
JsonObject embedded;
JsonObject local;
embedded.insert("Key1", "Value1");
embedded.insert("Key2", "Value2");
dynamic.insert("Bogus", "bogusValue");
dynamic.insert("embedded", embedded);
local = dynamic.value("embedded").toObject();
dynamic.remove("embedded");
QCOMPARE(local.keys().size(), size_t(2));
local.remove("Key1");
local.remove("Key2");
QCOMPARE(local.keys().size(), size_t(0));
local.insert("Key1", "anotherValue");
QCOMPARE(local.keys().size(), size_t(1));
}
void tst_Json::valueEquals()
{
QCOMPARE(JsonValue(), JsonValue());
QVERIFY(JsonValue() != JsonValue(JsonValue::Undefined));
QVERIFY(JsonValue() != JsonValue(true));
QVERIFY(JsonValue() != JsonValue(1.));
QVERIFY(JsonValue() != JsonValue(JsonArray()));
QVERIFY(JsonValue() != JsonValue(JsonObject()));
QCOMPARE(JsonValue(true), JsonValue(true));
QVERIFY(JsonValue(true) != JsonValue(false));
QVERIFY(JsonValue(true) != JsonValue(JsonValue::Undefined));
QVERIFY(JsonValue(true) != JsonValue());
QVERIFY(JsonValue(true) != JsonValue(1.));
QVERIFY(JsonValue(true) != JsonValue(JsonArray()));
QVERIFY(JsonValue(true) != JsonValue(JsonObject()));
QCOMPARE(JsonValue(1), JsonValue(1));
QVERIFY(JsonValue(1) != JsonValue(2));
QCOMPARE(JsonValue(1), JsonValue(1.));
QVERIFY(JsonValue(1) != JsonValue(1.1));
QVERIFY(JsonValue(1) != JsonValue(JsonValue::Undefined));
QVERIFY(JsonValue(1) != JsonValue());
QVERIFY(JsonValue(1) != JsonValue(true));
QVERIFY(JsonValue(1) != JsonValue(JsonArray()));
QVERIFY(JsonValue(1) != JsonValue(JsonObject()));
QCOMPARE(JsonValue(1.), JsonValue(1.));
QVERIFY(JsonValue(1.) != JsonValue(2.));
QVERIFY(JsonValue(1.) != JsonValue(JsonValue::Undefined));
QVERIFY(JsonValue(1.) != JsonValue());
QVERIFY(JsonValue(1.) != JsonValue(true));
QVERIFY(JsonValue(1.) != JsonValue(JsonArray()));
QVERIFY(JsonValue(1.) != JsonValue(JsonObject()));
QCOMPARE(JsonValue(JsonArray()), JsonValue(JsonArray()));
JsonArray nonEmptyArray;
nonEmptyArray.append(true);
QVERIFY(JsonValue(JsonArray()) != nonEmptyArray);
QVERIFY(JsonValue(JsonArray()) != JsonValue(JsonValue::Undefined));
QVERIFY(JsonValue(JsonArray()) != JsonValue());
QVERIFY(JsonValue(JsonArray()) != JsonValue(true));
QVERIFY(JsonValue(JsonArray()) != JsonValue(1.));
QVERIFY(JsonValue(JsonArray()) != JsonValue(JsonObject()));
QCOMPARE(JsonValue(JsonObject()), JsonValue(JsonObject()));
JsonObject nonEmptyObject;
nonEmptyObject.insert("Key", true);
QVERIFY(JsonValue(JsonObject()) != nonEmptyObject);
QVERIFY(JsonValue(JsonObject()) != JsonValue(JsonValue::Undefined));
QVERIFY(JsonValue(JsonObject()) != JsonValue());
QVERIFY(JsonValue(JsonObject()) != JsonValue(true));
QVERIFY(JsonValue(JsonObject()) != JsonValue(1.));
QVERIFY(JsonValue(JsonObject()) != JsonValue(JsonArray()));
QCOMPARE(JsonValue("foo"), JsonValue("foo"));
QCOMPARE(JsonValue("\x66\x6f\x6f"), JsonValue("foo"));
QCOMPARE(JsonValue("\x62\x61\x72"), JsonValue("bar"));
QCOMPARE(JsonValue(UNICODE_NON_CHARACTER), JsonValue(UNICODE_NON_CHARACTER));
QCOMPARE(JsonValue(UNICODE_DJE), JsonValue(UNICODE_DJE));
QCOMPARE(JsonValue("\xc3\xa9"), JsonValue("\xc3\xa9"));
}
void tst_Json::objectEquals_data()
{
QTest::addColumn<JsonObject>("left");
QTest::addColumn<JsonObject>("right");
QTest::addColumn<bool>("result");
QTest::newRow("two defaults") << JsonObject() << JsonObject() << true;
JsonObject object1;
object1.insert("property", 1);
JsonObject object2;
object2["property"] = 1;
JsonObject object3;
object3.insert("property1", 1);
object3.insert("property2", 2);
QTest::newRow("the same object (1 vs 2)") << object1 << object2 << true;
QTest::newRow("the same object (3 vs 3)") << object3 << object3 << true;
QTest::newRow("different objects (2 vs 3)") << object2 << object3 << false;
QTest::newRow("object vs default") << object1 << JsonObject() << false;
JsonObject empty;
empty.insert("property", 1);
empty.take("property");
QTest::newRow("default vs empty") << JsonObject() << empty << true;
QTest::newRow("empty vs empty") << empty << empty << true;
QTest::newRow("object vs empty") << object1 << empty << false;
JsonObject referencedEmpty;
referencedEmpty["undefined"];
QTest::newRow("referenced empty vs referenced empty") << referencedEmpty << referencedEmpty << true;
QTest::newRow("referenced empty vs object") << referencedEmpty << object1 << false;
JsonObject referencedObject1;
referencedObject1.insert("property", 1);
referencedObject1["undefined"];
JsonObject referencedObject2;
referencedObject2.insert("property", 1);
referencedObject2["aaaaaaaaa"]; // earlier then "property"
referencedObject2["zzzzzzzzz"]; // after "property"
QTest::newRow("referenced object vs default") << referencedObject1 << JsonObject() << false;
QTest::newRow("referenced object vs referenced object") << referencedObject1 << referencedObject1 << true;
QTest::newRow("referenced object vs object (different)") << referencedObject1 << object3 << false;
}
void tst_Json::objectEquals()
{
QFETCH(JsonObject, left);
QFETCH(JsonObject, 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);
// The same but from JsonValue perspective
QCOMPARE(JsonValue(left) == JsonValue(right), result);
QCOMPARE(JsonValue(left) != JsonValue(right), !result);
QCOMPARE(JsonValue(right) == JsonValue(left), result);
QCOMPARE(JsonValue(right) != JsonValue(left), !result);
}
void tst_Json::arrayEquals_data()
{
QTest::addColumn<JsonArray>("left");
QTest::addColumn<JsonArray>("right");
QTest::addColumn<bool>("result");
QTest::newRow("two defaults") << JsonArray() << JsonArray() << true;
JsonArray array1;
array1.append(1);
JsonArray array2;
array2.append(2111);
array2[0] = 1;
JsonArray array3;
array3.insert(0, 1);
array3.insert(1, 2);
QTest::newRow("the same array (1 vs 2)") << array1 << array2 << true;
QTest::newRow("the same array (3 vs 3)") << array3 << array3 << true;
QTest::newRow("different arrays (2 vs 3)") << array2 << array3 << false;
QTest::newRow("array vs default") << array1 << JsonArray() << false;
JsonArray empty;
empty.append(1);
empty.takeAt(0);
QTest::newRow("default vs empty") << JsonArray() << empty << true;
QTest::newRow("empty vs default") << empty << JsonArray() << true;
QTest::newRow("empty vs empty") << empty << empty << true;
QTest::newRow("array vs empty") << array1 << empty << false;
}
void tst_Json::arrayEquals()
{
QFETCH(JsonArray, left);
QFETCH(JsonArray, 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);
// The same but from JsonValue perspective
QCOMPARE(JsonValue(left) == JsonValue(right), result);
QCOMPARE(JsonValue(left) != JsonValue(right), !result);
QCOMPARE(JsonValue(right) == JsonValue(left), result);
QCOMPARE(JsonValue(right) != JsonValue(left), !result);
}
void tst_Json::bom()
{
QFile file(testDataDir + QLatin1String("/bom.json"));
file.open(QFile::ReadOnly);
std::string json = file.readAll().data();
// Import json document into a JsonDocument
JsonParseError error;
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(!doc.isNull());
QCOMPARE(error.error, JsonParseError::NoError);
}
void tst_Json::nesting()
{
// check that we abort parsing too deeply nested json documents.
// this is to make sure we don't crash because the parser exhausts the
// stack.
const char *array_data =
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]";
std::string json(array_data);
JsonParseError error;
JsonDocument doc = JsonDocument::fromJson(json, &error);
QVERIFY(!doc.isNull());
QCOMPARE(error.error, JsonParseError::NoError);
json = '[' + json + ']';
doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isNull());
QCOMPARE(error.error, JsonParseError::DeepNesting);
json = std::string("true ");
for (int i = 0; i < 1024; ++i)
json = "{ \"Key\": " + json + " }";
doc = JsonDocument::fromJson(json, &error);
QVERIFY(!doc.isNull());
QCOMPARE(error.error, JsonParseError::NoError);
json = '[' + json + ']';
doc = JsonDocument::fromJson(json, &error);
QVERIFY(doc.isNull());
QCOMPARE(error.error, JsonParseError::DeepNesting);
}
void tst_Json::longStrings()
{
#if 0
// test around 15 and 16 bit boundaries, as these are limits
// in the data structures (for Latin1String in qjson_p.h)
QString s(0x7ff0, 'a');
for (int i = 0x7ff0; i < 0x8010; i++) {
s.append(QLatin1Char('c'));
QMap <QString, QVariant> map;
map["key"] = s;
/* Create a JsonDocument from the QMap ... */
JsonDocument d1 = JsonDocument::fromVariant(QVariant(map));
/* ... and a std::string from the JsonDocument */
std::string a1 = d1.toJson();
/* Create a JsonDocument from the std::string ... */
JsonDocument d2 = JsonDocument::fromJson(a1);
/* ... and a std::string from the JsonDocument */
std::string a2 = d2.toJson();
QCOMPARE(a1, a2);
}
s = QString(0xfff0, 'a');
for (int i = 0xfff0; i < 0x10010; i++) {
s.append(QLatin1Char('c'));
QMap <QString, QVariant> map;
map["key"] = s;
/* Create a JsonDocument from the QMap ... */
JsonDocument d1 = JsonDocument::fromVariant(QVariant(map));
/* ... and a std::string from the JsonDocument */
std::string a1 = d1.toJson();
/* Create a JsonDocument from the std::string ... */
JsonDocument d2 = JsonDocument::fromJson(a1);
/* ... and a std::string from the JsonDocument */
std::string a2 = d2.toJson();
QCOMPARE(a1, a2);
}
#endif
}
void tst_Json::testJsonValueRefDefault()
{
JsonObject empty;
QCOMPARE(empty["n/a"].toString(), std::string());
QCOMPARE(empty["n/a"].toString("default"), std::string("default"));
QCOMPARE(empty["n/a"].toBool(), false);
QCOMPARE(empty["n/a"].toBool(true), true);
QCOMPARE(empty["n/a"].toInt(), 0);
QCOMPARE(empty["n/a"].toInt(42), 42);
QCOMPARE(empty["n/a"].toDouble(), 0.0);
QCOMPARE(empty["n/a"].toDouble(42.0), 42.0);
}
void tst_Json::arrayInitializerList()
{
#ifndef Q_COMPILER_INITIALIZER_LISTS
QSKIP("initializer_list is enabled only with c++11 support");
#else
QVERIFY(JsonArray{}.isEmpty());
QCOMPARE(JsonArray{"one"}.count(), 1);
QCOMPARE(JsonArray{1}.count(), 1);
{
JsonArray a{1.3, "hello", 0};
QCOMPARE(JsonValue(a[0]), JsonValue(1.3));
QCOMPARE(JsonValue(a[1]), JsonValue("hello"));
QCOMPARE(JsonValue(a[2]), JsonValue(0));
QCOMPARE(a.count(), 3);
}
{
JsonObject o;
o["property"] = 1;
JsonArray a1{o};
QCOMPARE(a1.count(), 1);
QCOMPARE(a1[0].toObject(), o);
JsonArray a2{o, 23};
QCOMPARE(a2.count(), 2);
QCOMPARE(a2[0].toObject(), o);
QCOMPARE(JsonValue(a2[1]), JsonValue(23));
JsonArray a3{a1, o, a2};
QCOMPARE(JsonValue(a3[0]), JsonValue(a1));
QCOMPARE(JsonValue(a3[1]), JsonValue(o));
QCOMPARE(JsonValue(a3[2]), JsonValue(a2));
JsonArray a4{1, JsonArray{1,2,3}, JsonArray{"hello", 2}, JsonObject{{"one", 1}}};
QCOMPARE(a4.count(), 4);
QCOMPARE(JsonValue(a4[0]), JsonValue(1));
{
JsonArray a41 = a4[1].toArray();
JsonArray a42 = a4[2].toArray();
JsonObject a43 = a4[3].toObject();
QCOMPARE(a41.count(), 3);
QCOMPARE(a42.count(), 2);
QCOMPARE(a43.count(), 1);
QCOMPARE(JsonValue(a41[2]), JsonValue(3));
QCOMPARE(JsonValue(a42[1]), JsonValue(2));
QCOMPARE(JsonValue(a43["one"]), JsonValue(1));
}
}
#endif
}
void tst_Json::objectInitializerList()
{
#ifndef Q_COMPILER_INITIALIZER_LISTS
QSKIP("initializer_list is enabled only with c++11 support");
#else
QVERIFY(JsonObject{}.isEmpty());
{ // one property
JsonObject one {{"one", 1}};
QCOMPARE(one.count(), 1);
QVERIFY(one.contains("one"));
QCOMPARE(JsonValue(one["one"]), JsonValue(1));
}
{ // two properties
JsonObject two {
{"one", 1},
{"two", 2}
};
QCOMPARE(two.count(), 2);
QVERIFY(two.contains("one"));
QVERIFY(two.contains("two"));
QCOMPARE(JsonValue(two["one"]), JsonValue(1));
QCOMPARE(JsonValue(two["two"]), JsonValue(2));
}
{ // nested object
JsonObject object{{"nested", JsonObject{{"innerProperty", 2}}}};
QCOMPARE(object.count(), 1);
QVERIFY(object.contains("nested"));
QVERIFY(object["nested"].isObject());
JsonObject nested = object["nested"].toObject();
QCOMPARE(JsonValue(nested["innerProperty"]), JsonValue(2));
}
{ // nested array
JsonObject object{{"nested", JsonArray{"innerValue", 2.1, "bum cyk cyk"}}};
QCOMPARE(object.count(), 1);
QVERIFY(object.contains("nested"));
QVERIFY(object["nested"].isArray());
JsonArray nested = object["nested"].toArray();
QCOMPARE(nested.count(), 3);
QCOMPARE(JsonValue(nested[0]), JsonValue("innerValue"));
QCOMPARE(JsonValue(nested[1]), JsonValue(2.1));
}
#endif
}
void tst_Json::unicodeKeys()
{
std::string json = "{"
"\"x\\u2090_1\": \"hello_1\","
"\"y\\u2090_2\": \"hello_2\","
"\"T\\u2090_3\": \"hello_3\","
"\"xyz_4\": \"hello_4\","
"\"abc_5\": \"hello_5\""
"}";
JsonParseError error;
JsonDocument doc = JsonDocument::fromJson(json, &error);
QCOMPARE(error.error, JsonParseError::NoError);
JsonObject o = doc.object();
QCOMPARE(o.keys().size(), size_t(5));
Q_FOREACH (const std::string &k, o.keys()) {
QByteArray key(k.data());
std::string suffix = key.mid(key.indexOf('_')).data();
QCOMPARE(o[key.data()].toString(), "hello" + suffix);
}
}
void tst_Json::garbageAtEnd()
{
JsonParseError error;
JsonDocument doc = JsonDocument::fromJson("{},", &error);
QCOMPARE(error.error, JsonParseError::GarbageAtEnd);
QCOMPARE(error.offset, 2);
QVERIFY(doc.isEmpty());
doc = JsonDocument::fromJson("{} ", &error);
QCOMPARE(error.error, JsonParseError::NoError);
QVERIFY(!doc.isEmpty());
}
void tst_Json::removeNonLatinKey()
{
const std::string nonLatinKeyName = "Атрибут100500";
JsonObject sourceObject;
sourceObject.insert("code", 1);
sourceObject.remove("code");
sourceObject.insert(nonLatinKeyName, 1);
const std::string json = JsonDocument(sourceObject).toJson();
const JsonObject restoredObject = JsonDocument::fromJson(json).object();
QCOMPARE(sourceObject.keys(), restoredObject.keys());
QVERIFY(sourceObject.contains(nonLatinKeyName));
QVERIFY(restoredObject.contains(nonLatinKeyName));
}
QTEST_MAIN(tst_Json)
#include "tst_json.moc"