/* This file is part of QJson * * Copyright (C) 2009 Flavio Castelli * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include class TestSerializer: public QObject { Q_OBJECT private slots: void testReadWriteEmptyDocument(); void testReadWrite(); void testReadWrite_data(); void testValueNull(); void testValueString(); void testValueString_data(); void testValueStringList(); void testValueStringList_data(); void testValueHashMap(); void testValueInteger(); void testValueInteger_data(); void testValueDouble(); void testValueDouble_data(); void testSetDoublePrecision(); void testValueFloat(); void testValueFloat_data(); void testValueBoolean(); void testValueBoolean_data(); void testSpecialNumbers(); void testSpecialNumbers_data(); void testIndentation(); void testIndentation_data(); void testSerializetoQIODevice(); void testSerializeWithoutOkParam(); void testEscapeChars(); void testEscapeChars_data(); private: void valueTest( const QVariant& value, const QString& expectedRegExp, bool errorExpected = false ); void valueTest( const QObject* object, const QString& expectedRegExp ); }; Q_DECLARE_METATYPE(QVariant) using namespace QJson; void TestSerializer::testReadWriteEmptyDocument() { QByteArray json = ""; Parser parser; bool ok; QVariant result = parser.parse( json, &ok ); QVERIFY(!ok); QVERIFY( ! result.isValid() ); Serializer serializer; const QByteArray serialized = serializer.serialize( result, &ok); QVERIFY( ok ); QByteArray expected = "null"; QCOMPARE(expected, serialized); } void TestSerializer::testReadWrite() { QFETCH( QByteArray, json ); Parser parser; bool ok; QVariant result = parser.parse( json, &ok ); QVERIFY(ok); Serializer serializer; const QByteArray serialized = serializer.serialize( result, &ok); QVERIFY(ok); QVariant writtenThenRead = parser.parse( serialized, &ok ); QVERIFY(ok); QCOMPARE( result, writtenThenRead ); } void TestSerializer::testReadWrite_data() { QTest::addColumn( "json" ); // array tests QTest::newRow( "empty array" ) << QByteArray("[]"); QTest::newRow( "basic array" ) << QByteArray("[\"person\",\"bar\"]"); QTest::newRow( "single int array" ) << QByteArray("[6]"); QTest::newRow( "int array" ) << QByteArray("[6,5,6,7]"); const QByteArray json = "[1,2.4, -100, -3.4, -5e+0, 2e0,3e+0,4.3E0,5.4E-0]"; QTest::newRow( QByteArray("array of various numbers") ) << json; // document tests QTest::newRow( "empty object" ) << QByteArray("{}"); QTest::newRow( "basic document" ) << QByteArray("{\"person\":\"bar\"}"); QTest::newRow( "object with ints" ) << QByteArray("{\"person\":6}"); const QByteArray json2 = "{ \"person\":\"bar\",\n\"number\" : 51.3 , \"array\":[\"item1\", 123]}"; QTest::newRow( "complicated document" ) << json2; // more complex cases const QByteArray json3 = "[ {\"person\":\"bar\"},\n\"number\",51.3 , [\"item1\", 123]]"; QTest::newRow( "complicated array" ) << json3; } void TestSerializer::testIndentation() { QFETCH( QByteArray, json ); QFETCH( QByteArray, expected_compact ); QFETCH( QByteArray, expected_min ); QFETCH( QByteArray, expected_med ); QFETCH( QByteArray, expected_full ); // parse Parser parser; bool ok; QVariant parsed = parser.parse( json, &ok ); QVERIFY(ok); Serializer serializer; QVariant reparsed; QByteArray serialized; // serialize with indent compact and reparse serializer.setIndentMode(QJson::IndentCompact); serialized = serializer.serialize( parsed, &ok); QVERIFY(ok); QCOMPARE( serialized, expected_compact); reparsed = parser.parse( serialized, &ok); QVERIFY(ok); QCOMPARE( parsed, reparsed); // serialize with indent minimum and reparse serializer.setIndentMode(QJson::IndentMinimum); serialized = serializer.serialize( parsed, &ok); QVERIFY(ok); QCOMPARE( serialized, expected_min); reparsed = parser.parse( serialized, &ok); QVERIFY(ok); QCOMPARE( parsed, reparsed); // serialize with indent medium and reparse serializer.setIndentMode(QJson::IndentMedium); serialized = serializer.serialize( parsed, &ok); QVERIFY(ok); QCOMPARE( serialized, expected_med); reparsed = parser.parse( serialized, &ok ); QVERIFY(ok); QCOMPARE( parsed, reparsed); // serialize with indent full and reparse serializer.setIndentMode(QJson::IndentFull); serialized = serializer.serialize( parsed, &ok); QVERIFY(ok); QCOMPARE( serialized, expected_full); reparsed = parser.parse( serialized, &ok ); QVERIFY(ok); QCOMPARE( parsed, reparsed); } void TestSerializer::testIndentation_data() { QTest::addColumn( "json" ); QTest::addColumn( "expected_compact" ); QTest::addColumn( "expected_min" ); QTest::addColumn( "expected_med" ); QTest::addColumn( "expected_full" ); const QByteArray json = " { \"foo\" : 0, \"foo1\" : 1, \"foo2\" : [ { \"bar\" : 1, \"foo\" : 0, \"foobar\" : 0 }, { \"bar\" : 1, \"foo\" : 1, \"foobar\" : 1 } ], \"foo3\" : [ 1, 2, 3, 4, 5, 6 ], \"foobaz\" : [ \"one\", \"two\", \"three\", \"four\" ] }"; const QByteArray ex_compact = "{\"foo\":0,\"foo1\":1,\"foo2\":[{\"bar\":1,\"foo\":0,\"foobar\":0},{\"bar\":1,\"foo\":1,\"foobar\":1}],\"foo3\":[1,2,3,4,5,6],\"foobaz\":[\"one\",\"two\",\"three\",\"four\"]}"; const QByteArray ex_min = "{ \"foo\" : 0, \"foo1\" : 1, \"foo2\" : [\n" " { \"bar\" : 1, \"foo\" : 0, \"foobar\" : 0 },\n" " { \"bar\" : 1, \"foo\" : 1, \"foobar\" : 1 }\n" " ], \"foo3\" : [\n" " 1,\n" " 2,\n" " 3,\n" " 4,\n" " 5,\n" " 6\n" " ], \"foobaz\" : [\n" " \"one\",\n" " \"two\",\n" " \"three\",\n" " \"four\"\n" " ] }"; const QByteArray ex_med = "{\n" " \"foo\" : 0, \"foo1\" : 1, \"foo2\" : [\n" " {\n" " \"bar\" : 1, \"foo\" : 0, \"foobar\" : 0\n" " },\n" " {\n" " \"bar\" : 1, \"foo\" : 1, \"foobar\" : 1\n" " }\n" " ], \"foo3\" : [\n" " 1,\n" " 2,\n" " 3,\n" " 4,\n" " 5,\n" " 6\n" " ], \"foobaz\" : [\n" " \"one\",\n" " \"two\",\n" " \"three\",\n" " \"four\"\n" " ]\n}"; const QByteArray ex_full = "{\n" " \"foo\" : 0,\n" " \"foo1\" : 1,\n" " \"foo2\" : [\n" " {\n" " \"bar\" : 1,\n" " \"foo\" : 0,\n" " \"foobar\" : 0\n" " },\n" " {\n" " \"bar\" : 1,\n" " \"foo\" : 1,\n" " \"foobar\" : 1\n" " }\n" " ],\n" " \"foo3\" : [\n" " 1,\n" " 2,\n" " 3,\n" " 4,\n" " 5,\n" " 6\n" " ],\n" " \"foobaz\" : [\n" " \"one\",\n" " \"two\",\n" " \"three\",\n" " \"four\"\n" " ]\n" "}"; QTest::newRow( "test indents" ) << json << ex_compact << ex_min << ex_med << ex_full; } void TestSerializer::valueTest( const QVariant& value, const QString& expectedRegExp, bool errorExpected ) { Serializer serializer; bool ok; const QByteArray serialized = serializer.serialize( value, &ok); QCOMPARE(ok, !errorExpected); QCOMPARE(serialized.isNull(), errorExpected); const QString serializedUnicode = QString::fromUtf8( serialized ); if (!errorExpected) { QRegExp expected( expectedRegExp ); QVERIFY( expected.isValid() ); QVERIFY2( expected.exactMatch( serializedUnicode ), qPrintable( QString( QLatin1String( "Expected regexp \"%1\" but got \"%2\"." ) ) .arg( expectedRegExp ).arg( serializedUnicode ) ) ); } else { QVERIFY(!serializer.errorMessage().isEmpty()); } } void TestSerializer::valueTest( const QObject* object, const QString& expectedRegExp ) { Serializer serializer; bool ok; const QByteArray serialized = serializer.serialize( object, &ok); QVERIFY(ok); const QString serializedUnicode = QString::fromUtf8( serialized ); QRegExp expected( expectedRegExp ); QVERIFY( expected.isValid() ); QVERIFY2( expected.exactMatch( serializedUnicode ), qPrintable( QString( QLatin1String( "Expected regexp \"%1\" but got \"%2\"." ) ) .arg( expectedRegExp ).arg( serializedUnicode ) ) ); } void TestSerializer::testValueNull() { valueTest( QVariant(), QLatin1String( "\\s*null\\s*" ) ); QVariantMap map; map[QLatin1String("value")] = QVariant(); valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:\\s*null\\s*\\}\\s*" ) ); } void TestSerializer::testValueString() { QFETCH( QVariant, value ); QFETCH( QString, expected ); valueTest( value, expected ); QVariantMap map; map[QLatin1String("value")] = value; valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) ); } void TestSerializer::testValueString_data() { QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QTest::newRow( "null string" ) << QVariant( QString() ) << QString( QLatin1String( "\\s*\"\"\\s*" ) ); QTest::newRow( "empty string" ) << QVariant( QString( QLatin1String( "" ) ) ) << QString( QLatin1String( "\\s*\"\"\\s*" ) ); QTest::newRow( "Simple String" ) << QVariant( QString( QLatin1String( "simpleString" ) ) ) << QString( QLatin1String( "\\s*\"simpleString\"\\s*" ) ); QTest::newRow( "string with tab" ) << QVariant( QString( QLatin1String( "string\tstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\tstring\"\\s*" ) ); QTest::newRow( "string with newline" ) << QVariant( QString( QLatin1String( "string\nstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\nstring\"\\s*" ) ); QTest::newRow( "string with bell" ) << QVariant( QString( QLatin1String( "string\bstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\bstring\"\\s*" ) ); QTest::newRow( "string with return" ) << QVariant( QString( QLatin1String( "string\rstring" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\rstring\"\\s*" ) ); QTest::newRow( "string with double quote" ) << QVariant( QString( QLatin1String( "string\"string" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\\"string\"\\s*" ) ); QTest::newRow( "string with backslash" ) << QVariant( QString( QLatin1String( "string\\string" ) ) ) << QString( QLatin1String( "\\s*\"string\\\\\\\\string\"\\s*" ) ); QString testStringWithUnicode = QString( QLatin1String( "string" ) ) + QChar( 0x2665 ) + QLatin1String( "string" ); QString testEscapedString = QString( QLatin1String( "string" ) ) + QLatin1String("\\\\u2665") + QLatin1String( "string" ); QTest::newRow( "string with unicode" ) << QVariant( testStringWithUnicode ) << QString( QLatin1String( "\\s*\"" ) + testEscapedString + QLatin1String( "\"\\s*" ) ); } void TestSerializer::testValueStringList() { QFETCH( QVariant, value ); QFETCH( QString, expected ); valueTest( value, expected ); QVariantMap map; map[QLatin1String("value")] = value; valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) ); } void TestSerializer::testValueStringList_data() { QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QStringList stringlist; QString expected; // simple QStringList stringlist << QLatin1String("hello") << QLatin1String("world"); expected = QLatin1String( "\\s*\\[\\s*\"hello\"\\s*,\\s*\"world\"\\s*\\]\\s*" ); QTest::newRow( "simple QStringList" ) << QVariant( stringlist) << expected; } void TestSerializer::testValueInteger() { QFETCH( QVariant, value ); QFETCH( QString, expected ); valueTest( value, expected ); QVariantMap map; map[QLatin1String("value")] = value; valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) ); } void TestSerializer::testValueInteger_data() { QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QTest::newRow( "int 0" ) << QVariant( static_cast( 0 ) ) << QString( QLatin1String( "\\s*0\\s*" ) ); QTest::newRow( "uint 0" ) << QVariant( static_cast( 0 ) ) << QString( QLatin1String( "\\s*0\\s*" ) ); QTest::newRow( "int -1" ) << QVariant( static_cast( -1 ) ) << QString( QLatin1String( "\\s*-1\\s*" ) ); QTest::newRow( "int 2133149800" ) << QVariant( static_cast(2133149800) ) << QString( QLatin1String( "\\s*2133149800\\s*" ) ); QTest::newRow( "uint 4133149800" ) << QVariant( static_cast(4133149800u) ) << QString( QLatin1String( "\\s*4133149800\\s*" ) ); QTest::newRow( "uint64 932838457459459" ) << QVariant( Q_UINT64_C(932838457459459) ) << QString( QLatin1String( "\\s*932838457459459\\s*" ) ); QTest::newRow( "max unsigned long long" ) << QVariant( std::numeric_limits::max() ) << QString( QLatin1String( "\\s*%1\\s*" ) ).arg(std::numeric_limits::max()); } void TestSerializer::testValueDouble() { QFETCH( QVariant, value ); QFETCH( QString, expected ); QFETCH( bool, errorExpected ); valueTest( value, expected, errorExpected ); QVariantMap map; map[QLatin1String("value")] = value; valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ), errorExpected ); } void TestSerializer::testValueDouble_data() { QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QTest::addColumn( "errorExpected" ); QTest::newRow( "double 0" ) << QVariant( 0.0 ) << QString( QLatin1String( "\\s*0.0\\s*" ) ) << false; QTest::newRow( "double -1" ) << QVariant( -1.0 ) << QString( QLatin1String( "\\s*-1.0\\s*" ) ) << false; QTest::newRow( "double 1.5E-20" ) << QVariant( 1.5e-20 ) << QString( QLatin1String( "\\s*1.5[Ee]-20\\s*" ) ) << false; QTest::newRow( "double -1.5E-20" ) << QVariant( -1.5e-20 ) << QString( QLatin1String( "\\s*-1.5[Ee]-20\\s*" ) ) << false; QTest::newRow( "double 2.0E-20" ) << QVariant( 2.0e-20 ) << QString( QLatin1String( "\\s*2(?:.0)?[Ee]-20\\s*" ) ) << false; QTest::newRow( "double infinity" ) << QVariant( std::numeric_limits< double >::infinity() ) << QString( ) << true; QTest::newRow( "double -infinity" ) << QVariant( -std::numeric_limits< double >::infinity() ) << QString( ) << true; QTest::newRow( "double NaN" ) << QVariant( std::numeric_limits< double >::quiet_NaN() ) << QString( ) << true; } void TestSerializer::testSetDoublePrecision() { bool ok; Serializer serializer; QByteArray actual; QString expected, actualUnicode; double num = 0.12345678; // Set 1 as double precision serializer.setDoublePrecision(1); expected = QString(QLatin1String("0.1")); actual = serializer.serialize( QVariant(num), &ok); QVERIFY(ok); actualUnicode = QString::fromUtf8(actual); QVERIFY2( QString::compare(expected, actualUnicode ) == 0, qPrintable( QString( QLatin1String( "Expected \"%1\" but got \"%2\"." ) ) .arg( expected ).arg( actualUnicode ) ) ); // Set 2 as double precision serializer.setDoublePrecision(2); expected = QString(QLatin1String("0.12")); actual = serializer.serialize( QVariant(num), &ok); QVERIFY(ok); actualUnicode = QString::fromUtf8(actual); QVERIFY2( QString::compare(expected, actualUnicode ) == 0, qPrintable( QString( QLatin1String( "Expected \"%1\" but got \"%2\"." ) ) .arg( expected ).arg( actualUnicode ) ) ); // Set 4 as double precision serializer.setDoublePrecision(4); expected = QString(QLatin1String("0.1235")); actual = serializer.serialize( QVariant(num), &ok); QVERIFY(ok); actualUnicode = QString::fromUtf8(actual); QVERIFY2( QString::compare(expected, actualUnicode ) == 0, qPrintable( QString( QLatin1String( "Expected \"%1\" but got \"%2\"." ) ) .arg( expected ).arg( actualUnicode ) ) ); // Set 14 as double precision serializer.setDoublePrecision(14); expected = QString(QLatin1String("0.12345678")); actual = serializer.serialize( QVariant(num), &ok); QVERIFY(ok); actualUnicode = QString::fromUtf8(actual); QVERIFY2( QString::compare(expected, actualUnicode ) == 0, qPrintable( QString( QLatin1String( "Expected \"%1\" but got \"%2\"." ) ) .arg( expected ).arg( actualUnicode ) ) ); } void TestSerializer::testValueFloat() { QFETCH( QVariant, value ); QFETCH( QString, expected ); QFETCH( bool, errorExpected ); valueTest( value, expected, errorExpected ); QVariantMap map; map[QLatin1String("value")] = value; valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ), errorExpected ); } void TestSerializer::testValueFloat_data() { QVariant v; float value; QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QTest::addColumn( "errorExpected" ); value = 0; v.setValue(value); QTest::newRow( "float 0" ) << v << QString( QLatin1String( "\\s*0.0\\s*" ) ) << false; value = -1; v.setValue(value); QTest::newRow( "float -1" ) << v << QString( QLatin1String( "\\s*-1.0\\s*" ) ) << false; value = 1.12f; v.setValue(value); QTest::newRow( "float 1.12" ) << v << QString( QLatin1String( "\\s*1.12\\s*" ) ) << false; } void TestSerializer::testValueBoolean() { QFETCH( QVariant, value ); QFETCH( QString, expected ); valueTest( value, expected ); QVariantMap map; map[QLatin1String("value")] = value; valueTest( QVariant(map), QLatin1String( "\\s*\\{\\s*\"value\"\\s*:" ) + expected + QLatin1String( "\\}\\s*" ) ); } void TestSerializer::testValueBoolean_data() { QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QTest::newRow( "bool false" ) << QVariant( false ) << QString( QLatin1String( "\\s*false\\s*" ) ); QTest::newRow( "bool true" ) << QVariant( true ) << QString( QLatin1String( "\\s*true\\s*" ) ); } void TestSerializer::testSpecialNumbers() { bool ok; QFETCH( QVariant, value ); QFETCH( QString, expected ); Serializer specialSerializer; QVERIFY(!specialSerializer.specialNumbersAllowed()); specialSerializer.allowSpecialNumbers(true); QVERIFY(specialSerializer.specialNumbersAllowed()); QByteArray serialized = specialSerializer.serialize(value, &ok); QVERIFY(ok); QCOMPARE(QString::fromLocal8Bit(serialized), expected); } void TestSerializer::testSpecialNumbers_data() { QTest::addColumn( "value" ); QTest::addColumn( "expected" ); QTest::newRow( "Infinity" ) << QVariant( std::numeric_limits< double >::infinity() ) << QString::fromLocal8Bit("Infinity"); QTest::newRow( "-Infinity" ) << QVariant( -std::numeric_limits< double >::infinity() ) << QString::fromLocal8Bit("-Infinity"); QTest::newRow( "Infinity" ) << QVariant( std::numeric_limits< double >::quiet_NaN() ) << QString::fromLocal8Bit("NaN"); } void TestSerializer::testSerializetoQIODevice() { QBuffer buffer; QVariantList variant; variant << QVariant(QLatin1String("Hello")); variant << QVariant(QLatin1String("world!")); Serializer serializer; bool ok; serializer.serialize(variant, &buffer, &ok); QCOMPARE(QString(QLatin1String(buffer.data())), QString(QLatin1String("[ \"Hello\", \"world!\" ]"))); QVERIFY(ok); } void TestSerializer::testSerializeWithoutOkParam() { QBuffer buffer; QVariantList variant; variant << QVariant(QLatin1String("Hello")); variant << QVariant(QLatin1String("world!")); Serializer serializer; const QByteArray serialized = serializer.serialize(variant); const QByteArray expected = "[ \"Hello\", \"world!\" ]"; QCOMPARE(expected, serialized); // test a serialization which produces an error QVariant brokenVariant ( std::numeric_limits< double >::quiet_NaN() ); QVERIFY(serializer.serialize(brokenVariant).isEmpty()); } void TestSerializer::testValueHashMap() { Serializer serializer; bool ok; QVariantHash hash; hash[QLatin1String("one")] = 1; hash[QLatin1String("three")] = 3; hash[QLatin1String("seven")] = 7; QByteArray json = serializer.serialize(hash, &ok); QVERIFY(ok); Parser parser; QVariant var = parser.parse(json, &ok); QVERIFY(ok); QVariantMap vmap = var.toMap(); QHashIterator hIt( hash ); while ( hIt.hasNext() ) { hIt.next(); QString key = hIt.key(); QVariant value = hIt.value(); QMap::const_iterator mIt = vmap.constFind(key); QVERIFY(mIt != vmap.constEnd()); QCOMPARE(mIt.value(), value); } } void TestSerializer::testEscapeChars() { QFETCH(QString, input); QFETCH(QString, escaped); Serializer serializer; bool ok; QVariantHash hash; hash.insert(QLatin1String("key"), input); QByteArray json = serializer.serialize(hash, &ok); QVERIFY(ok); QString expected = QString(QLatin1String("{ \"key\" : \"%1\" }")).arg(escaped); QString actual = QString::fromUtf8(json.data(), json.length()); QCOMPARE(actual, expected); } void TestSerializer::testEscapeChars_data() { QTest::addColumn("input"); QTest::addColumn("escaped"); QTest::newRow("simple ASCII string") << "input" << "input"; QTest::newRow("ASCII new lines and tabs") << "line1\nline2\rline\t3" << "line1\\nline2\\rline\\t3"; QTest::newRow("backspace, backslash and quotes") << "one\\two\bthree\"four" << "one\\\\two\\bthree\\\"four"; QChar unicodeSnowman(0x2603); QTest::newRow("non-ASCII unicode char") << QString(unicodeSnowman) << "\\u2603"; QTest::newRow("control chars") << QString(QChar(0x06)) << "\\u0006"; } #if QT_VERSION < QT_VERSION_CHECK(5,0,0) // using Qt4 rather then Qt5 QTEST_MAIN(TestSerializer) #include "moc_testserializer.cxx" #else QTEST_GUILESS_MAIN(TestSerializer) #include "testserializer.moc" #endif