forked from qt-creator/qt-creator
Source code needed adjustments though. Change-Id: I78b4610a6bb895a385c7c30a6c92c97a276b89dd Reviewed-by: hjk <hjk@qt.io>
2532 lines
81 KiB
C++
2532 lines
81 KiB
C++
/****************************************************************************
|
||
**
|
||
** 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"
|