diff options
Diffstat (limited to 'tests/auto/corelib')
60 files changed, 4157 insertions, 468 deletions
diff --git a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp index f8f9387abb..b3b7c082cc 100644 --- a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp +++ b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp @@ -2429,7 +2429,7 @@ void tst_QTextCodec::userCodec() QVERIFY(!QTextCodec::availableCodecs().contains("UserCodec")); QVERIFY(!QTextCodec::codecForName("UserCodec")); - QTextCodec *codec = new UserCodec; + UserCodec *codec = new UserCodec; executedOnce = true; QList<QByteArray> availableCodecs = QTextCodec::availableCodecs(); @@ -2448,6 +2448,11 @@ void tst_QTextCodec::userCodec() pcodec = QTextCodec::codecForMib(5000); QCOMPARE(pcodec, codec); + + delete codec; + + pcodec = QTextCodec::codecForName("UserCodec"); + QCOMPARE(pcodec, nullptr); } struct DontCrashAtExit { diff --git a/tests/auto/corelib/global/qflags/tst_qflags.cpp b/tests/auto/corelib/global/qflags/tst_qflags.cpp index 2f1b56629a..6129184738 100644 --- a/tests/auto/corelib/global/qflags/tst_qflags.cpp +++ b/tests/auto/corelib/global/qflags/tst_qflags.cpp @@ -39,6 +39,7 @@ private slots: void classEnum(); void initializerLists(); void testSetFlags(); + void adl(); }; void tst_QFlags::testFlag() const @@ -304,6 +305,27 @@ void tst_QFlags::testSetFlags() QVERIFY(!flags.testFlag(MyStrictEnum::StrictFour)); } +namespace SomeNS { +enum Foo { Foo_A = 1 << 0, Foo_B = 1 << 1, Foo_C = 1 << 2 }; + +Q_DECLARE_FLAGS(Foos, Foo) +Q_DECLARE_OPERATORS_FOR_FLAGS(Foos); + +Qt::Alignment alignment() +{ + // Checks that the operator| works, despite there is another operator| in this namespace. + return Qt::AlignLeft | Qt::AlignTop; +} +} + +void tst_QFlags::adl() +{ + SomeNS::Foos fl = SomeNS::Foo_B | SomeNS::Foo_C; + QVERIFY(fl & SomeNS::Foo_B); + QVERIFY(!(fl & SomeNS::Foo_A)); + QCOMPARE(SomeNS::alignment(), Qt::AlignLeft | Qt::AlignTop); +} + // (statically) check QTypeInfo for QFlags instantiations: enum MyEnum { Zero, One, Two, Four=4 }; Q_DECLARE_FLAGS( MyFlags, MyEnum ) diff --git a/tests/auto/corelib/global/qlogging/app/app.pro b/tests/auto/corelib/global/qlogging/app/app.pro index 30751d89ec..b90b685749 100644 --- a/tests/auto/corelib/global/qlogging/app/app.pro +++ b/tests/auto/corelib/global/qlogging/app/app.pro @@ -1,6 +1,15 @@ TEMPLATE = app -TARGET = app +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../debug/helper + } else { + TARGET = ../release/helper + } +} else { + TARGET = ../helper +} + QT = core DESTDIR = ./ diff --git a/tests/auto/corelib/global/qlogging/test/test.pro b/tests/auto/corelib/global/qlogging/test/test.pro index b48bf82cf9..91896d4494 100644 --- a/tests/auto/corelib/global/qlogging/test/test.pro +++ b/tests/auto/corelib/global/qlogging/test/test.pro @@ -1,11 +1,21 @@ CONFIG += testcase -CONFIG -= debug_and_release_target qtConfig(c++11): CONFIG += c++11 qtConfig(c++14): CONFIG += c++14 -TARGET = ../tst_qlogging +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qlogging + !android:!winrt: TEST_HELPER_INSTALLS = ../debug/helper + } else { + TARGET = ../../release/tst_qlogging + !android:!winrt: TEST_HELPER_INSTALLS = ../release/helper + } +} else { + TARGET = ../tst_qlogging + !android:!winrt: TEST_HELPER_INSTALLS = ../helper +} + QT = core testlib SOURCES = ../tst_qlogging.cpp DEFINES += QT_MESSAGELOGCONTEXT -!android:!winrt: TEST_HELPER_INSTALLS = ../app/app DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp index e7a3748c30..d3ed1a6d0d 100644 --- a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp +++ b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp @@ -64,7 +64,6 @@ private slots: void formatLogMessage(); private: - QString m_appDir; QStringList m_baseEnvironment; }; @@ -101,14 +100,6 @@ tst_qmessagehandler::tst_qmessagehandler() void tst_qmessagehandler::initTestCase() { #if QT_CONFIG(process) -# ifdef Q_OS_ANDROID - m_appDir = QCoreApplication::applicationDirPath(); -# else // !android - m_appDir = QFINDTESTDATA("app"); -# endif - QVERIFY2(!m_appDir.isEmpty(), qPrintable( - QString::fromLatin1("Couldn't find helper app dir starting from %1.").arg(QDir::currentPath()))); - m_baseEnvironment = QProcess::systemEnvironment(); for (int i = 0; i < m_baseEnvironment.count(); ++i) { if (m_baseEnvironment.at(i).startsWith("QT_MESSAGE_PATTERN=")) { @@ -806,7 +797,7 @@ void tst_qmessagehandler::qMessagePattern_data() #ifndef QT_NO_DEBUG QTest::newRow("backtrace") << "[%{backtrace}] %{message}" << true << (QList<QByteArray>() // MyClass::qt_static_metacall is explicitly marked as hidden in the Q_OBJECT macro - << "[MyClass::myFunction|MyClass::mySlot1|?app?|" QT_NAMESPACE_STR "QMetaMethod::invoke|" QT_NAMESPACE_STR "QMetaObject::invokeMethod] from_a_function 34"); + << "[MyClass::myFunction|MyClass::mySlot1|?helper?|" QT_NAMESPACE_STR "QMetaMethod::invoke|" QT_NAMESPACE_STR "QMetaObject::invokeMethod] from_a_function 34"); #endif QTest::newRow("backtrace depth,separator") << "[%{backtrace depth=2 separator=\"\n\"}] %{message}" << true << (QList<QByteArray>() @@ -830,10 +821,10 @@ void tst_qmessagehandler::qMessagePattern() QFETCH(QList<QByteArray>, expected); QProcess process; -#ifdef Q_OS_ANDROID - const QString appExe = m_appDir + "/libapp.so"; -#else // !android - const QString appExe = m_appDir + "/app"; +#ifndef Q_OS_ANDROID + const QString appExe(QLatin1String("helper")); +#else + const QString appExe(QCoreApplication::applicationDirPath() + QLatin1String("/libhelper.so")); #endif // @@ -880,10 +871,10 @@ void tst_qmessagehandler::setMessagePattern() // QProcess process; -#ifdef Q_OS_ANDROID - const QString appExe = m_appDir + "/libapp.so"; -#else // !android - const QString appExe = m_appDir + "/app"; +#ifndef Q_OS_ANDROID + const QString appExe(QLatin1String("helper")); +#else + const QString appExe(QCoreApplication::applicationDirPath() + QLatin1String("/libhelper.so")); #endif // make sure there is no QT_MESSAGE_PATTERN in the environment diff --git a/tests/auto/corelib/io/qfile/.gitignore b/tests/auto/corelib/io/qfile/.gitignore index c508239722..615264e4d4 100644 --- a/tests/auto/corelib/io/qfile/.gitignore +++ b/tests/auto/corelib/io/qfile/.gitignore @@ -1,6 +1,10 @@ tst_qfile -stdinprocess/stdinprocess -stdinprocess/stdinprocess.exe +stdinprocess_helper +stdinprocess_helper.exe +debug/stdinprocess_helper +debug/stdinprocess_helper.exe +release/stdinprocess_helper +release/stdinprocess_helper.exe readonlyfile newfile.txt appendfile.txt diff --git a/tests/auto/corelib/io/qfile/qfile.pro b/tests/auto/corelib/io/qfile/qfile.pro index 0735daedb3..d2bfc08372 100644 --- a/tests/auto/corelib/io/qfile/qfile.pro +++ b/tests/auto/corelib/io/qfile/qfile.pro @@ -1,2 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = test stdinprocess +SUBDIRS = test +!winrt: SUBDIRS += stdinprocess diff --git a/tests/auto/corelib/io/qfile/stdinprocess/main.cpp b/tests/auto/corelib/io/qfile/stdinprocess/main.cpp index 6ff42c2485..77a1932bd5 100644 --- a/tests/auto/corelib/io/qfile/stdinprocess/main.cpp +++ b/tests/auto/corelib/io/qfile/stdinprocess/main.cpp @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { if (argc < 2) { - printf("usage: stdinprocess <all|line <0|1>>\n"); + printf("usage: stdinprocess_helper <all|line <0|1>>\n"); printf("echos all its input to its output.\n"); return 1; } diff --git a/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro b/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro index 8e463e4cef..4029701943 100644 --- a/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro +++ b/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro @@ -1,8 +1,18 @@ SOURCES += main.cpp QT = core -CONFIG -= app_bundle debug_and_release_target +CONFIG -= app_bundle CONFIG += console +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/stdinprocess_helper + } else { + TARGET = ../../release/stdinprocess_helper + } +} else { + TARGET = ../stdinprocess_helper +} + # This app is testdata for tst_qfile target.path = $$[QT_INSTALL_TESTS]/tst_qfile/$$TARGET INSTALLS += target diff --git a/tests/auto/corelib/io/qfile/test/test.pro b/tests/auto/corelib/io/qfile/test/test.pro index 1472ddbb83..7b952b0283 100644 --- a/tests/auto/corelib/io/qfile/test/test.pro +++ b/tests/auto/corelib/io/qfile/test/test.pro @@ -1,5 +1,4 @@ CONFIG += testcase -CONFIG -= debug_and_release_target QT = core-private core testlib qtHaveModule(network): QT += network else: DEFINES += QT_NO_NETWORK @@ -10,7 +9,15 @@ contains(CONFIG, builtin_testdata) { TESTDATA += ../BLACKLIST -TARGET = ../tst_qfile +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qfile + } else { + TARGET = ../../release/tst_qfile + } +} else { + TARGET = ../tst_qfile +} SOURCES = ../tst_qfile.cpp INCLUDEPATH += ../../../../../shared/ HEADERS += ../../../../../shared/emulationdetector.h diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index 6665200585..41c9871e26 100644 --- a/tests/auto/corelib/io/qfile/tst_qfile.cpp +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -367,7 +367,7 @@ private: QTemporaryDir m_temporaryDir; const QString m_oldDir; - QString m_stdinProcessDir; + QString m_stdinProcess; QString m_testSourceFile; QString m_testLogFile; QString m_dosFile; @@ -379,12 +379,6 @@ private: QString m_noEndOfLineFile; }; -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) - #define STDINPROCESS_NAME "libstdinprocess.so" -#else // !android || android_embedded - #define STDINPROCESS_NAME "stdinprocess" -#endif // android && !android_embededd - static const char noReadFile[] = "noreadfile"; static const char readOnlyFile[] = "readonlyfile"; @@ -455,11 +449,13 @@ void tst_QFile::initTestCase() QVERIFY2(m_temporaryDir.isValid(), qPrintable(m_temporaryDir.errorString())); #if QT_CONFIG(process) #if defined(Q_OS_ANDROID) - m_stdinProcessDir = QCoreApplication::applicationDirPath(); + m_stdinProcess = QCoreApplication::applicationDirPath() + QLatin1String("/libstdinprocess_helper.so"); +#elif defined(Q_OS_WIN) + m_stdinProcess = QFINDTESTDATA("stdinprocess_helper.exe"); #else - m_stdinProcessDir = QFINDTESTDATA("stdinprocess"); + m_stdinProcess = QFINDTESTDATA("stdinprocess_helper"); #endif - QVERIFY(!m_stdinProcessDir.isEmpty()); + QVERIFY(!m_stdinProcess.isEmpty()); #endif m_testLogFile = QFINDTESTDATA("testlog.txt"); QVERIFY(!m_testLogFile.isEmpty()); @@ -981,7 +977,7 @@ void tst_QFile::readAllStdin() QProcess process; StdinReaderProcessGuard processGuard(&process); - process.start(m_stdinProcessDir + QStringLiteral("/" STDINPROCESS_NAME), QStringList(QStringLiteral("all"))); + process.start(m_stdinProcess, QStringList(QStringLiteral("all"))); QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); for (int i = 0; i < 5; ++i) { QTest::qWait(1000); @@ -1014,7 +1010,7 @@ void tst_QFile::readLineStdin() for (int i = 0; i < 2; ++i) { QProcess process; StdinReaderProcessGuard processGuard(&process); - process.start(m_stdinProcessDir + QStringLiteral("/" STDINPROCESS_NAME), + process.start(m_stdinProcess, QStringList() << QStringLiteral("line") << QString::number(i), QIODevice::Text | QIODevice::ReadWrite); QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); @@ -1050,7 +1046,7 @@ void tst_QFile::readLineStdin_lineByLine() for (int i = 0; i < 2; ++i) { QProcess process; StdinReaderProcessGuard processGuard(&process); - process.start(m_stdinProcessDir + QStringLiteral("/" STDINPROCESS_NAME), + process.start(m_stdinProcess, QStringList() << QStringLiteral("line") << QString::number(i), QIODevice::Text | QIODevice::ReadWrite); QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); diff --git a/tests/auto/corelib/io/qresourceengine/qresourceengine.pro b/tests/auto/corelib/io/qresourceengine/qresourceengine.pro index e8071297b1..1e12a41dea 100644 --- a/tests/auto/corelib/io/qresourceengine/qresourceengine.pro +++ b/tests/auto/corelib/io/qresourceengine/qresourceengine.pro @@ -1,25 +1,2 @@ -CONFIG += testcase -TARGET = tst_qresourceengine - -QT = core testlib -SOURCES = tst_qresourceengine.cpp -RESOURCES += testqrc/test.qrc - -qtPrepareTool(QMAKE_RCC, rcc, _DEP) -runtime_resource.target = runtime_resource.rcc -runtime_resource.depends = $$PWD/testqrc/test.qrc $$QMAKE_RCC_EXE -runtime_resource.commands = $$QMAKE_RCC -root /runtime_resource/ -binary $$PWD/testqrc/test.qrc -o $${runtime_resource.target} -QMAKE_EXTRA_TARGETS = runtime_resource -PRE_TARGETDEPS += $${runtime_resource.target} -QMAKE_DISTCLEAN += $${runtime_resource.target} - -TESTDATA += \ - parentdir.txt \ - testqrc/* -GENERATED_TESTDATA = $${runtime_resource.target} - -android:!android-embedded { - RESOURCES += android_testdata.qrc -} - -builtin_testdata: DEFINES += BUILTIN_TESTDATA +TEMPLATE = subdirs +SUBDIRS = staticplugin qresourceengine_test.pro diff --git a/tests/auto/corelib/io/qresourceengine/qresourceengine_test.pro b/tests/auto/corelib/io/qresourceengine/qresourceengine_test.pro new file mode 100644 index 0000000000..3838a72c21 --- /dev/null +++ b/tests/auto/corelib/io/qresourceengine/qresourceengine_test.pro @@ -0,0 +1,33 @@ +CONFIG += testcase +TARGET = tst_qresourceengine + +QT = core testlib +SOURCES = tst_qresourceengine.cpp +RESOURCES += testqrc/test.qrc + +qtPrepareTool(QMAKE_RCC, rcc, _DEP) +runtime_resource.target = runtime_resource.rcc +runtime_resource.depends = $$PWD/testqrc/test.qrc $$QMAKE_RCC_EXE +runtime_resource.commands = $$QMAKE_RCC -root /runtime_resource/ -binary $$PWD/testqrc/test.qrc -o $${runtime_resource.target} +QMAKE_EXTRA_TARGETS = runtime_resource +PRE_TARGETDEPS += $${runtime_resource.target} +QMAKE_DISTCLEAN += $${runtime_resource.target} + +TESTDATA += \ + parentdir.txt \ + testqrc/* +GENERATED_TESTDATA = $${runtime_resource.target} + +android:!android-embedded { + RESOURCES += android_testdata.qrc +} + +win32 { + CONFIG(debug, debug|release): LIBS += -Lstaticplugin/debug + else: LIBS += -Lstaticplugin/release +} else { + LIBS += -Lstaticplugin +} +LIBS += -lmoctestplugin + +builtin_testdata: DEFINES += BUILTIN_TESTDATA diff --git a/tests/auto/corelib/io/qresourceengine/staticplugin/.gitignore b/tests/auto/corelib/io/qresourceengine/staticplugin/.gitignore new file mode 100644 index 0000000000..c397dde6a5 --- /dev/null +++ b/tests/auto/corelib/io/qresourceengine/staticplugin/.gitignore @@ -0,0 +1 @@ +moctestplugin_plugin_resources.cpp diff --git a/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp b/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp new file mode 100644 index 0000000000..39a3a1e012 --- /dev/null +++ b/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp @@ -0,0 +1,9 @@ +#include <QObject> + +class PluginClass : public QObject +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.tests.moc" FILE "staticplugin.json") +}; + +#include "main.moc" diff --git a/tests/auto/corelib/io/qresourceengine/staticplugin/staticplugin.json b/tests/auto/corelib/io/qresourceengine/staticplugin/staticplugin.json new file mode 100644 index 0000000000..4103ecb18c --- /dev/null +++ b/tests/auto/corelib/io/qresourceengine/staticplugin/staticplugin.json @@ -0,0 +1 @@ +{ "Keys": [ "staticplugin" ] } diff --git a/tests/auto/corelib/io/qresourceengine/staticplugin/staticplugin.pro b/tests/auto/corelib/io/qresourceengine/staticplugin/staticplugin.pro new file mode 100644 index 0000000000..e19d884548 --- /dev/null +++ b/tests/auto/corelib/io/qresourceengine/staticplugin/staticplugin.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +TARGET = moctestplugin +CONFIG += plugin static +SOURCES = main.cpp +plugin_resource.files = main.cpp +plugin_resource.prefix = /staticplugin +RESOURCES += plugin_resource +QT = core diff --git a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp index b7e85e8f05..ab49dea6d8 100644 --- a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp +++ b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp @@ -57,6 +57,7 @@ private slots: void doubleSlashInRoot(); void setLocale(); void lastModified(); + void resourcesInStaticPlugins(); private: const QString m_runtimeResourceRcc; @@ -119,6 +120,7 @@ void tst_QResourceEngine::checkStructure_data() << QLatin1String("searchpath1") << QLatin1String("searchpath2") << QLatin1String("secondary_root") + << QLatin1String("staticplugin") << QLatin1String("test") << QLatin1String("withoutslashes"); @@ -127,7 +129,7 @@ void tst_QResourceEngine::checkStructure_data() #endif #if defined(BUILTIN_TESTDATA) - rootContents.insert(8, QLatin1String("testqrc")); + rootContents.insert(9, QLatin1String("testqrc")); #endif @@ -520,6 +522,16 @@ void tst_QResourceEngine::lastModified() } } +Q_IMPORT_PLUGIN(PluginClass) +void tst_QResourceEngine::resourcesInStaticPlugins() +{ + // We built a separate static plugin and attempted linking against + // it. That should successfully register the resources linked into + // the plugin via moc generated Q_INIT_RESOURCE calls in a + // Q_CONSTRUCTOR_FUNCTION. + QVERIFY(QFile::exists(":/staticplugin/main.cpp")); +} + QTEST_MAIN(tst_QResourceEngine) #include "tst_qresourceengine.moc" diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index db756ada39..77c1211ab5 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -531,7 +531,7 @@ void tst_QSettings::ctor() // more details in QMacSettingsPrivate::QMacSettingsPrivate(), organization was comify()-ed caseSensitive = settings5.fileName().contains("SoftWare.ORG");; } else { - caseSensitive = pathconf(QDir::currentPath().toLatin1().constData(), _PC_CASE_SENSITIVE); + caseSensitive = pathconf(settings5.fileName().toLatin1().constData(), _PC_CASE_SENSITIVE); } #elif defined(Q_OS_WIN32) || defined(Q_OS_WINRT) caseSensitive = false; diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp index 3de777653e..5cb130f631 100644 --- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp +++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp @@ -183,12 +183,14 @@ void tst_qstandardpaths::testDefaultLocations() #endif } +#ifdef Q_XDG_PLATFORM static void createTestFile(const QString &fileName) { QFile file(fileName); QVERIFY(file.open(QIODevice::WriteOnly)); QVERIFY(file.write("Hello")); } +#endif void tst_qstandardpaths::testCustomLocations() { diff --git a/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp b/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp index 8b1aa105de..1317489e2f 100644 --- a/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp +++ b/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp @@ -80,7 +80,7 @@ static int qInfoPrinter(const char *format, ...) // flush QtMessageHandler qt_message_print = qInstallMessageHandler(0); qInstallMessageHandler(qt_message_print); // restore the handler - qt_message_print(QtInfoMsg, QMessageLogContext(), QString::fromLocal8Bit(buf)); + qt_message_print(QtInfoMsg, QMessageLogContext(), QString::fromLocal8Bit(buf).trimmed()); bufuse = 0; } diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp index a7ab1e20d9..a9138a6505 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp @@ -152,6 +152,10 @@ private slots: void emitLayoutChangedOnlyIfSortingChanged(); void checkSetNewModel(); + + void removeIntervals_data(); + void removeIntervals(); + protected: void buildHierarchy(const QStringList &data, QAbstractItemModel *model); void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); @@ -4429,6 +4433,44 @@ void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged_data() QTest::newRow("many_changes_no_layoutChanged") << -1 << Qt::DisplayRole << "7,5,4,3,2,1,0,8" << "75432108" << "0248" << 0; } +// Custom version of QStringListModel which supports emitting dataChanged for many rows at once +class CustomStringListModel : public QAbstractListModel +{ +public: + bool setData(const QModelIndex &index, const QVariant &value, int role) override + { + if (index.row() >= 0 && index.row() < lst.size() + && (role == Qt::EditRole || role == Qt::DisplayRole)) { + lst.replace(index.row(), value.toString()); + emit dataChanged(index, index, { Qt::DisplayRole, Qt::EditRole }); + return true; + } + return false; + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + return lst.at(index.row()); + return QVariant(); + } + int rowCount(const QModelIndex & = QModelIndex()) const override { return lst.count(); } + + void replaceData(const QStringList &newData) + { + lst = newData; + emit dataChanged(index(0, 0), index(rowCount() - 1, 0), { Qt::DisplayRole, Qt::EditRole }); + } + + void emitDecorationChangedSignal() + { + const QModelIndex idx = index(0, 0); + emit dataChanged(idx, idx, { Qt::DecorationRole }); + } + +private: + QStringList lst; +}; + void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged() { QFETCH(int, changedRow); @@ -4438,45 +4480,6 @@ void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged() QFETCH(QString, expectedProxyRowTexts); QFETCH(int, expectedLayoutChanged); - // Custom version of QStringListModel which supports emitting dataChanged for many rows at once - class CustomStringListModel : public QAbstractListModel - { - public: - bool setData(const QModelIndex &index, const QVariant &value, int role) override - { - if (index.row() >= 0 && index.row() < lst.size() - && (role == Qt::EditRole || role == Qt::DisplayRole)) { - lst.replace(index.row(), value.toString()); - emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); - return true; - } - return false; - } - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override - { - if (role == Qt::DisplayRole || role == Qt::EditRole) - return lst.at(index.row()); - return QVariant(); - } - int rowCount(const QModelIndex & = QModelIndex()) const override - { - return lst.count(); - } - - void replaceData(const QStringList &newData) - { - lst = newData; - emit dataChanged(index(0, 0), index(rowCount()-1, 0), {Qt::DisplayRole, Qt::EditRole}); - } - - void emitDecorationChangedSignal() - { - const QModelIndex idx = index(0, 0); - emit dataChanged(idx, idx, {Qt::DecorationRole}); - } - private: - QStringList lst; - }; CustomStringListModel model; QStringList strings; for (auto i = 8; i >= 1; --i) @@ -4514,6 +4517,137 @@ void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged() QCOMPARE(proxyLayoutChangedSpy.size(), expectedLayoutChanged); } +void tst_QSortFilterProxyModel::removeIntervals_data() +{ + QTest::addColumn<QStringList>("sourceItems"); + QTest::addColumn<int>("sortOrder"); + QTest::addColumn<QString>("filter"); + QTest::addColumn<QStringList>("replacementSourceItems"); + QTest::addColumn<IntPairList>("expectedRemovedProxyIntervals"); + QTest::addColumn<QStringList>("expectedProxyItems"); + + QTest::newRow("filter all, sort ascending") + << (QStringList() << "a" + << "b" + << "c") // sourceItems + << static_cast<int>(Qt::AscendingOrder) // sortOrder + << "[^x]" // filter + << (QStringList() << "x" + << "x" + << "x") // replacementSourceItems + << (IntPairList() << IntPair(0, 2)) // expectedRemovedIntervals + << QStringList() // expectedProxyItems + ; + + QTest::newRow("filter all, sort descending") + << (QStringList() << "a" + << "b" + << "c") // sourceItems + << static_cast<int>(Qt::DescendingOrder) // sortOrder + << "[^x]" // filter + << (QStringList() << "x" + << "x" + << "x") // replacementSourceItems + << (IntPairList() << IntPair(0, 2)) // expectedRemovedIntervals + << QStringList() // expectedProxyItems + ; + + QTest::newRow("filter first and last, sort ascending") + << (QStringList() << "a" + << "b" + << "c") // sourceItems + << static_cast<int>(Qt::AscendingOrder) // sortOrder + << "[^x]" // filter + << (QStringList() << "x" + << "b" + << "x") // replacementSourceItems + << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals + << (QStringList() << "b") // expectedProxyItems + ; + + QTest::newRow("filter first and last, sort descending") + << (QStringList() << "a" + << "b" + << "c") // sourceItems + << static_cast<int>(Qt::DescendingOrder) // sortOrder + << "[^x]" // filter + << (QStringList() << "x" + << "b" + << "x") // replacementSourceItems + << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals + << (QStringList() << "b") // expectedProxyItems + ; +} + +void tst_QSortFilterProxyModel::removeIntervals() +{ + QFETCH(QStringList, sourceItems); + QFETCH(int, sortOrder); + QFETCH(QString, filter); + QFETCH(QStringList, replacementSourceItems); + QFETCH(IntPairList, expectedRemovedProxyIntervals); + QFETCH(QStringList, expectedProxyItems); + + CustomStringListModel model; + QSortFilterProxyModel proxy; + + model.replaceData(sourceItems); + proxy.setSourceModel(&model); + + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex sindex = model.index(i, 0, QModelIndex()); + QModelIndex pindex = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole)); + } + + proxy.setDynamicSortFilter(true); + + if (sortOrder != -1) + proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); + if (!filter.isEmpty()) + proxy.setFilterRegExp(QRegExp(filter)); + + (void)proxy.rowCount(QModelIndex()); // force mapping + + QSignalSpy removeSpy(&proxy, &QSortFilterProxyModel::rowsRemoved); + QSignalSpy insertSpy(&proxy, &QSortFilterProxyModel::rowsInserted); + QSignalSpy aboutToRemoveSpy(&proxy, &QSortFilterProxyModel::rowsAboutToBeRemoved); + QSignalSpy aboutToInsertSpy(&proxy, &QSortFilterProxyModel::rowsAboutToBeInserted); + + QVERIFY(removeSpy.isValid()); + QVERIFY(insertSpy.isValid()); + QVERIFY(aboutToRemoveSpy.isValid()); + QVERIFY(aboutToInsertSpy.isValid()); + + model.replaceData(replacementSourceItems); + + QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count()); + for (int i = 0; i < aboutToRemoveSpy.count(); ++i) { + QList<QVariant> args = aboutToRemoveSpy.at(i); + QCOMPARE(args.at(1).type(), QVariant::Int); + QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); + } + QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count()); + for (int i = 0; i < removeSpy.count(); ++i) { + QList<QVariant> args = removeSpy.at(i); + QCOMPARE(args.at(1).type(), QVariant::Int); + QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); + } + + QCOMPARE(insertSpy.count(), 0); + QCOMPARE(aboutToInsertSpy.count(), 0); + + QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count()); + for (int i = 0; i < expectedProxyItems.count(); ++i) { + QModelIndex pindex = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i)); + } +} + void tst_QSortFilterProxyModel::dynamicFilterWithoutSort() { QStringListModel model; diff --git a/tests/auto/corelib/kernel/qobject/.gitignore b/tests/auto/corelib/kernel/qobject/.gitignore index 7970e32c8f..d609065333 100644 --- a/tests/auto/corelib/kernel/qobject/.gitignore +++ b/tests/auto/corelib/kernel/qobject/.gitignore @@ -1,3 +1,7 @@ tst_qobject -signalbug/signalbug -signalbug/signalbug.exe +signalbug_helper +signalbug_helper.exe +debug/signalbug_helper +release/signalbug_helper +debug/signalbug_helper.exe +release/signalbug_helper.exe diff --git a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro index cc51b4c661..755fecbd04 100644 --- a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro +++ b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro @@ -1,6 +1,15 @@ -CONFIG -= app_bundle debug_and_release +CONFIG -= app_bundle CONFIG += console -DESTDIR = ./ +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/signalbug_helper + } else { + TARGET = ../../release/signalbug_helper + } +} else { + TARGET = ../signalbug_helper +} + QT = core HEADERS += signalbug.h diff --git a/tests/auto/corelib/kernel/qobject/test/test.pro b/tests/auto/corelib/kernel/qobject/test/test.pro index 4e77cb48c5..be15074523 100644 --- a/tests/auto/corelib/kernel/qobject/test/test.pro +++ b/tests/auto/corelib/kernel/qobject/test/test.pro @@ -1,10 +1,29 @@ CONFIG += testcase console -TARGET = ../tst_qobject +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qobject + } else { + TARGET = ../../release/tst_qobject + } +} else { + TARGET = ../tst_qobject +} + QT = core-private network testlib SOURCES = ../tst_qobject.cpp # Force C++17 if available (needed due to P0012R1) contains(QT_CONFIG, c++1z): CONFIG += c++1z -!winrt: TEST_HELPER_INSTALLS = ../signalbug/signalbug +!winrt { + debug_and_release { + CONFIG(debug, debug|release) { + TEST_HELPER_INSTALLS = ../debug/signalbug_helper + } else { + TEST_HELPER_INSTALLS = ../release/signalbug_helper + } + } else { + TEST_HELPER_INSTALLS = ../signalbug_helper + } +} DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index c734cfe4dd..fdacf83eb2 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -55,7 +55,6 @@ class tst_QObject : public QObject { Q_OBJECT private slots: - void initTestCase(); void disconnect(); void connectSlotsByName(); void connectSignalsToSignalsWithDefaultArguments(); @@ -145,6 +144,7 @@ private slots: void disconnectDoesNotLeakFunctor(); void contextDoesNotLeakFunctor(); void connectBase(); + void connectWarnings(); void qmlConnect(); void exceptions(); void noDeclarativeParentChangedOnDestruction(); @@ -282,14 +282,6 @@ static void playWithObjects() } } -void tst_QObject::initTestCase() -{ -#if QT_CONFIG(process) - const QString testDataDir = QFileInfo(QFINDTESTDATA("signalbug")).absolutePath(); - QVERIFY2(QDir::setCurrent(testDataDir), qPrintable("Could not chdir to " + testDataDir)); -#endif -} - void tst_QObject::disconnect() { SenderObject *s = new SenderObject; @@ -3014,7 +3006,7 @@ void tst_QObject::recursiveSignalEmission() #else QProcess proc; // signalbug helper app should always be next to this test binary - const QString path = QStringLiteral("signalbug/signalbug"); + const QString path = QStringLiteral("signalbug_helper"); proc.start(path); QVERIFY2(proc.waitForStarted(), qPrintable(QString::fromLatin1("Cannot start '%1': %2").arg(path, proc.errorString()))); QVERIFY(proc.waitForFinished()); @@ -6685,6 +6677,26 @@ void tst_QObject::connectBase() QCOMPARE( r1.count_slot3, 1 ); } +void tst_QObject::connectWarnings() +{ + SubSender sub; + SenderObject obj; + ReceiverObject r1; + r1.reset(); + + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): invalid null parameter"); + connect(nullptr, &SubSender::signal1, &r1, &ReceiverObject::slot1); + + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SubSender, Unknown): invalid null parameter"); + connect(&sub, &SubSender::signal1, nullptr, &ReceiverObject::slot1); + + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): invalid null parameter"); + connect(nullptr, &SenderObject::signal1, &r1, &ReceiverObject::slot1); + + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, Unknown): invalid null parameter"); + connect(&obj, &SenderObject::signal1, nullptr, &ReceiverObject::slot1); +} + struct QmlReceiver : public QtPrivate::QSlotObjectBase { int callCount; diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index 5b2d77a02c..f004ec7856 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -75,6 +75,7 @@ private slots: void dontBlockEvents(); void postedEventsShouldNotStarveTimers(); + void connectTo(); }; void tst_QTimer::zeroTimer() @@ -979,5 +980,30 @@ void tst_QTimer::crossThreadSingleShotToFunctor() delete o; } +void tst_QTimer::connectTo() +{ + QTimer timer; + QSignalSpy timeoutSpy(&timer, &QTimer::timeout); + timer.setInterval(0); + timer.start(); + + auto context = new QObject(); + + int count = 0; + timer.connectTo([&count] { count++; }); + QMetaObject::Connection connection = timer.connectTo(context, [&count] { count++; }); + timer.connectTo(&timer, &QTimer::stop); + + + QTest::qWait(100); + QCOMPARE(count, 2); + QCOMPARE(timeoutSpy.count(), 1); + + // Test that connection is bound to context lifetime + QVERIFY(connection); + delete context; + QVERIFY(!connection); +} + QTEST_MAIN(tst_QTimer) #include "tst_qtimer.moc" diff --git a/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp b/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp index e2a0c2dad3..ac8aaa1327 100644 --- a/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp +++ b/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp @@ -46,6 +46,7 @@ protected slots: private slots: void simple_data(); void simple(); + void blockedWaiting(); void manyNotifiers(); void disableNotifiersInActivatedSlot_data(); void disableNotifiersInActivatedSlot(); @@ -104,6 +105,26 @@ void tst_QWinEventNotifier::simple() QVERIFY(simpleActivated); } +void tst_QWinEventNotifier::blockedWaiting() +{ + simpleHEvent = CreateEvent(0, true, false, 0); + QWinEventNotifier n(simpleHEvent); + QObject::connect(&n, &QWinEventNotifier::activated, + this, &tst_QWinEventNotifier::simple_activated); + simpleActivated = false; + + SetEvent(simpleHEvent); + QCOMPARE(WaitForSingleObject(simpleHEvent, 1000), WAIT_OBJECT_0); + + n.setEnabled(false); + ResetEvent(simpleHEvent); + n.setEnabled(true); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(QTestEventLoop::instance().timeout()); + QVERIFY(!simpleActivated); +} + class EventWithNotifier : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro b/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro new file mode 100644 index 0000000000..5df331314a --- /dev/null +++ b/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro @@ -0,0 +1,11 @@ +QT = core testlib +TARGET = tst_qcborstreamreader +CONFIG += testcase +SOURCES += \ + tst_qcborstreamreader.cpp + +INCLUDEPATH += \ + ../../../../../src/3rdparty/tinycbor/src \ + ../../../../../src/3rdparty/tinycbor/tests/parser + +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp new file mode 100644 index 0000000000..24d9c7409e --- /dev/null +++ b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp @@ -0,0 +1,1091 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 <QtCore/qcborstream.h> +#include <QtTest> + +class tst_QCborStreamReader : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase_data(); + void basics(); + void clear_data(); + void clear(); + void integers_data(); + void integers(); + void fixed_data(); + void fixed(); + void strings_data(); + void strings(); + void tags_data(); + void tags() { fixed(); } + void emptyContainers_data(); + void emptyContainers(); + + void arrays_data(); + void arrays(); + void maps_data() { arrays_data(); } + void maps(); + void undefLengthArrays_data() { arrays_data(); } + void undefLengthArrays(); + void undefLengthMaps_data() { arrays_data(); } + void undefLengthMaps(); + + void next_data() { arrays_data(); } + void next(); + void validation_data(); + void validation(); + void recursionLimit_data(); + void recursionLimit(); + + void addData_singleElement_data(); + void addData_singleElement(); + void addData_complex_data() { arrays_data(); } + void addData_complex(); +}; + +#define FOR_CBOR_TYPE(F) \ + F(QCborStreamReader::UnsignedInteger) \ + F(QCborStreamReader::NegativeInteger) \ + F(QCborStreamReader::ByteArray) \ + F(QCborStreamReader::String) \ + F(QCborStreamReader::Array) \ + F(QCborStreamReader::Map) \ + F(QCborStreamReader::Tag) \ + F(QCborStreamReader::SimpleType) \ + F(QCborStreamReader::Float16) \ + F(QCborStreamReader::Float) \ + F(QCborStreamReader::Double) \ + F(QCborStreamReader::Invalid) + +QT_BEGIN_NAMESPACE +namespace QTest { +template<> char *toString<QCborStreamReader::Type>(const QCborStreamReader::Type &t) +{ + return qstrdup([=]() { + switch (t) { +#define TYPE(t) \ + case t: return QT_STRINGIFY(t); + FOR_CBOR_TYPE(TYPE) +#undef TYPE + } + return "<huh?>"; + }()); +} +} // namespace QTest +QT_END_NAMESPACE + +// Get the data from TinyCBOR (see src/3rdparty/tinycbor/tests/parser/data.cpp) +#include "data.cpp" + +void tst_QCborStreamReader::initTestCase_data() +{ + QTest::addColumn<bool>("useDevice"); + QTest::newRow("QByteArray") << false; + QTest::newRow("QBuffer") << true; +} + +void tst_QCborStreamReader::basics() +{ + QFETCH_GLOBAL(bool, useDevice); + QBuffer buffer; + QCborStreamReader reader; + + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + QVERIFY(reader.device() != nullptr); + } else { + QCOMPARE(reader.device(), nullptr); + } + + QCOMPARE(reader.currentOffset(), 0); + QCOMPARE(reader.lastError(), QCborError::EndOfFile); + + QCOMPARE(reader.type(), QCborStreamReader::Invalid); + QVERIFY(!reader.isUnsignedInteger()); + QVERIFY(!reader.isNegativeInteger()); + QVERIFY(!reader.isByteArray()); + QVERIFY(!reader.isString()); + QVERIFY(!reader.isArray()); + QVERIFY(!reader.isContainer()); + QVERIFY(!reader.isMap()); + QVERIFY(!reader.isTag()); + QVERIFY(!reader.isSimpleType()); + QVERIFY(!reader.isBool()); + QVERIFY(!reader.isNull()); + QVERIFY(!reader.isUndefined()); + QVERIFY(!reader.isFloat16()); + QVERIFY(!reader.isFloat()); + QVERIFY(!reader.isDouble()); + QVERIFY(!reader.isValid()); + QVERIFY(reader.isInvalid()); + + QCOMPARE(reader.containerDepth(), 0); + QCOMPARE(reader.parentContainerType(), QCborStreamReader::Invalid); + QVERIFY(!reader.hasNext()); + QVERIFY(!reader.next()); + QCOMPARE(reader.lastError(), QCborError::EndOfFile); + + QVERIFY(reader.isLengthKnown()); // well, it's not unknown + QCOMPARE(reader.length(), ~0ULL); + + if (useDevice) { + reader.reparse(); + QVERIFY(reader.device() != nullptr); + } else { + reader.addData(QByteArray()); + QCOMPARE(reader.device(), nullptr); + } + + // nothing changes, we added nothing + QCOMPARE(reader.currentOffset(), 0); + QCOMPARE(reader.lastError(), QCborError::EndOfFile); + + QCOMPARE(reader.type(), QCborStreamReader::Invalid); + QVERIFY(!reader.isUnsignedInteger()); + QVERIFY(!reader.isNegativeInteger()); + QVERIFY(!reader.isByteArray()); + QVERIFY(!reader.isString()); + QVERIFY(!reader.isArray()); + QVERIFY(!reader.isContainer()); + QVERIFY(!reader.isMap()); + QVERIFY(!reader.isTag()); + QVERIFY(!reader.isSimpleType()); + QVERIFY(!reader.isBool()); + QVERIFY(!reader.isNull()); + QVERIFY(!reader.isUndefined()); + QVERIFY(!reader.isFloat16()); + QVERIFY(!reader.isFloat()); + QVERIFY(!reader.isDouble()); + QVERIFY(!reader.isValid()); + QVERIFY(reader.isInvalid()); + + QVERIFY(reader.isLengthKnown()); // well, it's not unknown + QCOMPARE(reader.length(), ~0ULL); + + QCOMPARE(reader.containerDepth(), 0); + QCOMPARE(reader.parentContainerType(), QCborStreamReader::Invalid); + QVERIFY(!reader.hasNext()); + QVERIFY(!reader.next()); + + reader.clear(); + QCOMPARE(reader.device(), nullptr); + QCOMPARE(reader.currentOffset(), 0); + QCOMPARE(reader.lastError(), QCborError::EndOfFile); +} + +void tst_QCborStreamReader::clear_data() +{ + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QCborError>("firstError"); + QTest::addColumn<int>("offsetAfterSkip"); + QTest::newRow("invalid") << QByteArray(512, '\xff') << QCborError{QCborError::UnexpectedBreak} << 0; + QTest::newRow("valid") << QByteArray(512, '\0') << QCborError{QCborError::NoError} << 0; + QTest::newRow("skipped") << QByteArray(512, '\0') << QCborError{QCborError::NoError} << 1; +} + +void tst_QCborStreamReader::clear() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(QCborError, firstError); + QFETCH(int, offsetAfterSkip); + + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QCOMPARE(reader.isValid(), !firstError); + QCOMPARE(reader.currentOffset(), 0); + QCOMPARE(reader.lastError(), firstError); + + if (offsetAfterSkip) { + reader.next(); + QVERIFY(!reader.isValid()); + QCOMPARE(reader.currentOffset(), 1); + QCOMPARE(reader.lastError(), QCborError::NoError); + } + + reader.clear(); + QCOMPARE(reader.device(), nullptr); + QCOMPARE(reader.currentOffset(), 0); + QCOMPARE(reader.lastError(), QCborError::EndOfFile); +} + +void tst_QCborStreamReader::integers_data() +{ + addIntegers(); +} + +void tst_QCborStreamReader::integers() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(bool, isNegative); + QFETCH(quint64, expectedRaw); + QFETCH(qint64, expectedValue); + QFETCH(bool, inInt64Range); + quint64 absolute = (isNegative ? expectedRaw + 1 : expectedRaw); + + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + QVERIFY(reader.isInteger()); + + if (inInt64Range) + QCOMPARE(reader.toInteger(), expectedValue); + if (isNegative) + QCOMPARE(quint64(reader.toNegativeInteger()), absolute); + else + QCOMPARE(reader.toUnsignedInteger(), absolute); +} + +void escapedAppendTo(QString &result, const QByteArray &data) +{ + result += "h'" + QString::fromLatin1(data.toHex()) + '\''; +} + +void escapedAppendTo(QString &result, const QString &data) +{ + result += '"'; + for (int i = 0; i <= data.size(); i += 245) { + // hopefully we won't have a surrogate pair split here + QScopedArrayPointer<char> escaped(QTest::toPrettyUnicode(data.mid(i, 245))); + QLatin1String s(escaped.data() + 1); // skip opening " + s.chop(1); // drop the closing " + result += s; + } + result += '"'; +} + +template <typename S, QCborStreamReader::StringResult<S> (QCborStreamReader:: *Decoder)()> +static QString parseOneString_helper(QCborStreamReader &reader) +{ + QString result; + bool parens = !reader.isLengthKnown(); + if (parens) + result += '('; + + auto r = (reader.*Decoder)(); + const char *comma = ""; + while (r.status == QCborStreamReader::Ok) { + result += comma; + escapedAppendTo(result, r.data); + + r = (reader.*Decoder)(); + comma = ", "; + } + + if (r.status == QCborStreamReader::Error) + return QString(); + + if (parens) + result += ')'; + return result; +} + +static QString parseOneByteArray(QCborStreamReader &reader) +{ + return parseOneString_helper<QByteArray, &QCborStreamReader::readByteArray>(reader); +} + +static QString parseOneString(QCborStreamReader &reader) +{ + return parseOneString_helper<QString, &QCborStreamReader::readString>(reader); +} + +static QString makeNegativeString(QCborNegativeInteger n) +{ + return n == QCborNegativeInteger(0) ? + QString("-18446744073709551616") : + QString("-%1").arg(quint64(n)); +} + +template <typename T> static inline bool canConvertTo(double v) +{ + // The [conv.fpint] (7.10 Floating-integral conversions) section of the + // standard says only exact conversions are guaranteed. Converting + // integrals to floating-point with loss of precision has implementation- + // defined behavior whether the next higher or next lower is returned; + // converting FP to integral is UB if it can't be represented.; + Q_STATIC_ASSERT(std::numeric_limits<T>::is_integer); + + double supremum = ldexp(1, std::numeric_limits<T>::digits); + if (v >= supremum) + return false; + + if (v < std::numeric_limits<T>::min()) // either zero or a power of two, so it's exact + return false; + + // we're in range + return v == floor(v); +} + +static QString makeFpString(double v) +{ + if (canConvertTo<qint64>(v)) + return QString::number(qint64(v)) + '.'; + if (canConvertTo<quint64>(v)) + return QString::number(quint64(v)) + '.'; + + QString s = QString::number(v, 'g', std::numeric_limits<double>::digits10 + 2); + if (!s.contains('.') && !s.contains('e') && !qIsInf(v) && !qIsNaN(v)) + s += '.'; + return s; +} + +static QString makeFpString(float v) +{ + if (qIsInf(v)) + return v > 0 ? "inf" : "-inf"; + if (qIsNaN(v)) + return "nan"; + return makeFpString(double(v)) + 'f'; +} + +static QString makeFpString(qfloat16 v) +{ + if (qIsInf(v)) + return v > 0 ? "inf" : "-inf"; + if (qIsNaN(v)) + return "nan"; + return makeFpString(double(v)) + "f16"; +} + +static QString parseOne(QCborStreamReader &reader) +{ + QString result; + + switch (reader.type()) { + case QCborStreamReader::UnsignedInteger: + result = QString::number(reader.toUnsignedInteger()); + break; + case QCborStreamReader::NegativeInteger: + result = makeNegativeString(reader.toNegativeInteger()); + break; + case QCborStreamReader::ByteArray: + return parseOneByteArray(reader); + case QCborStreamReader::String: + return parseOneString(reader); + case QCborStreamReader::Array: + case QCborStreamReader::Map: { + const char *delimiters = (reader.isArray() ? "[]" : "{}"); + result += delimiters[0]; + reader.enterContainer(); + + QLatin1String comma(""); + while (reader.lastError() == QCborError::NoError && reader.hasNext()) { + result += comma + parseOne(reader); + comma = QLatin1String(", "); + + if (reader.parentContainerType() == QCborStreamReader::Map + && reader.lastError() == QCborError::NoError) + result += ": " + parseOne(reader); + } + + if (reader.isValid()) + return QString(); + if (reader.lastError() != QCborError::NoError) + return QString(); + reader.leaveContainer(); + result += delimiters[1]; + return result; + } + case QCborStreamReader::Tag: { + QCborTag tag = reader.toTag(); + if (!reader.next()) + return QString(); + return QString("%1(%2)").arg(quint64(tag)).arg(parseOne(reader)); + } + case QCborStreamReader::SimpleType: + switch (reader.toSimpleType()) { + case QCborSimpleType::False: + result = QStringLiteral("false"); + break; + case QCborSimpleType::True: + result = QStringLiteral("true"); + break; + case QCborSimpleType::Null: + result = QStringLiteral("null"); + break; + case QCborSimpleType::Undefined: + result = QStringLiteral("undefined"); + break; + default: + result = QString("simple(%1)").arg(quint8(reader.toSimpleType())); + break; + } + break; + case QCborStreamReader::Float16: + result = makeFpString(reader.toFloat16()); + break; + case QCborStreamReader::Float: + result = makeFpString(reader.toFloat()); + break; + case QCborStreamReader::Double: + result = makeFpString(reader.toDouble()); + break; + case QCborStreamReader::Invalid: + return QStringLiteral("<invalid>"); + } + + if (!reader.next()) + return QString(); + return result; +} + +bool parseNonRecursive(QString &result, bool &printingStringChunks, QCborStreamReader &reader) +{ + while (reader.lastError() == QCborError::NoError) { + if (!reader.hasNext()) { + if (result.endsWith(", ")) + result.chop(2); + if (reader.containerDepth() == 0) + return true; + result += reader.parentContainerType() == QCborStreamReader::Map ? "}, " : "], "; + reader.leaveContainer(); + continue; + } + + switch (reader.type()) { + case QCborStreamReader::UnsignedInteger: + result += QString::number(reader.toUnsignedInteger()); + break; + case QCborStreamReader::NegativeInteger: + result += makeNegativeString(reader.toNegativeInteger()); + break; + case QCborStreamReader::ByteArray: + case QCborStreamReader::String: { + QCborStreamReader::StringResultCode status; + if (!printingStringChunks && !reader.isLengthKnown()) { + printingStringChunks = true; + result += '('; + } + if (reader.isByteArray()) { + auto r = reader.readByteArray(); + status = r.status; + if (r.status == QCborStreamReader::Ok) + escapedAppendTo(result, r.data); + } else { + auto r = reader.readString(); + status = r.status; + if (r.status == QCborStreamReader::Ok) + escapedAppendTo(result, r.data); + } + + if (status == QCborStreamReader::EndOfString) { + if (result.endsWith(", ")) + result.chop(2); + if (printingStringChunks) + result += ')'; + result += ", "; + printingStringChunks = false; + } + if (status == QCborStreamReader::Ok && printingStringChunks) + result += ", "; + + continue; + } + case QCborStreamReader::Array: + result += '['; + reader.enterContainer(); + continue; + case QCborStreamReader::Map: + result += '{'; + reader.enterContainer(); + continue; + case QCborStreamReader::Tag: + result += QString("Tag:%1:").arg(quint64(reader.toTag())); + reader.next(); + continue; // skip the comma + case QCborStreamReader::SimpleType: + switch (reader.toSimpleType()) { + case QCborSimpleType::False: + result += QStringLiteral("false"); + break; + case QCborSimpleType::True: + result += QStringLiteral("true"); + break; + case QCborSimpleType::Null: + result += QStringLiteral("null"); + break; + case QCborSimpleType::Undefined: + result += QStringLiteral("undefined"); + break; + default: + result += QString("simple(%1)").arg(quint8(reader.toSimpleType())); + break; + } + break; + case QCborStreamReader::Float16: + result += makeFpString(reader.toFloat16()); + break; + case QCborStreamReader::Float: + result += makeFpString(reader.toFloat()); + break; + case QCborStreamReader::Double: + result += makeFpString(reader.toDouble()); + break; + case QCborStreamReader::Invalid: + break; + } + + reader.next(); + result += ", "; + } + return false; +}; + + +static QString &removeIndicators(QString &str) +{ + // remove any CBOR encoding indicators from the string, since parseOne above + // doesn't produce them + static QRegularExpression rx("_(\\d+)? ?"); + return str.replace(rx, QString()); +} + +void tst_QCborStreamReader::fixed_data() +{ + addColumns(); + addFixedData(); +} + +void tst_QCborStreamReader::fixed() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + QCOMPARE(parseOne(reader), expected); + + // verify that we can re-read + reader.reset(); + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + QCOMPARE(parseOne(reader), expected); +} + +void tst_QCborStreamReader::strings_data() +{ + addColumns(); + addStringsData(); +} + +void tst_QCborStreamReader::strings() +{ + fixed(); + if (QTest::currentTestFailed()) + return; + + // Extra string checks: + // We'll compare the reads using readString() and readByteArray() + // (henceforth "control data" because fixed() above tested them) with those + // obtained with readStringChunk(). + + QFETCH(QByteArray, data); + QFETCH(QString, expected); + QFETCH_GLOBAL(bool, useDevice); + bool isChunked = expected.startsWith('('); + + QBuffer buffer(&data), controlBuffer(&data); + QCborStreamReader reader(data), controlReader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + controlBuffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + controlReader.setDevice(&controlBuffer); + } + QVERIFY(reader.isString() || reader.isByteArray()); + QCOMPARE(reader.isLengthKnown(), !isChunked); + + if (!isChunked) + QCOMPARE(reader.currentStringChunkSize(), qsizetype(reader.length())); + + int chunks = 0; + forever { + QCborStreamReader::StringResult<QByteArray> controlData; + if (reader.isString()) { + auto r = controlReader.readString(); + controlData.data = r.data.toUtf8(); + controlData.status = r.status; + } else { + controlData = controlReader.readByteArray(); + } + QVERIFY(controlData.status != QCborStreamReader::Error); + + for (int i = 0; i < 10; ++i) { + // this call must work several times with the same result + QCOMPARE(reader.currentStringChunkSize(), controlData.data.size()); + } + + QByteArray chunk(controlData.data.size(), Qt::Uninitialized); + auto r = reader.readStringChunk(chunk.data(), chunk.size()); + QCOMPARE(r.status, controlData.status); + if (r.status == QCborStreamReader::Ok) + QCOMPARE(r.data, controlData.data.size()); + else + QCOMPARE(r.data, 0); + QCOMPARE(chunk, controlData.data); + + if (r.status == QCborStreamReader::EndOfString) + break; + ++chunks; + } + + if (!isChunked) + QCOMPARE(chunks, 1); +} + +void tst_QCborStreamReader::tags_data() +{ + addColumns(); + addTagsData(); +} + +void tst_QCborStreamReader::emptyContainers_data() +{ + addColumns(); + addEmptyContainersData(); +} + +void tst_QCborStreamReader::emptyContainers() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + if (reader.isLengthKnown()) + QCOMPARE(reader.length(), 0U); + QCOMPARE(parseOne(reader), expected); + + // verify that we can re-read + reader.reset(); + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + if (reader.isLengthKnown()) + QCOMPARE(reader.length(), 0U); + QCOMPARE(parseOne(reader), expected); +} + +void tst_QCborStreamReader::arrays_data() +{ + addColumns(); + addFixedData(); + addStringsData(); + addTagsData(); + addEmptyContainersData(); +} + +static void checkContainer(int len, const QByteArray &data, const QString &expected) +{ + QFETCH_GLOBAL(bool, useDevice); + + QByteArray copy = data; + QBuffer buffer(©); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + if (len >= 0) { + QVERIFY(reader.isLengthKnown()); + QCOMPARE(reader.length(), uint(len)); + } + QCOMPARE(parseOne(reader), expected); + + // verify that we can re-read + reader.reset(); + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + if (len >= 0) { + QVERIFY(reader.isLengthKnown()); + QCOMPARE(reader.length(), uint(len)); + } + QCOMPARE(parseOne(reader), expected); +} + +void tst_QCborStreamReader::arrays() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + checkContainer(1, '\x81' + data, '[' + expected + ']'); + if (QTest::currentTestFailed()) + return; + + checkContainer(2, '\x82' + data + data, '[' + expected + ", " + expected + ']'); +} + +void tst_QCborStreamReader::maps() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + // int keys + checkContainer(1, "\xa1\1" + data, "{1: " + expected + '}'); + if (QTest::currentTestFailed()) + return; + + checkContainer(2, "\xa2\1" + data + '\x20' + data, + "{1: " + expected + ", -1: " + expected + '}'); + if (QTest::currentTestFailed()) + return; + + // string keys + checkContainer(1, "\xa1\x65Hello" + data, "{\"Hello\": " + expected + '}'); + if (QTest::currentTestFailed()) + return; + + checkContainer(2, "\xa2\x65World" + data + "\x65Hello" + data, + "{\"World\": " + expected + ", \"Hello\": " + expected + '}'); +} + +void tst_QCborStreamReader::undefLengthArrays() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + checkContainer(-1, '\x9f' + data + '\xff', '[' + expected + ']'); + if (QTest::currentTestFailed()) + return; + + checkContainer(-2, '\x9f' + data + data + '\xff', '[' + expected + ", " + expected + ']'); +} + +void tst_QCborStreamReader::undefLengthMaps() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + // int keys + checkContainer(-1, "\xbf\1" + data + '\xff', "{1: " + expected + '}'); + if (QTest::currentTestFailed()) + return; + + checkContainer(-2, "\xbf\1" + data + '\x20' + data + '\xff', + "{1: " + expected + ", -1: " + expected + '}'); + if (QTest::currentTestFailed()) + return; + + // string keys + checkContainer(-1, "\xbf\x65Hello" + data + '\xff', "{\"Hello\": " + expected + '}'); + if (QTest::currentTestFailed()) + return; + + checkContainer(-2, "\xbf\x65World" + data + "\x65Hello" + data + '\xff', + "{\"World\": " + expected + ", \"Hello\": " + expected + '}'); +} + +void tst_QCborStreamReader::next() +{ + QFETCH(QByteArray, data); + + auto doit = [](QByteArray data) { + QFETCH_GLOBAL(bool, useDevice); + + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + return reader.next(); + }; + + QVERIFY(doit('\x81' + data)); + QVERIFY(doit('\x82' + data + data)); + QVERIFY(doit('\x9f' + data + '\xff')); + QVERIFY(doit("\x81\x9f" + data + '\xff')); + QVERIFY(doit("\x9f\x81" + data + '\xff')); + + QVERIFY(doit("\xa1\1" + data)); + QVERIFY(doit("\xa2\1" + data + '\x20' + data)); + QVERIFY(doit("\xbf\1" + data + '\xff')); + QVERIFY(doit("\xbf\x9f\1\xff\x9f" + data + "\xff\xff")); +} + +void tst_QCborStreamReader::validation_data() +{ + addValidationColumns(); + addValidationData(); +} + +void tst_QCborStreamReader::validation() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + parseOne(reader); + QVERIFY(reader.lastError() != QCborError::NoError); + + // next() should fail + reader.reset(); + QVERIFY(!reader.next()); + QVERIFY(reader.lastError() != QCborError::NoError); +} + +static const int Recursions = 3; +void tst_QCborStreamReader::recursionLimit_data() +{ + static const int recursions = Recursions + 2; + QTest::addColumn<QByteArray>("data"); + + QTest::newRow("array") << QByteArray(recursions, '\x81') + '\x20'; + QTest::newRow("_array") << QByteArray(recursions, '\x9f') + '\x20' + QByteArray(recursions, '\xff'); + + QByteArray data; + for (int i = 0; i < recursions; ++i) + data += "\xa1\x65Hello"; + data += '\2'; + QTest::newRow("map-recursive-values") << data; + + data.clear(); + for (int i = 0; i < recursions; ++i) + data += "\xbf\x65World"; + data += '\2'; + for (int i = 0; i < recursions; ++i) + data += "\xff"; + QTest::newRow("_map-recursive-values") << data; + + data = QByteArray(recursions, '\xa1'); + data += '\2'; + for (int i = 0; i < recursions; ++i) + data += "\x7f\x64quux\xff"; + QTest::newRow("map-recursive-keys") << data; + + data = QByteArray(recursions, '\xbf'); + data += '\2'; + for (int i = 0; i < recursions; ++i) + data += "\1\xff"; + QTest::newRow("_map-recursive-keys") << data; + + data.clear(); + for (int i = 0; i < recursions / 2; ++i) + data += "\x81\xa1\1"; + data += '\2'; + QTest::newRow("mixed") << data; +} + +void tst_QCborStreamReader::recursionLimit() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + + data.prepend('\x81'); + QBuffer buffer(&data); + QCborStreamReader reader(data); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + + // verify that it works normally: + QVERIFY(reader.enterContainer()); + QVERIFY(reader.next()); + QVERIFY(!reader.hasNext()); + + reader.reset(); + QVERIFY(reader.enterContainer()); + QVERIFY(!reader.next(Recursions)); + QCOMPARE(reader.lastError(), QCborError::NestingTooDeep); +} + +void tst_QCborStreamReader::addData_singleElement_data() +{ + addColumns(); + addFixedData(); + addNonChunkedStringsData(); +} + +void tst_QCborStreamReader::addData_singleElement() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + QByteArray growing; + QBuffer buffer(&growing); + QCborStreamReader reader; + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + for (int i = 0; i < data.size() - 1; ++i) { + // add one byte from the data + if (useDevice) { + growing.append(data.at(i)); + reader.reparse(); + } else { + reader.addData(data.constData() + i, 1); + } + + parseOne(reader); + QCOMPARE(reader.lastError(), QCborError::EndOfFile); + } + + // add the last byte + if (useDevice) { + growing.append(data.right(1)); + reader.reparse(); + } else { + reader.addData(data.right(1)); + } + QCOMPARE(reader.lastError(), QCborError::NoError); + QCOMPARE(parseOne(reader), expected); +} + +void tst_QCborStreamReader::addData_complex() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + // transform tags (parseNonRecursive can't produce the usual form) + expected.replace(QRegularExpression(R"/((\d+)\(([^)]+)\))/"), "Tag:\\1:\\2"); + + auto doit = [](const QByteArray &data) { + QFETCH_GLOBAL(bool, useDevice); + + // start with one byte + int added = 1; + QByteArray growing = data.left(added); + QBuffer buffer(&growing); + QCborStreamReader reader(growing); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + + QString result; + bool printingStringChunks = false; + forever { + if (parseNonRecursive(result, printingStringChunks, reader)) + return result; + if (reader.lastError() != QCborError::EndOfFile) + return reader.lastError().toString(); + + while (reader.lastError() == QCborError::EndOfFile) { + // add more data + if (added == data.size()) + return QStringLiteral("Couldn't parse even with all data"); + + if (useDevice) { + growing.append(data.at(added)); + reader.reparse(); + } else { + reader.addData(data.constData() + added, 1); + } + ++added; + } + } + }; + + // plain: + QCOMPARE(doit(data), expected); + + // in an array + QCOMPARE(doit('\x81' + data), '[' + expected + ']'); + QCOMPARE(doit('\x82' + data + data), '[' + expected + ", " + expected + ']'); + + QCOMPARE(doit('\x9f' + data + '\xff'), '[' + expected + ']'); + QCOMPARE(doit('\x9f' + data + data + '\xff'), '[' + expected + ", " + expected + ']'); + + // in a map + QCOMPARE(doit("\xa1\x01" + data), "{1, " + expected + '}'); + QCOMPARE(doit("\xa1\x65Hello" + data), "{\"Hello\", " + expected + '}'); + QCOMPARE(doit("\xa1\x7f\x65Hello\x65World\xff" + data), "{(\"Hello\", \"World\"), " + expected + '}'); + QCOMPARE(doit("\xa2\x01" + data + "\x65Hello" + data), + "{1, " + expected + ", \"Hello\", " + expected + '}'); + + QCOMPARE(doit("\xbf\x01" + data + '\xff'), "{1, " + expected + '}'); + + // mixed + QCOMPARE(doit("\xbf\x01\x81" + data + '\xff'), "{1, [" + expected + "]}"); + QCOMPARE(doit("\xbf\x01\x82" + data + data + '\xff'), + "{1, [" + expected + ", " + expected + "]}"); + QCOMPARE(doit("\xbf\x01\x9f" + data + data + "\xff\xff"), + "{1, [" + expected + ", " + expected + "]}"); +} + + +QTEST_MAIN(tst_QCborStreamReader) + +#include "tst_qcborstreamreader.moc" diff --git a/tests/auto/corelib/serialization/qcborstreamwriter/qcborstreamwriter.pro b/tests/auto/corelib/serialization/qcborstreamwriter/qcborstreamwriter.pro new file mode 100644 index 0000000000..3391b5a296 --- /dev/null +++ b/tests/auto/corelib/serialization/qcborstreamwriter/qcborstreamwriter.pro @@ -0,0 +1,8 @@ +QT = core testlib +TARGET = tst_qcborstreamwriter +CONFIG += testcase +SOURCES += \ + tst_qcborstreamwriter.cpp + +INCLUDEPATH += ../../../../../src/3rdparty/tinycbor/tests/encoder +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp b/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp new file mode 100644 index 0000000000..6995b4d08b --- /dev/null +++ b/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 <QtTest> + +class tst_QCborStreamWriter : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase_data(); + void fixed_data(); + void fixed(); + void strings_data(); + void strings() { fixed(); } + void nonAsciiStrings_data(); + void nonAsciiStrings(); + void arraysAndMaps_data(); + void arraysAndMaps() { fixed(); } + void tags_data(); + void tags(); + void arrays_data() { tags_data(); } + void arrays(); + void maps_data() { tags_data(); } + void maps(); +}; + +// Get the data from TinyCBOR (see src/3rdparty/tinycbor/tests/encoder/data.cpp) +typedef quint64 CborTag; +#include "data.cpp" + +void encodeVariant(QCborStreamWriter &writer, const QVariant &v) +{ + int type = v.userType(); + switch (type) { + case QVariant::Int: + case QVariant::LongLong: + return writer.append(v.toLongLong()); + + case QVariant::UInt: + case QVariant::ULongLong: + return writer.append(v.toULongLong()); + + case QVariant::Bool: + return writer.append(v.toBool()); + + case QVariant::Invalid: + return writer.appendUndefined(); + + case QMetaType::VoidStar: + return writer.append(nullptr); + + case QVariant::Double: + return writer.append(v.toDouble()); + + case QMetaType::Float: + return writer.append(v.toFloat()); + + case QVariant::String: + return writer.append(v.toString()); + + case QVariant::ByteArray: + return writer.append(v.toByteArray()); + + default: + if (type == qMetaTypeId<NegativeInteger>()) + return writer.append(QCborNegativeInteger(v.value<NegativeInteger>().abs)); + if (type == qMetaTypeId<SimpleType>()) + return writer.append(QCborSimpleType(v.value<SimpleType>().type)); + if (type == qMetaTypeId<qfloat16>()) + return writer.append(v.value<qfloat16>()); + if (type == qMetaTypeId<Tag>()) { + writer.append(QCborTag(v.value<Tag>().tag)); + return encodeVariant(writer, v.value<Tag>().tagged); + } + if (type == QVariant::List || type == qMetaTypeId<IndeterminateLengthArray>()) { + QVariantList list = v.toList(); + if (type == qMetaTypeId<IndeterminateLengthArray>()) { + list = v.value<IndeterminateLengthArray>(); + writer.startArray(); + } else { + writer.startArray(list.length()); + } + for (const QVariant &v2 : qAsConst(list)) + encodeVariant(writer, v2); + QVERIFY(writer.endArray()); + return; + } + if (type == qMetaTypeId<Map>() || type == qMetaTypeId<IndeterminateLengthMap>()) { + Map map = v.value<Map>(); + if (type == qMetaTypeId<IndeterminateLengthMap>()) { + map = v.value<IndeterminateLengthMap>(); + writer.startMap(); + } else { + writer.startMap(map.length()); + } + for (auto pair : qAsConst(map)) { + encodeVariant(writer, pair.first); + encodeVariant(writer, pair.second); + } + QVERIFY(writer.endMap()); + return; + } + } + QFAIL("Shouldn't have got here"); +} + +void compare(const QVariant &input, const QByteArray &output) +{ + QFETCH_GLOBAL(bool, useDevice); + + if (useDevice) { + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QCborStreamWriter writer(&buffer); + encodeVariant(writer, input); + QCOMPARE(buffer.data(), output); + } else { + QByteArray buffer; + QCborStreamWriter writer(&buffer); + encodeVariant(writer, input); + QCOMPARE(buffer, output); + } +} + +void tst_QCborStreamWriter::initTestCase_data() +{ + QTest::addColumn<bool>("useDevice"); + QTest::newRow("QByteArray") << false; + QTest::newRow("QIODevice") << true; +} + +void tst_QCborStreamWriter::fixed_data() +{ + addColumns(); + addFixedData(); +} + +void tst_QCborStreamWriter::fixed() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + compare(input, output); +} + +void tst_QCborStreamWriter::strings_data() +{ + addColumns(); + addStringsData(); +} + +void tst_QCborStreamWriter::nonAsciiStrings_data() +{ + QTest::addColumn<QByteArray>("output"); + QTest::addColumn<QString>("input"); + QTest::addColumn<bool>("isLatin1"); + + QByteArray latin1 = u8"Résumé"; + QTest::newRow("shortlatin1") + << ("\x68" + latin1) << QString::fromUtf8(latin1) << true; + + // replicate it 5 times (total 40 bytes) + latin1 += latin1 + latin1 + latin1 + latin1; + QTest::newRow("longlatin1") + << ("\x78\x28" + latin1) << QString::fromUtf8(latin1) << true; + + QByteArray nonlatin1 = u8"Χαίρετε"; + QTest::newRow("shortnonlatin1") + << ("\x6e" + nonlatin1) << QString::fromUtf8(nonlatin1) << false; + + // replicate it 4 times (total 56 bytes) + nonlatin1 = nonlatin1 + nonlatin1 + nonlatin1 + nonlatin1; + QTest::newRow("longnonlatin1") + << ("\x78\x38" + nonlatin1) << QString::fromUtf8(nonlatin1) << false; +} + +void tst_QCborStreamWriter::nonAsciiStrings() +{ + QFETCH(QByteArray, output); + QFETCH(QString, input); + QFETCH(bool, isLatin1); + QFETCH_GLOBAL(bool, useDevice); + + // will be wrong if !isLatin1 + QByteArray latin1 = input.toLatin1(); + + if (useDevice) { + { + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QCborStreamWriter writer(&buffer); + writer.append(input); + QCOMPARE(buffer.data(), output); + } + + if (isLatin1) { + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QCborStreamWriter writer(&buffer); + writer.append(QLatin1String(latin1.constData(), latin1.size())); + QCOMPARE(buffer.data(), output); + } + } else { + { + QByteArray buffer; + QCborStreamWriter writer(&buffer); + encodeVariant(writer, input); + QCOMPARE(buffer, output); + } + + if (isLatin1) { + QByteArray buffer; + QCborStreamWriter writer(&buffer); + writer.append(QLatin1String(latin1.constData(), latin1.size())); + QCOMPARE(buffer, output); + } + } +} + +void tst_QCborStreamWriter::arraysAndMaps_data() +{ + addColumns(); + addArraysAndMaps(); +} + +void tst_QCborStreamWriter::tags_data() +{ + addColumns(); + addFixedData(); + addStringsData(); + addArraysAndMaps(); +} + +void tst_QCborStreamWriter::tags() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + + compare(QVariant::fromValue(Tag{1, input}), "\xc1" + output); +} + +void tst_QCborStreamWriter::arrays() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + + compare(make_list(input), "\x81" + output); + if (QTest::currentTestFailed()) + return; + + compare(make_list(input, input), "\x82" + output + output); + if (QTest::currentTestFailed()) + return; + + // nested lists + compare(make_list(make_list(input)), "\x81\x81" + output); + if (QTest::currentTestFailed()) + return; + + compare(make_list(make_list(input), make_list(input)), "\x82\x81" + output + "\x81" + output); +} + +void tst_QCborStreamWriter::maps() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + + compare(make_map({{1, input}}), "\xa1\1" + output); + if (QTest::currentTestFailed()) + return; + + compare(make_map({{1, input}, {input, 24}}), "\xa2\1" + output + output + "\x18\x18"); +} + +QTEST_MAIN(tst_QCborStreamWriter) + +#include "tst_qcborstreamwriter.moc" diff --git a/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro b/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro new file mode 100644 index 0000000000..9dd67da1f0 --- /dev/null +++ b/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro @@ -0,0 +1,11 @@ +QT = core testlib +TARGET = tst_qcborvalue +CONFIG += testcase +SOURCES += \ + tst_qcborvalue.cpp + +INCLUDEPATH += \ + ../../../../../src/3rdparty/tinycbor/src \ + ../../../../../src/3rdparty/tinycbor/tests/parser + +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp new file mode 100644 index 0000000000..0cd4c5cca0 --- /dev/null +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -0,0 +1,1490 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 <QtCore/qcborvalue.h> +#include <QtTest> + +Q_DECLARE_METATYPE(QCborValue) +Q_DECLARE_METATYPE(QCborValue::EncodingOptions) + +class tst_QCborValue : public QObject +{ + Q_OBJECT + +private slots: + void basics_data(); + void basics(); + void tagged_data() { basics_data(); } + void tagged(); + void extendedTypes_data(); + void extendedTypes(); + void copyCompare_data() { basics_data(); } + void copyCompare(); + + void arrayDefaultInitialization(); + void arrayEmptyInitializerList(); + void arrayEmptyDetach(); + void arrayInitializerList(); + void arrayMutation(); + void arrayPrepend(); + void arrayInsertRemove_data() { basics_data(); } + void arrayInsertRemove(); + void arrayInsertTagged_data() { basics_data(); } + void arrayInsertTagged(); + void arrayStringElements(); + void arraySelfAssign_data() { basics_data(); } + void arraySelfAssign(); + + void mapDefaultInitialization(); + void mapEmptyInitializerList(); + void mapEmptyDetach(); + void mapSimpleInitializerList(); + void mapMutation(); + void mapStringValues(); + void mapStringKeys(); + void mapInsertRemove_data() { basics_data(); } + void mapInsertRemove(); + void mapInsertTagged_data() { basics_data(); } + void mapInsertTagged(); + void mapSelfAssign_data() { basics_data(); } + void mapSelfAssign(); + void mapComplexKeys_data() { basics_data(); } + void mapComplexKeys(); + + void sorting(); + + void toCbor_data(); + void toCbor(); + void fromCbor_data(); + void fromCbor(); + void validation_data(); + void validation(); +}; + +// Get the validation data from TinyCBOR (see src/3rdparty/tinycbor/tests/parser/data.cpp) +#include "data.cpp" + +struct SimpleTypeWrapper +{ + // QCborSimpleType is an enum, so QVariant knows how to convert it to + // integer and we don't want it to do that. + SimpleTypeWrapper(QCborSimpleType type = {}) : st(type) {} + QCborSimpleType st; +}; +Q_DECLARE_METATYPE(SimpleTypeWrapper) + +void tst_QCborValue::basics_data() +{ + QTest::addColumn<QCborValue::Type>("type"); + QTest::addColumn<QCborValue>("v"); + QTest::addColumn<QVariant>("expectedValue"); + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUuid uuid = QUuid::createUuid(); + + QMetaEnum me = QMetaEnum::fromType<QCborValue::Type>(); + auto add = [me](QCborValue::Type t, const QCborValue &v, const QVariant &exp) { + auto addRow = [=]() -> QTestData & { + const char *typeString = me.valueToKey(t); + if (t == QCborValue::Integer) + return QTest::addRow("Integer:%lld", exp.toLongLong()); + if (t == QCborValue::Double) + return QTest::addRow("Double:%g", exp.toDouble()); + if (t == QCborValue::ByteArray || t == QCborValue::String) + return QTest::addRow("%s:%d", typeString, exp.toString().size()); + return QTest::newRow(typeString); + }; + addRow() << t << v << exp; + }; + auto st = [](QCborSimpleType t) { return QVariant::fromValue<SimpleTypeWrapper>(t); }; + + add(QCborValue::Undefined, QCborValue(), st(QCborSimpleType::Undefined)); + add(QCborValue::Null, QCborValue::Null, st(QCborSimpleType::Null)); + QTest::newRow("nullptr") << QCborValue::Null << QCborValue(nullptr) + << st(QCborSimpleType::Null); + add(QCborValue::False, false, st(QCborSimpleType::False)); + QTest::newRow("false") << QCborValue::False << QCborValue(QCborValue::False) + << st(QCborSimpleType::False); + add(QCborValue::True, true, st(QCborSimpleType::True)); + QTest::newRow("true") << QCborValue::True << QCborValue(QCborValue::True) + << st(QCborSimpleType::True); + QTest::newRow("simpletype") << QCborValue::Type(QCborValue::SimpleType + 255) + << QCborValue(QCborSimpleType(255)) + << st(QCborSimpleType(255)); + add(QCborValue::Integer, 0, 0); + add(QCborValue::Integer, 1, 1); + add(QCborValue::Integer, -1, -1); + add(QCborValue::Integer, std::numeric_limits<qint64>::min(), std::numeric_limits<qint64>::min()); + add(QCborValue::Integer, std::numeric_limits<qint64>::max(), std::numeric_limits<qint64>::max()); + add(QCborValue::Double, 0., 0.); + add(QCborValue::Double, 1.25, 1.25); + add(QCborValue::Double, -1.25, -1.25); + add(QCborValue::Double, qInf(), qInf()); + add(QCborValue::Double, -qInf(), -qInf()); + add(QCborValue::Double, qQNaN(), qQNaN()); + add(QCborValue::ByteArray, QByteArray("Hello"), QByteArray("Hello")); + add(QCborValue::ByteArray, QByteArray(), QByteArray()); + add(QCborValue::String, "Hello", "Hello"); + add(QCborValue::String, QLatin1String(), QString()); + add(QCborValue::DateTime, QCborValue(dt), dt); + add(QCborValue::Url, QCborValue(QUrl("http://example.com")), QUrl("http://example.com")); + add(QCborValue::RegularExpression, QCborValue(QRegularExpression("^.*$")), QRegularExpression("^.*$")); + add(QCborValue::Uuid, QCborValue(uuid), uuid); + + // empty arrays and maps + add(QCborValue::Array, QCborArray(), QVariantList()); + add(QCborValue::Map, QCborMap(), QVariantMap()); +} + +static void basicTypeCheck(QCborValue::Type type, const QCborValue &v, const QVariant &expectedValue) +{ + bool isSimpleType = (expectedValue.userType() == qMetaTypeId<SimpleTypeWrapper>()); + QCborSimpleType st = expectedValue.value<SimpleTypeWrapper>().st; + + QCOMPARE(v.type(), type); + QCOMPARE(v.isInteger(), type == QCborValue::Integer); + QCOMPARE(v.isByteArray(), type == QCborValue::ByteArray); + QCOMPARE(v.isString(), type == QCborValue::String); + QCOMPARE(v.isArray(), type == QCborValue::Array); + QCOMPARE(v.isMap(), type == QCborValue::Map); + QCOMPARE(v.isFalse(), type == QCborValue::False); + QCOMPARE(v.isTrue(), type == QCborValue::True); + QCOMPARE(v.isBool(), type == QCborValue::False || type == QCborValue::True); + QCOMPARE(v.isNull(), type == QCborValue::Null); + QCOMPARE(v.isUndefined(), type == QCborValue::Undefined); + QCOMPARE(v.isDouble(), type == QCborValue::Double); + QCOMPARE(v.isDateTime(), type == QCborValue::DateTime); + QCOMPARE(v.isUrl(), type == QCborValue::Url); + QCOMPARE(v.isUuid(), type == QCborValue::Uuid); + QCOMPARE(v.isInvalid(), type == QCborValue::Invalid); + QCOMPARE(v.isContainer(), type == QCborValue::Array || type == QCborValue::Map); + QCOMPARE(v.isSimpleType(), isSimpleType); + QCOMPARE(v.isSimpleType(QCborSimpleType::False), st == QCborSimpleType::False); + QCOMPARE(v.isSimpleType(QCborSimpleType::True), st == QCborSimpleType::True); + QCOMPARE(v.isSimpleType(QCborSimpleType::Null), st == QCborSimpleType::Null); + QCOMPARE(v.isSimpleType(QCborSimpleType::Undefined), st == QCborSimpleType::Undefined); + QCOMPARE(v.isSimpleType(QCborSimpleType(255)), st == QCborSimpleType(255)); + + if (v.isInteger()) { + QCOMPARE(v.toInteger(), expectedValue.toLongLong()); + QCOMPARE(v.toDouble(), 0. + expectedValue.toLongLong()); + } else { + QCOMPARE(v.toInteger(), qint64(expectedValue.toDouble())); + QCOMPARE(v.toDouble(), expectedValue.toDouble()); + } + QCOMPARE(v.toBool(true), st != QCborSimpleType::False); + QCOMPARE(v.toBool(), st == QCborSimpleType::True); + if (st == QCborSimpleType::Undefined) + QCOMPARE(v.toSimpleType(QCborSimpleType::Null), QCborSimpleType::Undefined); + else if (isSimpleType) + QCOMPARE(v.toSimpleType(), st); + else + QCOMPARE(v.toSimpleType(), QCborSimpleType::Undefined); + +#define CMP(expr, T, validexpr) \ + if (expectedValue.userType() == qMetaTypeId<T>()) \ + QCOMPARE(expr, expectedValue.value<T>()); \ + else \ + QVERIFY(validexpr) + CMP(v.toByteArray(), QByteArray, v.toByteArray().isNull()); + CMP(v.toString(), QString, v.toString().isNull()); + CMP(v.toDateTime(), QDateTime, !v.toDateTime().isValid()); + CMP(v.toUrl(), QUrl, !v.toUrl().isValid()); + CMP(v.toRegularExpression(), QRegularExpression, v.toRegularExpression().pattern().isNull()); + CMP(v.toUuid(), QUuid, v.toUuid().isNull()); +#undef CMP + + QVERIFY(v.toArray().isEmpty()); + QVERIFY(v.toMap().isEmpty()); + + QVERIFY(v["Hello"].isUndefined()); + QVERIFY(v[0].isUndefined()); +} + +void tst_QCborValue::basics() +{ + QFETCH(QCborValue::Type, type); + QFETCH(QCborValue, v); + QFETCH(QVariant, expectedValue); + + basicTypeCheck(type, v, expectedValue); +} + +void tst_QCborValue::tagged() +{ + QFETCH(QCborValue::Type, type); + QFETCH(QCborValue, v); + QFETCH(QVariant, expectedValue); + + // make it tagged + QCborValue tagged(QCborKnownTags::Signature, v); + QVERIFY(tagged.isTag()); + QCOMPARE(tagged.tag(), QCborTag(QCborKnownTags::Signature)); + + // shouldn't compare equal + QVERIFY(tagged != v); + QVERIFY(v != tagged); + + // ensure we can reach the original value + basicTypeCheck(type, tagged.taggedValue(), expectedValue); + QVERIFY(tagged.taggedValue() == v); + QVERIFY(v == tagged.taggedValue()); + + // nested tagging should work too + QCborValue tagged2(QCborKnownTags::EncodedCbor, tagged); + QVERIFY(tagged2.isTag()); + QCOMPARE(tagged2.tag(), QCborTag(QCborKnownTags::EncodedCbor)); + + QVERIFY(tagged2 != tagged); + QVERIFY(tagged != tagged2); + + QVERIFY(tagged2.taggedValue() == tagged); + QVERIFY(tagged == tagged2.taggedValue()); + QVERIFY(tagged2.taggedValue().taggedValue() == v); + QVERIFY(v == tagged2.taggedValue().taggedValue()); +} + +void tst_QCborValue::extendedTypes_data() +{ + QTest::addColumn<QCborValue>("extended"); + QTest::addColumn<QCborValue>("tagged"); + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUuid uuid = QUuid::createUuid(); + + QTest::newRow("DateTime") << QCborValue(dt) + << QCborValue(QCborKnownTags::DateTimeString, dt.toString(Qt::ISODateWithMs)); + QTest::newRow("Url:Empty") << QCborValue(QUrl()) + << QCborValue(QCborKnownTags::Url, QString()); + QTest::newRow("Url:Authority") << QCborValue(QUrl("https://example.com")) + << QCborValue(QCborKnownTags::Url, "https://example.com"); + QTest::newRow("Url:Path") << QCborValue(QUrl("file:///tmp/none")) + << QCborValue(QCborKnownTags::Url, "file:///tmp/none"); + QTest::newRow("Url:QueryFragment") << QCborValue(QUrl("whatever:?a=b&c=d#e")) + << QCborValue(QCborKnownTags::Url, "whatever:?a=b&c=d#e"); + QTest::newRow("Regex:Empty") << QCborValue(QRegularExpression()) + << QCborValue(QCborKnownTags::RegularExpression, QString()); + QTest::newRow("Regex") << QCborValue(QRegularExpression("^.*$")) + << QCborValue(QCborKnownTags::RegularExpression, QString("^.*$")); + QTest::newRow("Uuid") << QCborValue(uuid) + << QCborValue(QCborKnownTags::Uuid, uuid.toRfc4122()); +} + +void tst_QCborValue::extendedTypes() +{ + QFETCH(QCborValue, extended); + QFETCH(QCborValue, tagged); + QVERIFY(!extended.isTag()); + QVERIFY(tagged.isTag()); + + // despite that, they actually compare equal + QVERIFY(extended == tagged); + QVERIFY(tagged == extended); +} + +void tst_QCborValue::copyCompare() +{ + QFETCH(QCborValue, v); + QCborValue other = v; + other = v; + v = other; + + QCOMPARE(v.compare(other), 0); + QCOMPARE(v, other); + QVERIFY(!(v != other)); + QVERIFY(!(v < other)); +#if QT_HAS_INCLUDE(<compare>) + QVERIFY(v <= other); + QVERIFY(v >= other); + QVERIFY(!(v > other)); +#endif + + if (v.isUndefined()) + other = nullptr; + else + other = {}; + QVERIFY(v.type() != other.type()); + QVERIFY(!(v == other)); + QVERIFY(v != other); + + // they're different types, so they can't compare equal + QVERIFY(v.compare(other) != 0); + QVERIFY((v < other) || (other < v)); +} + +void tst_QCborValue::arrayDefaultInitialization() +{ + QCborArray a; + QVERIFY(a.isEmpty()); + QCOMPARE(a.size(), 0); + QVERIFY(!a.contains(0)); + QVERIFY(!a.contains(-1)); + QVERIFY(!a.contains(false)); + QVERIFY(!a.contains(true)); + QVERIFY(!a.contains(nullptr)); + QVERIFY(!a.contains({})); + QVERIFY(!a.contains(1.0)); + QVERIFY(!a.contains(QByteArray("Hello"))); + QVERIFY(!a.contains("Hello")); + QVERIFY(!a.contains(QCborArray())); + QVERIFY(!a.contains(QCborMap())); + QVERIFY(!a.contains(QCborValue(QDateTime::currentDateTimeUtc()))); + QVERIFY(!a.contains(QCborValue(QUrl("http://example.com")))); + QVERIFY(!a.contains(QCborValue(QUuid::createUuid()))); + + QVERIFY(a.at(0).isUndefined()); + QCOMPARE(a.constBegin(), a.constEnd()); + + QVERIFY(a == a); + QVERIFY(a == QCborArray()); + QVERIFY(QCborArray() == a); + + QCborValue v(a); + QVERIFY(v.isArray()); + QVERIFY(!v.isMap()); + QVERIFY(!v.isTag()); + QVERIFY(v[0].isUndefined()); + + QCborArray a2 = v.toArray(); + QVERIFY(a2.isEmpty()); + QCOMPARE(a2, a); +} + +void tst_QCborValue::mapDefaultInitialization() +{ + QCborMap m; + QVERIFY(m.isEmpty()); + QCOMPARE(m.size(), 0); + QVERIFY(m.keys().isEmpty()); + QVERIFY(!m.contains(0)); + QVERIFY(!m.contains(-1)); + QVERIFY(!m.contains(false)); + QVERIFY(!m.contains(true)); + QVERIFY(!m.contains(QCborValue::Null)); + QVERIFY(!m.contains({})); + QVERIFY(!m.contains(1.0)); + QVERIFY(!m.contains(QLatin1String("Hello"))); + QVERIFY(!m.contains(QStringLiteral("Hello"))); + QVERIFY(!m.contains(QCborValue(QByteArray("Hello")))); + QVERIFY(!m.contains(QCborArray())); + QVERIFY(!m.contains(QCborMap())); + QVERIFY(!m.contains(QCborValue(QDateTime::currentDateTimeUtc()))); + QVERIFY(!m.contains(QCborValue(QUrl("http://example.com")))); + QVERIFY(!m.contains(QCborValue(QUuid::createUuid()))); + + QVERIFY(m.value(0).isUndefined()); + QVERIFY(m.value(QLatin1String("Hello")).isUndefined()); + QVERIFY(m.value(QStringLiteral("Hello")).isUndefined()); + QVERIFY(m.value(QCborValue()).isUndefined()); + + QVERIFY(m == m); + QVERIFY(m == QCborMap{}); + QVERIFY(QCborMap{} == m); + + QCborValue v(m); + QVERIFY(v.isMap()); + QVERIFY(!v.isArray()); + QVERIFY(!v.isTag()); + QVERIFY(v[0].isUndefined()); + QVERIFY(v[QLatin1String("Hello")].isUndefined()); + QVERIFY(v["Hello"].isUndefined()); + + QCborMap m2 = v.toMap(); + QVERIFY(m2.isEmpty()); + QCOMPARE(m2.size(), 0); + QCOMPARE(m2, m); +} + +void tst_QCborValue::arrayEmptyInitializerList() +{ + QCborArray a{}; + QVERIFY(a.isEmpty()); + QCOMPARE(a.size(), 0); + QVERIFY(a == a); + QVERIFY(a == QCborArray()); + QVERIFY(QCborArray() == a); +} + +void tst_QCborValue::mapEmptyInitializerList() +{ + QCborMap m{}; + QVERIFY(m.isEmpty()); + QCOMPARE(m.size(), 0); + QVERIFY(m == m); + QVERIFY(m == QCborMap{}); + QVERIFY(QCborMap{} == m); +} + +void tst_QCborValue::arrayEmptyDetach() +{ + QCborArray a; + QCOMPARE(a.begin(), a.end()); + QVERIFY(a.isEmpty()); + QCOMPARE(a.size(), 0); + + QVERIFY(a == a); + QVERIFY(a == QCborArray()); + QVERIFY(QCborArray() == a); + + QCborValue v(a); + QVERIFY(v.isArray()); + QVERIFY(!v.isMap()); + QVERIFY(!v.isTag()); + + QCborArray a2 = v.toArray(); + QVERIFY(a2.isEmpty()); + QCOMPARE(a2, a); +} + +void tst_QCborValue::mapEmptyDetach() +{ + QCborMap m; + QCOMPARE(m.begin(), m.end()); + QVERIFY(m.isEmpty()); + QCOMPARE(m.size(), 0); + + QVERIFY(m == m); + QVERIFY(m == QCborMap{}); + QVERIFY(QCborMap{} == m); + + QCborValue v(m); + QVERIFY(v.isMap()); + QVERIFY(!v.isArray()); + QVERIFY(!v.isTag()); + + QCborMap m2 = v.toMap(); + QVERIFY(m2.isEmpty()); + QCOMPARE(m2, m); +} + +void tst_QCborValue::arrayInitializerList() +{ + QCborArray a{0, -1, false, true, nullptr, {}, 1.0}; + QVERIFY(!a.isEmpty()); + QCOMPARE(a.size(), 7); + QCOMPARE(a.at(0), QCborValue(0)); + QCOMPARE(a.at(1), QCborValue(-1)); + QCOMPARE(a.at(2), QCborValue(QCborValue::False)); + QCOMPARE(a.at(3), QCborValue(QCborValue::True)); + QCOMPARE(a.at(4), QCborValue(QCborValue::Null)); + QCOMPARE(a.at(5), QCborValue(QCborValue::Undefined)); + QCOMPARE(a.at(6), QCborValue(1.0)); + + QVERIFY(a == a); + QVERIFY(a != QCborArray{}); + QVERIFY(QCborArray{} != a); + QVERIFY(a == QCborArray({0, -1, false, true, nullptr, {}, 1.0})); + + QCborValue v = a; + QCOMPARE(v[0], QCborValue(0)); + QCOMPARE(v[1], QCborValue(-1)); + QCOMPARE(v[2], QCborValue(QCborValue::False)); + QCOMPARE(v[3], QCborValue(QCborValue::True)); + QCOMPARE(v[4], QCborValue(QCborValue::Null)); + QCOMPARE(v[5], QCborValue(QCborValue::Undefined)); + QCOMPARE(v[6], QCborValue(1.0)); + + QVERIFY(a.contains(0)); + QVERIFY(a.contains(-1)); + QVERIFY(a.contains(false)); + QVERIFY(a.contains(true)); + QVERIFY(a.contains(nullptr)); + QVERIFY(a.contains({})); + QVERIFY(a.contains(1.0)); + QVERIFY(!a.contains(QByteArray("Hello"))); + QVERIFY(!a.contains("Hello")); + QVERIFY(!a.contains(QCborArray())); + QVERIFY(!a.contains(QCborMap())); + QVERIFY(!a.contains(QCborValue(QDateTime::currentDateTimeUtc()))); + QVERIFY(!a.contains(QCborValue(QUrl("http://example.com")))); + QVERIFY(!a.contains(QCborValue(QUuid::createUuid()))); + + // iterators + auto it = a.constBegin(); + auto end = a.constEnd(); + QCOMPARE(end - it, 7); + QCOMPARE(it + 7, end); + QVERIFY(it->isInteger()); + QCOMPARE(*it, QCborValue(0)); + QCOMPARE(it[1], QCborValue(-1)); + QCOMPARE(*(it + 2), QCborValue(false)); + it += 3; + QCOMPARE(*it, QCborValue(true)); + ++it; + QCOMPARE(*it, QCborValue(nullptr)); + it++; + QCOMPARE(*it, QCborValue()); + --end; + QCOMPARE(*end, QCborValue(1.0)); + end--; + QCOMPARE(it, end); + + // range for + int i = 0; + for (const QCborValue &v : qAsConst(a)) { + QVERIFY(!v.isInvalid()); + QCOMPARE(v.isUndefined(), i == 5); // 6th element is Undefined + ++i; + } + QCOMPARE(i, a.size()); +} + +void tst_QCborValue::mapSimpleInitializerList() +{ + QCborMap m{{0, 0}, {1, 0}, {2, "Hello"}, {"Hello", 2}, {3, QLatin1String("World")}, {QLatin1String("World"), 3}}; + QCOMPARE(m.size(), 6); + QVERIFY(m == m); + QVERIFY(m != QCborMap{}); + QVERIFY(QCborMap{} != m); + QVERIFY(m == QCborMap({{0, 0}, {1, 0}, {2, "Hello"}, {"Hello", 2}, {3, QLatin1String("World")}, {QLatin1String("World"), 3}})); + + QCborValue vmap = m; + { + QVERIFY(m.contains(0)); + QCborValue v = m.value(0); + QVERIFY(v.isInteger()); + QCOMPARE(v.toInteger(), 0); + QCOMPARE(vmap[0], v); + } + { + QVERIFY(m.contains(1)); + QCborValue v = m.value(1); + QVERIFY(v.isInteger()); + QCOMPARE(v.toInteger(), 0); + QCOMPARE(vmap[1], v); + } + { + QVERIFY(m.contains(2)); + QCborValue v = m.value(2); + QVERIFY(v.isString()); + QCOMPARE(v.toString(), "Hello"); + QCOMPARE(vmap[2], v); + } + { + QVERIFY(m.contains(3)); + QCborValue v = m.value(3); + QVERIFY(v.isString()); + QCOMPARE(v.toString(), "World"); + QCOMPARE(vmap[3], v); + } + { + QVERIFY(m.contains(QStringLiteral("Hello"))); + QCborValue v = m.value(QLatin1String("Hello")); + QVERIFY(v.isInteger()); + QCOMPARE(v.toInteger(), 2); + QCOMPARE(vmap[QStringLiteral("Hello")], v); + } + { + QVERIFY(m.contains(QLatin1String("World"))); + QCborValue v = m.value(QStringLiteral("World")); + QVERIFY(v.isInteger()); + QCOMPARE(v.toInteger(), 3); + QCOMPARE(vmap[QLatin1String("World")], v); + } + + QVERIFY(!m.contains(QCborValue::Null)); + QVERIFY(!m.contains(QCborValue())); + QVERIFY(!m.contains(QCborValue(1.0))); // Important: 1.0 does not match 1 + QVERIFY(!m.contains(QCborValue(QByteArray("Hello")))); + QVERIFY(!m.contains(QCborArray())); + QVERIFY(!m.contains(QCborMap())); + QVERIFY(!m.contains(QCborValue(QDateTime::currentDateTimeUtc()))); + QVERIFY(!m.contains(QCborValue(QUrl("http://example.com")))); + QVERIFY(!m.contains(QCborValue(QUuid::createUuid()))); + + // iterators (QCborMap is not sorted) + auto it = m.constBegin(); + auto end = m.constEnd(); + QCOMPARE(end - it, 6); + QCOMPARE(it + 6, end); + QCOMPARE(it.key(), QCborValue(0)); + QCOMPARE(it.value(), QCborValue(0)); + QVERIFY(it->isInteger()); + ++it; + QCOMPARE(it.key(), QCborValue(1)); + QCOMPARE(it.value(), QCborValue(0)); + QCOMPARE((it + 1).key(), QCborValue(2)); + QVERIFY((it + 1)->isString()); + QCOMPARE((it + 1)->toString(), "Hello"); + it += 2; + QCOMPARE(it.key(), QCborValue("Hello")); + QVERIFY(it->isInteger()); + it++; + QCOMPARE(it.key(), QCborValue(3)); + QVERIFY(it->isString()); + QCOMPARE(it.value().toString(), "World"); + --end; + QCOMPARE(end.key(), QCborValue("World")); + QCOMPARE(end.value(), QCborValue(3)); + end--; + QCOMPARE(it, end); + + // range for + int i = 0; + for (auto pair : qAsConst(m)) { + QVERIFY(!pair.first.isUndefined()); + QVERIFY(!pair.second.isUndefined()); + ++i; + } + QCOMPARE(i, m.size()); +} + +void tst_QCborValue::arrayMutation() +{ + QCborArray a{42}; + { + QCborValueRef v = a[0]; + QVERIFY(!a.isEmpty()); + QVERIFY(v.isInteger()); + QCOMPARE(v.toInteger(), 42); + + // now mutate the list + v = true; + QVERIFY(v.isBool()); + QVERIFY(v.isTrue()); + QVERIFY(a.at(0).isTrue()); + QVERIFY(a.at(0) == v); + QVERIFY(v == a.at(0)); + } + + QVERIFY(a == a); + QVERIFY(a == QCborArray{true}); + + QCborArray a2 = a; + a.append(nullptr); + QCOMPARE(a.size(), 2); + QCOMPARE(a2.size(), 1); + + // self-insertion + a2.append(a2); + QCOMPARE(a2.size(), 2); + QCOMPARE(a2.last().toArray().size(), 1); + + QCborValueRef v = a[0]; + QVERIFY(v.isTrue()); + v = 2.5; + QVERIFY(v.isDouble()); + QVERIFY(a.first().isDouble()); + QVERIFY(a.last().isNull()); + QVERIFY(a2.first().isTrue()); + + a2 = a; + auto it = a.begin(); // detaches again + auto end = a.end(); + QCOMPARE(end - it, 2); + QCOMPARE(it + 2, end); + QCOMPARE(*it, QCborValue(2.5)); + QCOMPARE(*++it, QCborValue(nullptr)); + QVERIFY(a2 == a); + QVERIFY(a == a2); + + *it = -1; + QCOMPARE(*it, QCborValue(-1)); + QCOMPARE(a.at(1), QCborValue(-1)); + QCOMPARE(a2.at(1), QCborValue(nullptr)); + QCOMPARE(++it, end); +} + +void tst_QCborValue::mapMutation() +{ + QCborMap m; + QVERIFY(m.isEmpty()); + + { + QCborValueRef v = m[42]; + QCOMPARE(m.size(), 1); + QVERIFY(v.isUndefined()); + + // now mutate the list + v = true; + QVERIFY(v.isBool()); + QVERIFY(v.isTrue()); + QVERIFY(m.begin()->isTrue()); + QVERIFY(m.begin().value() == v); + QVERIFY(v == m.begin().value()); + } + + QVERIFY(m == QCborMap({{42, true}})); + QVERIFY(QCborMap({{42, true}}) == m); + + QCborMap m2 = m; + m.insert({nullptr, nullptr}); + QCOMPARE(m.size(), 2); + QCOMPARE(m2.size(), 1); + + QCborValueRef v = m[42]; + QVERIFY(v.isTrue()); + v = 2.5; + QVERIFY(v.isDouble()); + QVERIFY(m.begin()->isDouble()); + QVERIFY((m.end() - 1)->isNull()); + QVERIFY(m2.begin()->isTrue()); + + m2 = m; + auto it = m.begin(); // detaches again + auto end = m.end(); + QCOMPARE(end - it, 2); + QCOMPARE(it + 2, end); + QCOMPARE(it.key(), QCborValue(42)); + QCOMPARE(it.value(), QCborValue(2.5)); + QCOMPARE((++it).value(), QCborValue(nullptr)); + QCOMPARE(it.key(), QCborValue(nullptr)); + QVERIFY(m2 == m); + QVERIFY(m == m2); + + it.value() = -1; + QCOMPARE(it.key(), QCborValue(nullptr)); + QCOMPARE(it.value(), QCborValue(-1)); + QCOMPARE((m.end() - 1)->toInteger(), -1); + QVERIFY((m2.end() - 1)->isNull()); + QCOMPARE(++it, end); +} + +void tst_QCborValue::arrayPrepend() +{ + QCborArray a; + a.prepend(0); + a.prepend(nullptr); + QCOMPARE(a.at(1), QCborValue(0)); + QCOMPARE(a.at(0), QCborValue(nullptr)); +} + +void tst_QCborValue::arrayInsertRemove() +{ + QFETCH(QCborValue, v); + QCborArray a; + a.append(42); + a.append(v); + a.insert(1, QCborValue(nullptr)); + QCOMPARE(a.at(0), QCborValue(42)); + QCOMPARE(a.at(1), QCborValue(nullptr)); + QCOMPARE(a.at(2), v); + + // remove 42 + a.removeAt(0); + QCOMPARE(a.size(), 2); + QCOMPARE(a.at(0), QCborValue(nullptr)); + QCOMPARE(a.at(1), v); + + auto it = a.begin(); + it = a.erase(it); // removes nullptr + QCOMPARE(a.size(), 1); + QCOMPARE(a.at(0), v); + + it = a.erase(it); + QVERIFY(a.isEmpty()); + QCOMPARE(it, a.end()); +} + +void tst_QCborValue::arrayStringElements() +{ + QCborArray a{"Hello"}; + a.append(QByteArray("Hello")); + a.append(QLatin1String("World")); + QVERIFY(a == a); + QVERIFY(a == QCborArray({QLatin1String("Hello"), + QByteArray("Hello"), QStringLiteral("World")})); + + QCborValueRef r1 = a[0]; + QCOMPARE(r1.toString(), "Hello"); + QCOMPARE(r1.operator QCborValue(), QCborValue("Hello")); + QVERIFY(r1 == QCborValue("Hello")); + + QCborValue v2 = a.at(1); + QCOMPARE(v2.toByteArray(), QByteArray("Hello")); + QCOMPARE(v2, QCborValue(QByteArray("Hello"))); + + // v2 must continue to be valid after the entry getting removed + a.removeAt(1); + QCOMPARE(v2.toByteArray(), QByteArray("Hello")); + QCOMPARE(v2, QCborValue(QByteArray("Hello"))); + + v2 = a.at(1); + QCOMPARE(v2.toString(), "World"); + QCOMPARE(v2, QCborValue("World")); +} + +void tst_QCborValue::mapStringValues() +{ + QCborMap m{{0, "Hello"}}; + m.insert({1, QByteArray("Hello")}); + m.insert({2, QLatin1String("World")}); + QVERIFY(m == m); + + QCborValueRef r1 = m[0]; + QCOMPARE(r1.toString(), "Hello"); + QCOMPARE(r1.operator QCborValue(), QCborValue("Hello")); + QVERIFY(r1 == QCborValue("Hello")); + + QCborValue v2 = m.value(1); + QCOMPARE(v2.toByteArray(), QByteArray("Hello")); + QCOMPARE(v2, QCborValue(QByteArray("Hello"))); + + // v2 must continue to be valid after the entry getting removed + m.erase(m.constFind(1)); + QCOMPARE(v2.toByteArray(), QByteArray("Hello")); + QCOMPARE(v2, QCborValue(QByteArray("Hello"))); + + v2 = (m.begin() + 1).value(); + QCOMPARE(v2.toString(), "World"); + QCOMPARE(v2, QCborValue("World")); +} + +void tst_QCborValue::mapStringKeys() +{ + QCborMap m{{QLatin1String("Hello"), 1}, {QStringLiteral("World"), 2}}; + QCOMPARE(m.value(QStringLiteral("Hello")), QCborValue(1)); + QCOMPARE(m.value(QLatin1String("World")), QCborValue(2)); + + QCborMap m2 = m; + QVERIFY(m2 == m); + QVERIFY(m == m2); + + m.insert({QByteArray("foo"), "bar"}); + QCOMPARE(m.size(), 3); + QCOMPARE(m2.size(), 2); + QVERIFY(m2 != m); + QVERIFY(m != m2); + + QVERIFY(m2.value(QCborValue(QByteArray("foo"))).isUndefined()); + QVERIFY(m.value(QCborValue(QLatin1String("foo"))).isUndefined()); + QCOMPARE(m.value(QCborValue(QByteArray("foo"))).toString(), "bar"); +} + +void tst_QCborValue::mapInsertRemove() +{ + QFETCH(QCborValue, v); + QCborMap m{{1, v}}; + + m.remove(1); + QVERIFY(m.isEmpty()); + QVERIFY(!m.contains(1)); + + m.insert(2, v); + QVERIFY(m.contains(2)); + QVERIFY(m[2] == v); + QVERIFY(v == m[2]); + + auto it = m.find(2); + it = m.erase(it); + QVERIFY(m.isEmpty()); + + // creates m[2] and m[42] just by referencing them + m[2]; + QCborValueRef r = m[42]; + QCOMPARE(m.size(), 2); + + r = v; + it = m.find(42); + QVERIFY(it.value() == v); + QVERIFY(v == it.value()); + QVERIFY(it.value() == r); + QVERIFY(r == it.value()); +} + +void tst_QCborValue::arrayInsertTagged() +{ + QFETCH(QCborValue, v); + + // make it tagged + QCborValue tagged(QCborKnownTags::Signature, v); + + QCborArray a{tagged}; + a.insert(1, tagged); + QCOMPARE(a.size(), 2); + QCOMPARE(a.at(0), tagged); + QCOMPARE(a.at(1), tagged); + QCOMPARE(a.at(0).taggedValue(), v); + QCOMPARE(a.at(1).taggedValue(), v); +} + +void tst_QCborValue::mapInsertTagged() +{ + QFETCH(QCborValue, v); + + // make it tagged + QCborValue tagged(QCborKnownTags::Signature, v); + + QCborMap m{{11, tagged}}; + m.insert({-21, tagged}); + QCOMPARE(m.size(), 2); + QCOMPARE(m.constBegin().value(), tagged); + QCOMPARE(m.value(-21), tagged); + QCOMPARE(m.value(11).taggedValue(), v); + QCOMPARE((m.end() - 1).value().taggedValue(), v); +} + +void tst_QCborValue::arraySelfAssign() +{ + QFETCH(QCborValue, v); + QCborArray a; + + a = {v}; + + // Test 1: QCborValue created first, so + // QCborArray::insert() detaches + { + a.append(a); + QCOMPARE(a.size(), 2); + QCOMPARE(a.last().toArray().size(), 1); + } + + a = {v}; + + // Test 2: QCborValueRef created first + { + a.append(36); + auto it = a.end() - 1; + *it = a; + + QCOMPARE(a.size(), 2); + QCOMPARE(it->toArray().size(), 2); + QCOMPARE(it->toArray().last(), QCborValue(36)); + } +} + +void tst_QCborValue::mapSelfAssign() +{ + QFETCH(QCborValue, v); + QCborMap m; + + m = {{0, v}}; + QCOMPARE(m.size(), 1); + + // Test 1: create a QCborValue first + // in this case, QCborMap::operator[] detaches first + { + QCborValue vm = m; + m[1] = vm; // self-assign + QCOMPARE(m.size(), 2); + QCOMPARE(m.value(0), v); + + QCborMap m2 = m.value(1).toMap(); + // there mustn't be an element with key 1 + QCOMPARE(m2.size(), 1); + QCOMPARE(m2.value(0), v); + QVERIFY(!m2.contains(1)); + } + + m = {{0, v}}; + + // Test 2: create the QCborValueRef first + // in this case, there's no opportunity to detach + { + QCborValueRef rv = m[1]; + rv = m; // self-assign (implicit QCborValue creation) + QCOMPARE(m.size(), 2); + QCOMPARE(m.value(0), v); + + QCborMap m2 = m.value(1).toMap(); + // there must be an element with key 1 + QCOMPARE(m2.size(), 2); + QCOMPARE(m2.value(0), v); + QVERIFY(m2.contains(1)); + QCOMPARE(m2.value(1), QCborValue()); + } + + m = {{0, v}}; + + // Test 3: don't force creation of either before + // in this case, it's up to the compiler to choose + { + m[1] = m; // self-assign + QCOMPARE(m.size(), 2); + + QCborMap m2 = m.value(1).toMap(); + QVERIFY(m2.size() == 1 || m2.size() == 2); + } + + m = {{0, v}}; + + // Test 4: self-assign as key + // in this scase, QCborMap::operator[] must detach + { + m[m] = v; + QCOMPARE(m.size(), 2); + + auto it = m.constEnd() - 1; + QCOMPARE(it.value(), v); + QCOMPARE(it.key(), QCborMap({{0, v}})); + } +} + +void tst_QCborValue::mapComplexKeys() +{ + QFETCH(QCborValue, v); + QCborValue tagged(QCborKnownTags::Signature, v); + + QCborMap m{{42, true}, {v, 42}, {-3, nullptr}}; + QCOMPARE(m.size(), 3); + QVERIFY(m.contains(42)); + QVERIFY(m.contains(-3)); + QVERIFY(m.contains(v)); + QVERIFY(!m.contains(tagged)); + + auto it = m.constFind(v); + QVERIFY(it != m.constEnd()); + QVERIFY(it.key() == v); + QVERIFY(v == it.key()); + QCOMPARE(it.value().toInteger(), 42); + + QCborArray a{0, 1, 2, 3, v}; + m[a] = 1; + QCOMPARE(m.size(), 4); + QCOMPARE((m.constEnd() - 1).value(), QCborValue(1)); + if (v != QCborValue(QCborValue::Array)) + QVERIFY(!m.contains(QCborArray{})); + QVERIFY(!m.contains(QCborArray{0})); + QVERIFY(!m.contains(QCborArray{0, 1})); + QVERIFY(!m.contains(QCborArray{0, 1, 2})); + QVERIFY(!m.contains(QCborArray{0, 1, 2, 4})); + QVERIFY(!m.contains(QCborArray{0, 1, 2, 3, v, 4})); + + it = m.constFind(QCborArray{0, 1, 2, 3, v}); + QVERIFY(it != m.constEnd()); + QCOMPARE(it.key(), a); + QCOMPARE(it.value(), QCborValue(1)); + + m[m] = 1; // assign itself as a key -- this necessarily detaches before + QCOMPARE(m.size(), 5); + QCOMPARE((m.end() - 1).value(), 1); + QCOMPARE((m.end() - 1).key().toMap().size(), 4); + + QCborValue mv(m); + if (v.isInteger()) { + // we should be able to find using the overloads too + QCOMPARE(m[v.toInteger()].toInteger(), 42); + QCOMPARE(mv[v.toInteger()].toInteger(), 42); + } else if (v.isString()) { + // ditto + QCOMPARE(m[v.toString()].toInteger(), 42); + QCOMPARE(mv[v.toString()].toInteger(), 42); + + // basics_data() strings are Latin1 + QByteArray latin1 = v.toString().toLatin1(); + Q_ASSERT(v.toString() == QString::fromLatin1(latin1)); + QCOMPARE(m[QLatin1String(latin1)].toInteger(), 42); + } + + m.remove(v); + QVERIFY(!m.contains(v)); + QVERIFY(!m.contains(tagged)); + + QCborValueRef r = m[tagged]; + QVERIFY(!m.contains(v)); + QVERIFY(m.contains(tagged)); + r = 47; + QCOMPARE(m[tagged].toInteger(), 47); +} + +void tst_QCborValue::sorting() +{ + QCborValue vundef, vnull(nullptr); + QCborValue vtrue(true), vfalse(false); + QCborValue vint1(1), vint2(2); + QCborValue vneg1(-1), vneg2(-2); + QCborValue vba2(QByteArray("Hello")), vba3(QByteArray("World")), vba1(QByteArray("foo")); + QCborValue vs2("Hello"), vs3("World"), vs1("foo"); + QCborValue va1(QCborValue::Array), va2(QCborArray{1}), va3(QCborArray{0, 0}); + QCborValue vm1(QCborValue::Map), vm2(QCborMap{{1, 0}}), vm3(QCborMap{{0, 0}, {1, 0}}); + QCborValue vdt1(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)), vdt2(QDateTime::currentDateTimeUtc()); + QCborValue vtagged1(QCborKnownTags::UnixTime_t, 0), vtagged2(QCborKnownTags::UnixTime_t, 0.0), + vtagged3(QCborKnownTags::Signature, 0), vtagged4(QCborTag(-2), 0), vtagged5(QCborTag(-1), 0); + QCborValue vurl1(QUrl("https://example.net")), vurl2(QUrl("https://example.com/")); + QCborValue vuuid1{QUuid()}, vuuid2(QUuid::createUuid()); + QCborValue vsimple1(QCborSimpleType(1)), vsimple32(QCborSimpleType(32)), vsimple255(QCborSimpleType(255)); + QCborValue vdouble1(1.5), vdouble2(qInf()); + QCborValue vndouble1(-1.5), vndouble2(-qInf()); + +#define CHECK_ORDER(v1, v2) \ + QVERIFY(v1 < v2); \ + QVERIFY(!(v2 < v2)) + + // intra-type comparisons + CHECK_ORDER(vfalse, vtrue); + CHECK_ORDER(vsimple1, vsimple32); + CHECK_ORDER(vsimple32, vsimple255); + CHECK_ORDER(vint1, vint2); + CHECK_ORDER(vdouble1, vdouble2); + CHECK_ORDER(vndouble1, vndouble2); + // note: shorter length sorts first + CHECK_ORDER(vba1, vba2); + CHECK_ORDER(vba2, vba3); + CHECK_ORDER(vs1, vs2); + CHECK_ORDER(vs2, vs3); + CHECK_ORDER(va1, va2); + CHECK_ORDER(va2, va3); + CHECK_ORDER(vm1, vm2); + CHECK_ORDER(vm2, vm3); + CHECK_ORDER(vdt1, vdt2); + CHECK_ORDER(vtagged1, vtagged2); + CHECK_ORDER(vtagged2, vtagged3); + CHECK_ORDER(vtagged3, vtagged4); + CHECK_ORDER(vtagged4, vtagged5); + CHECK_ORDER(vurl1, vurl2); + CHECK_ORDER(vuuid1, vuuid2); + + // surprise 1: CBOR sorts integrals by absolute value + CHECK_ORDER(vneg1, vneg2); + + // surprise 2: CBOR sorts negatives after positives (sign+magnitude) + CHECK_ORDER(vint2, vneg1); + QVERIFY(vint2.toInteger() > vneg1.toInteger()); + CHECK_ORDER(vdouble2, vndouble1); + QVERIFY(vdouble2.toDouble() > vndouble1.toDouble()); + + // inter-type comparisons + CHECK_ORDER(vneg2, vba1); + CHECK_ORDER(vba3, vs1); + CHECK_ORDER(vs3, va1); + CHECK_ORDER(va2, vm1); + CHECK_ORDER(vm2, vdt1); + CHECK_ORDER(vdt2, vtagged1); + CHECK_ORDER(vtagged2, vurl1); + CHECK_ORDER(vurl1, vuuid1); + CHECK_ORDER(vuuid2, vtagged3); + CHECK_ORDER(vtagged4, vsimple1); + CHECK_ORDER(vsimple1, vfalse); + CHECK_ORDER(vtrue, vnull); + CHECK_ORDER(vnull, vundef); + CHECK_ORDER(vundef, vsimple32); + CHECK_ORDER(vsimple255, vdouble1); + + // which shows all doubles sorted after integrals + CHECK_ORDER(vint2, vdouble1); + QVERIFY(vint2.toInteger() > vdouble1.toDouble()); +#undef CHECK_ORDER +} + +static void addCommonCborData() +{ + // valid for both decoding and encoding + QTest::addColumn<QCborValue>("v"); + QTest::addColumn<QByteArray>("result"); + QTest::addColumn<QCborValue::EncodingOptions>("options"); + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUuid uuid = QUuid::createUuid(); + QCborValue::EncodingOptions noxfrm = QCborValue::NoTransformation; + + // integrals + QTest::newRow("Integer:0") << QCborValue(0) << raw("\x00") << noxfrm; + QTest::newRow("Integer:1") << QCborValue(1) << raw("\x01") << noxfrm; + QTest::newRow("Integer:-1") << QCborValue(-1) << raw("\x20") << noxfrm; + QTest::newRow("Integer:INT64_MAX") << QCborValue(std::numeric_limits<qint64>::max()) + << raw("\x1b\x7f\xff\xff\xff""\xff\xff\xff\xff") + << noxfrm; + QTest::newRow("Integer:INT64_MIN") << QCborValue(std::numeric_limits<qint64>::min()) + << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") + << noxfrm; + + QTest::newRow("simple0") << QCborValue(QCborValue::SimpleType) << raw("\xe0") << noxfrm; + QTest::newRow("simple1") << QCborValue(QCborSimpleType(1)) << raw("\xe1") << noxfrm; + QTest::newRow("simple255") << QCborValue(QCborSimpleType(255)) << raw("\xf8\xff") << noxfrm; + QTest::newRow("Undefined") << QCborValue() << raw("\xf7") << noxfrm; + QTest::newRow("Null") << QCborValue(nullptr) << raw("\xf6") << noxfrm; + QTest::newRow("True") << QCborValue(true) << raw("\xf5") << noxfrm; + QTest::newRow("False") << QCborValue(false) << raw("\xf4") << noxfrm; + QTest::newRow("simple32") << QCborValue(QCborSimpleType(32)) << raw("\xf8\x20") << noxfrm; + QTest::newRow("simple255") << QCborValue(QCborSimpleType(255)) << raw("\xf8\xff") << noxfrm; + + QTest::newRow("Double:0") << QCborValue(0.) << raw("\xfb\0\0\0\0""\0\0\0\0") << noxfrm; + QTest::newRow("Double:1.5") << QCborValue(1.5) << raw("\xfb\x3f\xf8\0\0""\0\0\0\0") << noxfrm; + QTest::newRow("Double:-1.5") << QCborValue(-1.5) << raw("\xfb\xbf\xf8\0\0""\0\0\0\0") << noxfrm; + QTest::newRow("Double:INT64_MAX+1") << QCborValue(std::numeric_limits<qint64>::max() + 1.) + << raw("\xfb\x43\xe0\0\0""\0\0\0\0") << noxfrm; + QTest::newRow("Double:maxintegralfp") << QCborValue(18446744073709551616.0 - 2048) + << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") + << noxfrm; + QTest::newRow("Double:minintegralfp") << QCborValue(-18446744073709551616.0 + 2048) + << raw("\xfb\xc3\xef\xff\xff""\xff\xff\xff\xff") + << noxfrm; + QTest::newRow("Double:inf") << QCborValue(qInf()) << raw("\xfb\x7f\xf0\0\0""\0\0\0\0") << noxfrm; + QTest::newRow("Double:-inf") << QCborValue(-qInf()) << raw("\xfb\xff\xf0\0""\0\0\0\0\0") << noxfrm; + QTest::newRow("Double:nan") << QCborValue(qQNaN()) << raw("\xfb\x7f\xf8\0\0""\0\0\0\0") << noxfrm; + + QTest::newRow("Float:0") << QCborValue(0.) << raw("\xfa\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + QTest::newRow("Float:1.5") << QCborValue(1.5) << raw("\xfa\x3f\xc0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + QTest::newRow("Float:-1.5") << QCborValue(-1.5) << raw("\xfa\xbf\xc0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + QTest::newRow("Float:inf") << QCborValue(qInf()) << raw("\xfa\x7f\x80\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + QTest::newRow("Float:-inf") << QCborValue(-qInf()) << raw("\xfa\xff\x80\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + QTest::newRow("Float:nan") << QCborValue(qQNaN()) << raw("\xfa\x7f\xc0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + + QTest::newRow("Float16:0") << QCborValue(0.) << raw("\xf9\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + QTest::newRow("Float16:1.5") << QCborValue(1.5) << raw("\xf9\x3e\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + QTest::newRow("Float16:-1.5") << QCborValue(-1.5) << raw("\xf9\xbe\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + QTest::newRow("Float16:inf") << QCborValue(qInf()) << raw("\xf9\x7c\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + QTest::newRow("Float16:-inf") << QCborValue(-qInf()) << raw("\xf9\xfc\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + QTest::newRow("Float16:nan") << QCborValue(qQNaN()) << raw("\xf9\x7e\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + + // out of range of qint64, but in range for CBOR, so these do get converted + // to integrals on write and back to double on read + QTest::newRow("UseInteger:INT64_MAX+1") << QCborValue(std::numeric_limits<qint64>::max() + 1.) + << raw("\x1b\x80\0\0\0""\0\0\0\0") + << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:maxintegralfp") << QCborValue(18446744073709551616.0 - 2048) + << raw("\x1b\xff\xff\xff\xff""\xff\xff\xf8\0") + << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:minintegralfp") << QCborValue(-18446744073709551616.0 + 2048) + << raw("\x3b\xff\xff\xff\xff""\xff\xff\xf7\xff") + << QCborValue::EncodingOptions(QCborValue::UseIntegers); + + QTest::newRow("ByteArray:Empty") << QCborValue(QByteArray()) << raw("\x40") << noxfrm; + QTest::newRow("ByteArray") << QCborValue(QByteArray("Hello")) << raw("\x45Hello") << noxfrm; + QTest::newRow("ByteArray:WithNull") << QCborValue(raw("\0\1\2\xff")) << raw("\x44\0\1\2\xff") << noxfrm; + + QTest::newRow("String:Empty") << QCborValue(QString()) << raw("\x60") << noxfrm; + QTest::newRow("String:UsAscii") << QCborValue("Hello") << raw("\x65Hello") << noxfrm; + QTest::newRow("String:Latin1") << QCborValue(QLatin1String("R\xe9sum\xe9")) + << raw("\x68R\xc3\xa9sum\xc3\xa9") << noxfrm; + QTest::newRow("String:Unicode") << QCborValue(QStringLiteral(u"éś α €")) + << raw("\x6b\xc3\xa9\xc5\x9b \xce\xb1 \xe2\x82\xac") << noxfrm; + + QTest::newRow("DateTime") << QCborValue(dt) // this is UTC + << "\xc0\x78\x18" + dt.toString(Qt::ISODateWithMs).toLatin1() + << noxfrm; + QTest::newRow("DateTime-UTC") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::UTC)) + << raw("\xc0\x78\x18" "2018-01-01T09:00:00.000Z") + << noxfrm; + QTest::newRow("DateTime-Local") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::LocalTime)) + << raw("\xc0\x77" "2018-01-01T09:00:00.000") + << noxfrm; + QTest::newRow("DateTime+01:00") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::OffsetFromUTC, 3600)) + << raw("\xc0\x78\x1d" "2018-01-01T09:00:00.000+01:00") + << noxfrm; + QTest::newRow("Url:Empty") << QCborValue(QUrl()) << raw("\xd8\x20\x60") << noxfrm; + QTest::newRow("Url") << QCborValue(QUrl("HTTPS://example.com/{%30%31}?q=%3Ca+b%20%C2%A9%3E&%26")) + << raw("\xd8\x20\x78\x27" "https://example.com/{01}?q=<a+b \xC2\xA9>&%26") + << noxfrm; + QTest::newRow("Regex:Empty") << QCborValue(QRegularExpression()) << raw("\xd8\x23\x60") << noxfrm; + QTest::newRow("Regex") << QCborValue(QRegularExpression("^.*$")) + << raw("\xd8\x23\x64" "^.*$") << noxfrm; + QTest::newRow("Uuid") << QCborValue(uuid) << raw("\xd8\x25\x50") + uuid.toRfc4122() << noxfrm; + + // empty arrays and maps + QTest::newRow("Array") << QCborValue(QCborArray()) << raw("\x80") << noxfrm; + QTest::newRow("Map") << QCborValue(QCborMap()) << raw("\xa0") << noxfrm; + + QTest::newRow("Tagged:ByteArray") << QCborValue(QCborKnownTags::PositiveBignum, raw("\1\0\0\0\0""\0\0\0\0")) + << raw("\xc2\x49\1\0\0\0\0""\0\0\0\0") << noxfrm; + QTest::newRow("Tagged:Array") << QCborValue(QCborKnownTags::Decimal, QCborArray{-2, 27315}) + << raw("\xc4\x82\x21\x19\x6a\xb3") << noxfrm; +} + +void tst_QCborValue::toCbor_data() +{ + addCommonCborData(); + + // The rest of these tests are conversions whose decoding does not yield + // back the same QCborValue. + + // Signalling NaN get normalized to quiet ones + QTest::newRow("Double:snan") << QCborValue(qSNaN()) << raw("\xfb\x7f\xf8\0""\0\0\0\0\0") << QCborValue::EncodingOptions(); + QTest::newRow("Float:snan") << QCborValue(qSNaN()) << raw("\xfa\x7f\xc0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat); + QTest::newRow("Float16:snan") << QCborValue(qSNaN()) << raw("\xf9\x7e\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16); + + // Floating point written as integers are read back as integers + QTest::newRow("UseInteger:0") << QCborValue(0.) << raw("\x00") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:1") << QCborValue(1.) << raw("\x01") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:-1") << QCborValue(-1.) << raw("\x20") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:INT64_MIN") << QCborValue(std::numeric_limits<qint64>::min() + 0.) + << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") + << QCborValue::EncodingOptions(QCborValue::UseIntegers); + + // but obviously non-integral or out of range floating point stay FP + QTest::newRow("UseInteger:1.5") << QCborValue(1.5) << raw("\xfb\x3f\xf8\0\0""\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:-1.5") << QCborValue(-1.5) << raw("\xfb\xbf\xf8\0\0""\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:inf") << QCborValue(qInf()) << raw("\xfb\x7f\xf0\0\0""\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:-inf") << QCborValue(-qInf()) << raw("\xfb\xff\xf0\0""\0\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:nan") << QCborValue(qQNaN()) << raw("\xfb\x7f\xf8\0\0""\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:2^64") << QCborValue(18446744073709551616.0) << raw("\xfb\x43\xf0\0\0""\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); + QTest::newRow("UseInteger:-2^65") << QCborValue(-2 * 18446744073709551616.0) << raw("\xfb\xc4\0\0\0""\0\0\0\0") << QCborValue::EncodingOptions(QCborValue::UseIntegers); +} + +void tst_QCborValue::toCbor() +{ + QFETCH(QCborValue, v); + QFETCH(QByteArray, result); + QFETCH(QCborValue::EncodingOptions, options); + + QCOMPARE(v.toCbor(options), result); + + // in maps and arrays + QCOMPARE(QCborArray{v}.toCborValue().toCbor(options), "\x81" + result); + QCOMPARE(QCborArray({v, v}).toCborValue().toCbor(options), + "\x82" + result + result); + QCOMPARE(QCborMap({{1, v}}).toCborValue().toCbor(options), + "\xa1\x01" + result); + + // tagged + QCborValue t(QCborKnownTags::Signature, v); + QCOMPARE(t.toCbor(options), "\xd9\xd9\xf7" + result); + QCOMPARE(QCborArray({t, t}).toCborValue().toCbor(options), + "\x82\xd9\xd9\xf7" + result + "\xd9\xd9\xf7" + result); + QCOMPARE(QCborMap({{1, t}}).toCborValue().toCbor(options), + "\xa1\x01\xd9\xd9\xf7" + result); +} + +void tst_QCborValue::fromCbor_data() +{ + addCommonCborData(); + + // chunked strings + QTest::newRow("ByteArray:Chunked") << QCborValue(QByteArray("Hello")) + << raw("\x5f\x43Hel\x42lo\xff"); + QTest::newRow("ByteArray:Chunked:Empty") << QCborValue(QByteArray()) << raw("\x5f\xff"); + QTest::newRow("String:Chunked") << QCborValue("Hello") + << raw("\x7f\x63Hel\x62lo\xff"); + QTest::newRow("String:Chunked:Empty") << QCborValue(QString()) + << raw("\x7f\xff"); + + QTest::newRow("DateTime:NoMilli") << QCborValue(QDateTime::fromSecsSinceEpoch(1515565477, Qt::UTC)) + << raw("\xc0\x74" "2018-01-10T06:24:37Z"); + QTest::newRow("UnixTime_t:Integer") << QCborValue(QDateTime::fromSecsSinceEpoch(1515565477, Qt::UTC)) + << raw("\xc1\x1a\x5a\x55\xb1\xa5"); + QTest::newRow("UnixTime_t:Double") << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, Qt::UTC)) + << raw("\xc1\xfb\x41\xd6\x95\x6c""\x69\x48\x00\x00"); + + QTest::newRow("Url:NotNormalized") << QCborValue(QUrl("https://example.com/\xc2\xa9 ")) + << raw("\xd8\x20\x78\x1dHTTPS://EXAMPLE.COM/%c2%a9%20"); + + QTest::newRow("Uuid:Zero") << QCborValue(QUuid()) << raw("\xd8\x25\x40"); + QTest::newRow("Uuid:TooShort") << QCborValue(QUuid::fromRfc4122(raw("\1\2\3\4""\4\3\2\0""\0\0\0\0""\0\0\0\0"))) + << raw("\xd8\x25\x47" "\1\2\3\4\4\3\2"); + QTest::newRow("Uuid:TooLong") << QCborValue(QUuid::fromRfc4122(raw("\1\2\3\4""\4\3\2\0""\0\0\0\0""\0\0\0\1"))) + << raw("\xd8\x25\x51" "\1\2\3\4""\4\3\2\0""\0\0\0\0""\0\0\0\1""\2"); +} + +void tst_QCborValue::fromCbor() +{ + QFETCH(QCborValue, v); + QFETCH(QByteArray, result); + + auto doCheck = [](const QCborValue &v, const QByteArray &result) { + QCborParserError error; + QCborValue decoded = QCborValue::fromCbor(result, &error); + QVERIFY2(error.error == QCborError(), qPrintable(error.errorString())); + QCOMPARE(error.offset, result.size()); + QVERIFY(decoded == v); + QVERIFY(v == decoded); + }; + + doCheck(v, result); + if (QTest::currentTestFailed()) + return; + + // in an array + doCheck(QCborArray{v}, "\x81" + result); + if (QTest::currentTestFailed()) + return; + + doCheck(QCborArray{v, v}, "\x82" + result + result); + if (QTest::currentTestFailed()) + return; + + // in a map + doCheck(QCborMap{{1, v}}, "\xa1\1" + result); + if (QTest::currentTestFailed()) + return; + + // undefined-length arrays and maps + doCheck(QCborArray{v}, "\x9f" + result + "\xff"); + if (QTest::currentTestFailed()) + return; + doCheck(QCborArray{v, v}, "\x9f" + result + result + "\xff"); + if (QTest::currentTestFailed()) + return; + doCheck(QCborMap{{1, v}}, "\xbf\1" + result + "\xff"); + if (QTest::currentTestFailed()) + return; + + // tagged + QCborValue t(QCborKnownTags::Signature, v); + doCheck(t, "\xd9\xd9\xf7" + result); + if (QTest::currentTestFailed()) + return; + + // in an array + doCheck(QCborArray{t}, "\x81\xd9\xd9\xf7" + result); + if (QTest::currentTestFailed()) + return; + + doCheck(QCborArray{t, t}, "\x82\xd9\xd9\xf7" + result + "\xd9\xd9\xf7" + result); + if (QTest::currentTestFailed()) + return; + + // in a map + doCheck(QCborMap{{1, t}}, "\xa1\1\xd9\xd9\xf7" + result); + if (QTest::currentTestFailed()) + return; +} + +void tst_QCborValue::validation_data() +{ + addValidationColumns(); + addValidationData(); + + // These tests say we have arrays and maps with very large item counts. + // They are meant to ensure we don't pre-allocate a lot of memory + // unnecessarily and possibly crash the application. The actual number of + // elements in the stream is only 2, so we should get an unexpected EOF + // error. QCborValue internally uses 16 bytes per element, so we get to + // 2 GB at 2^27 elements. + QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0"); + QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0"); + + // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements + QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0"); +} + +void tst_QCborValue::validation() +{ + QFETCH(QByteArray, data); + + QCborParserError error; + QCborValue decoded = QCborValue::fromCbor(data, &error); + QVERIFY(error.error != QCborError{}); + + if (data.startsWith('\x81')) { + // decode without the array prefix + decoded = QCborValue::fromCbor(data.mid(1), &error); + QVERIFY(error.error != QCborError{}); + } +} + +QTEST_MAIN(tst_QCborValue) + +#include "tst_qcborvalue.moc" diff --git a/tests/auto/corelib/serialization/qcborvalue_json/qcborvalue_json.pro b/tests/auto/corelib/serialization/qcborvalue_json/qcborvalue_json.pro new file mode 100644 index 0000000000..c11000b7c2 --- /dev/null +++ b/tests/auto/corelib/serialization/qcborvalue_json/qcborvalue_json.pro @@ -0,0 +1,7 @@ +QT = core testlib +TARGET = tst_qcborvalue_json +CONFIG += testcase +SOURCES += \ + tst_qcborvalue_json.cpp + +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp new file mode 100644 index 0000000000..324e9b1dfb --- /dev/null +++ b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp @@ -0,0 +1,339 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 <QtCore/qcborvalue.h> +#include <QtTest> + +Q_DECLARE_METATYPE(QCborValue) + +class tst_QCborValue_Json : public QObject +{ + Q_OBJECT + +private slots: + void toVariant_data(); + void toVariant(); + void toJson_data() { toVariant_data(); } + void toJson(); + void taggedByteArrayToJson_data(); + void taggedByteArrayToJson(); + + void fromVariant_data() { toVariant_data(); } + void fromVariant(); + void fromJson_data(); + void fromJson(); + + void nonStringKeysInMaps_data(); + void nonStringKeysInMaps(); +}; + +void tst_QCborValue_Json::toVariant_data() +{ + QTest::addColumn<QCborValue>("v"); + QTest::addColumn<QVariant>("variant"); + QTest::addColumn<QJsonValue>("json"); + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUuid uuid = QUuid::createUuid(); + + QMetaEnum me = QMetaEnum::fromType<QCborValue::Type>(); + auto add = [me](const QCborValue &v, const QVariant &exp, const QJsonValue &json) { + auto addRow = [=]() -> QTestData & { + const char *typeString = me.valueToKey(v.type()); + if (v.type() == QCborValue::Integer) + return QTest::addRow("Integer:%lld", exp.toLongLong()); + if (v.type() == QCborValue::Double) + return QTest::addRow("Double:%g", exp.toDouble()); + if (v.type() == QCborValue::ByteArray || v.type() == QCborValue::String) + return QTest::addRow("%s:%d", typeString, exp.toString().size()); + if (v.type() >= 0x10000) + return QTest::newRow(exp.typeName()); + return QTest::newRow(typeString); + }; + addRow() << v << exp << json; + }; + + // good JSON matching: + add(QCborValue(), QVariant(), QJsonValue::Undefined); + add(nullptr, QVariant::fromValue(nullptr), QJsonValue::Null); + add(false, false, false); + add(true, true, true); + add(0, 0, 0); + add(1, 1, 1); + add(-1, -1, -1); + add(0., 0., 0.); + add(1.25, 1.25, 1.25); + add(-1.25, -1.25, -1.25); + add("Hello", "Hello", "Hello"); + + // converts to string in JSON: + add(QByteArray("Hello"), QByteArray("Hello"), "SGVsbG8"); + add(QCborValue(dt), dt, dt.toString(Qt::ISODateWithMs)); + add(QCborValue(QUrl("http://example.com/{q}")), QUrl("http://example.com/{q}"), + "http://example.com/%7Bq%7D"); // note the encoded form in JSON + add(QCborValue(QRegularExpression(".")), QRegularExpression("."), "."); + add(QCborValue(uuid), uuid, uuid.toString(QUuid::WithoutBraces)); + + // not valid in JSON + QTest::newRow("simpletype") << QCborValue(QCborSimpleType(255)) + << QVariant::fromValue(QCborSimpleType(255)) + << QJsonValue("simple(255)"); + QTest::newRow("Double:inf") << QCborValue(qInf()) + << QVariant(qInf()) + << QJsonValue(); + QTest::newRow("Double:-inf") << QCborValue(-qInf()) + << QVariant(-qInf()) + << QJsonValue(); + QTest::newRow("Double:nan") << QCborValue(qQNaN()) + << QVariant(qQNaN()) + << QJsonValue(); + + // large integral values lose precision in JSON + QTest::newRow("Integer:max") << QCborValue(std::numeric_limits<qint64>::max()) + << QVariant(std::numeric_limits<qint64>::max()) + << QJsonValue(std::numeric_limits<qint64>::max()); + QTest::newRow("Integer:min") << QCborValue(std::numeric_limits<qint64>::min()) + << QVariant(std::numeric_limits<qint64>::min()) + << QJsonValue(std::numeric_limits<qint64>::min()); + + // empty arrays and maps + add(QCborArray(), QVariantList(), QJsonArray()); + add(QCborMap(), QVariantMap(), QJsonObject()); +} + +void tst_QCborValue_Json::toVariant() +{ + QFETCH(QCborValue, v); + QFETCH(QVariant, variant); + + if (qIsNaN(variant.toDouble())) { + // because NaN != NaN, QVariant(NaN) != QVariant(NaN), so we + // only need to compare the classification + QVERIFY(qIsNaN(v.toVariant().toDouble())); + + // the rest of this function depends on the variant comparison + return; + } + + QCOMPARE(v.toVariant(), variant); + + // tags get ignored: + QCOMPARE(QCborValue(QCborKnownTags::Signature, v).toVariant(), variant); + + // make arrays with this item + QCOMPARE(QCborArray({v}).toVariantList(), QVariantList({variant})); + QCOMPARE(QCborArray({v, v}).toVariantList(), QVariantList({variant, variant})); + + // and maps + QCOMPARE(QCborMap({{"foo", v}}).toVariantMap(), QVariantMap({{"foo", variant}})); + QCOMPARE(QCborMap({{"foo", v}}).toVariantHash(), QVariantHash({{"foo", variant}})); + + // finally, mixed + QCOMPARE(QCborArray{QCborMap({{"foo", v}})}.toVariantList(), + QVariantList{QVariantMap({{"foo", variant}})}); +} + +void tst_QCborValue_Json::toJson() +{ + QFETCH(QCborValue, v); + QFETCH(QJsonValue, json); + + QCOMPARE(v.toJsonValue(), json); + + // most tags get ignored: + QCOMPARE(QCborValue(QCborKnownTags::Signature, v).toJsonValue(), json); + + // make arrays with this item + QCOMPARE(QCborArray({v}).toJsonArray(), QJsonArray({json})); + QCOMPARE(QCborArray({v, v}).toJsonArray(), QJsonArray({json, json})); + + // and maps + QCOMPARE(QCborMap({{"foo", v}}).toJsonObject(), QJsonObject({{"foo", json}})); + + // finally, mixed + QCOMPARE(QCborArray{QCborMap({{"foo", v}})}.toJsonArray(), + QJsonArray{QJsonObject({{"foo", json}})}); +} + +void tst_QCborValue_Json::taggedByteArrayToJson_data() +{ + QTest::addColumn<QCborValue>("v"); + QTest::addColumn<QJsonValue>("json"); + + QByteArray data("\xff\x01"); + QTest::newRow("base64url") << QCborValue(QCborKnownTags::ExpectedBase64url, data) << QJsonValue("_wE"); + QTest::newRow("base64") << QCborValue(QCborKnownTags::ExpectedBase64, data) << QJsonValue("/wE="); + QTest::newRow("hex") << QCborValue(QCborKnownTags::ExpectedBase16, data) << QJsonValue("ff01"); +} + +void tst_QCborValue_Json::taggedByteArrayToJson() +{ + QFETCH(QCborValue, v); + QFETCH(QJsonValue, json); + + QCOMPARE(v.toJsonValue(), json); + QCOMPARE(QCborArray({v}).toJsonArray(), QJsonArray({json})); +} + +void tst_QCborValue_Json::fromVariant() +{ + QFETCH(QCborValue, v); + QFETCH(QVariant, variant); + + QCOMPARE(QCborValue::fromVariant(variant), v); + + // try arrays + QCOMPARE(QCborArray::fromVariantList({variant}), QCborArray{v}); + QCOMPARE(QCborArray::fromVariantList({variant, variant}), QCborArray({v, v})); + + if (variant.type() == QVariant::String) { + QString s = variant.toString(); + QCOMPARE(QCborArray::fromStringList({s}), QCborArray{v}); + QCOMPARE(QCborArray::fromStringList({s, s}), QCborArray({v, v})); + } + + // maps... + QVariantMap map{{"foo", variant}}; + QCOMPARE(QCborMap::fromVariantMap(map), QCborMap({{"foo", v}})); + QCOMPARE(QCborMap::fromVariantHash({{"foo", variant}}), QCborMap({{"foo", v}})); + + // nested + QVariantMap outer{{"bar", QVariantList{0, map, true}}}; + QCOMPARE(QCborMap::fromVariantMap(outer), + QCborMap({{"bar", QCborArray{0, QCborMap{{"foo", v}}, true}}})); +} + +void tst_QCborValue_Json::fromJson_data() +{ + QTest::addColumn<QCborValue>("v"); + QTest::addColumn<QJsonValue>("json"); + + QTest::newRow("null") << QCborValue(QCborValue::Null) << QJsonValue(QJsonValue::Null); + QTest::newRow("false") << QCborValue(false) << QJsonValue(false); + QTest::newRow("true") << QCborValue(true) << QJsonValue(true); + QTest::newRow("0") << QCborValue(0) << QJsonValue(0.); + QTest::newRow("1") << QCborValue(1) << QJsonValue(1); + QTest::newRow("1.5") << QCborValue(1.5) << QJsonValue(1.5); + QTest::newRow("string") << QCborValue("Hello") << QJsonValue("Hello"); + QTest::newRow("array") << QCborValue(QCborValue::Array) << QJsonValue(QJsonValue::Array); + QTest::newRow("map") << QCborValue(QCborValue::Map) << QJsonValue(QJsonValue::Object); +} + +void tst_QCborValue_Json::fromJson() +{ + QFETCH(QCborValue, v); + QFETCH(QJsonValue, json); + + QCOMPARE(QCborValue::fromJsonValue(json), v); + QCOMPARE(QCborArray::fromJsonArray({json}), QCborArray({v})); + QCOMPARE(QCborArray::fromJsonArray({json, json}), QCborArray({v, v})); + QCOMPARE(QCborMap::fromJsonObject({{"foo", json}}), QCborMap({{"foo", v}})); + + // confirm we can roundtrip back to JSON + QCOMPARE(QCborValue::fromJsonValue(json).toJsonValue(), json); +} + +void tst_QCborValue_Json::nonStringKeysInMaps_data() +{ + QTest::addColumn<QCborValue>("key"); + QTest::addColumn<QString>("converted"); + + auto add = [](const char *str, const QCborValue &v) { + QTest::newRow(str) << v << str; + }; + add("0", 0); + add("-1", -1); + add("false", false); + add("true", true); + add("null", nullptr); + add("undefined", {}); // should this be ""? + add("simple(255)", QCborSimpleType(255)); + add("2.5", 2.5); + + QByteArray data("\xff\x01"); + QTest::newRow("bytearray") << QCborValue(data) << "_wE"; + QTest::newRow("base64url") << QCborValue(QCborKnownTags::ExpectedBase64url, data) << "_wE"; + QTest::newRow("base64") << QCborValue(QCborKnownTags::ExpectedBase64, data) << "/wE="; + QTest::newRow("hex") << QCborValue(QCborKnownTags::ExpectedBase16, data) << "ff01"; + + QTest::newRow("emptyarray") << QCborValue(QCborValue::Array) << "[ ]"; + QTest::newRow("emptymap") << QCborValue(QCborValue::Map) << "{ }"; + QTest::newRow("array") << QCborValue(QCborArray{1, true, 2.5, "Hello"}) + << "[ 1, true, 2.5, \"Hello\" ]"; + QTest::newRow("map") << QCborValue(QCborMap{{"Hello", 0}, {0, "Hello"}}) + << "{ \"Hello\": 0, 0: \"Hello\" }"; + + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUrl url("https://example.com"); + QUuid uuid = QUuid::createUuid(); + QTest::newRow("QDateTime") << QCborValue(dt) << dt.toString(Qt::ISODateWithMs); + QTest::newRow("QUrl") << QCborValue(url) << url.toString(QUrl::FullyEncoded); + QTest::newRow("QRegularExpression") << QCborValue(QRegularExpression(".*")) << ".*"; + QTest::newRow("QUuid") << QCborValue(uuid) << uuid.toString(QUuid::WithoutBraces); +} + +void tst_QCborValue_Json::nonStringKeysInMaps() +{ + QFETCH(QCborValue, key); + QFETCH(QString, converted); + + QCborMap m; + m.insert(key, 0); + + { + QVariantMap vm = m.toVariantMap(); + auto it = vm.begin(); + QVERIFY(it != vm.end()); + QCOMPARE(it.key(), converted); + QCOMPARE(it.value(), 0); + QCOMPARE(++it, vm.end()); + } + + { + QJsonObject o = m.toJsonObject(); + auto it = o.begin(); + QVERIFY(it != o.end()); + QCOMPARE(it.key(), converted); + QCOMPARE(it.value(), 0); + QCOMPARE(++it, o.end()); + } +} + +QTEST_MAIN(tst_QCborValue_Json) + +#include "tst_qcborvalue_json.moc" diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp index 14a2528cc6..c6faf8c7d5 100644 --- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp @@ -260,16 +260,16 @@ static int NColorRoles[] = { QPalette::HighlightedText + 1, // Qt_4_0, Qt_4_1 QPalette::HighlightedText + 1, // Qt_4_2 QPalette::AlternateBase + 1, // Qt_4_3 - QPalette::ToolTipText + 1, // Qt_4_4 - QPalette::ToolTipText + 1, // Qt_4_5 - QPalette::ToolTipText + 1, // Qt_4_6 - QPalette::ToolTipText + 1, // Qt_5_0 - QPalette::ToolTipText + 1, // Qt_5_1 - QPalette::ToolTipText + 1, // Qt_5_2 - QPalette::ToolTipText + 1, // Qt_5_3 - QPalette::ToolTipText + 1, // Qt_5_4 - QPalette::ToolTipText + 1, // Qt_5_5 - QPalette::ToolTipText + 1, // Qt_5_6 + QPalette::PlaceholderText + 1, // Qt_4_4 + QPalette::PlaceholderText + 1, // Qt_4_5 + QPalette::PlaceholderText + 1, // Qt_4_6 + QPalette::PlaceholderText + 1, // Qt_5_0 + QPalette::PlaceholderText + 1, // Qt_5_1 + QPalette::PlaceholderText + 1, // Qt_5_2 + QPalette::PlaceholderText + 1, // Qt_5_3 + QPalette::PlaceholderText + 1, // Qt_5_4 + QPalette::PlaceholderText + 1, // Qt_5_5 + QPalette::PlaceholderText + 1, // Qt_5_6 0 // add the correct value for Qt_5_7 here later }; @@ -2139,7 +2139,7 @@ void tst_QDataStream::setVersion() */ // revise the test if new color roles or color groups are added - QVERIFY(QPalette::NColorRoles == QPalette::ToolTipText + 1); + QVERIFY(QPalette::NColorRoles == QPalette::PlaceholderText + 1); QCOMPARE(int(QPalette::NColorGroups), 3); QByteArray ba2; diff --git a/tests/auto/corelib/serialization/serialization.pro b/tests/auto/corelib/serialization/serialization.pro index afb9c5b61c..9187de1bc5 100644 --- a/tests/auto/corelib/serialization/serialization.pro +++ b/tests/auto/corelib/serialization/serialization.pro @@ -1,6 +1,10 @@ TEMPLATE = subdirs SUBDIRS = \ json \ + qcborstreamreader \ + qcborstreamwriter \ + qcborvalue \ + qcborvalue_json \ qdatastream \ qtextstream \ qxmlstream diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index d4a3ee6054..b8c82c2ea0 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -1021,6 +1021,8 @@ void tst_QFuture::iterators() QCOMPARE(i2, c2); QCOMPARE(c2, i2); QCOMPARE(c2, c2); + QCOMPARE(1 + i1, i1 + 1); + QCOMPARE(1 + c1, c1 + 1); QVERIFY(i1 != i2); QVERIFY(i1 != c2); @@ -1070,6 +1072,8 @@ void tst_QFuture::iterators() QCOMPARE(i2, c2); QCOMPARE(c2, i2); QCOMPARE(c2, c2); + QCOMPARE(1 + i1, i1 + 1); + QCOMPARE(1 + c1, c1 + 1); QVERIFY(i1 != i2); QVERIFY(i1 != c2); diff --git a/tests/auto/corelib/thread/qsemaphore/BLACKLIST b/tests/auto/corelib/thread/qsemaphore/BLACKLIST index eb83b03556..0786f50417 100644 --- a/tests/auto/corelib/thread/qsemaphore/BLACKLIST +++ b/tests/auto/corelib/thread/qsemaphore/BLACKLIST @@ -1,6 +1,8 @@ [tryAcquireWithTimeout:0.2s] windows osx-10.12 +osx-10.13 [tryAcquireWithTimeout:2s] windows osx-10.12 +osx-10.13 diff --git a/tests/auto/corelib/thread/qthread/BLACKLIST b/tests/auto/corelib/thread/qthread/BLACKLIST index d75249454f..9333592369 100644 --- a/tests/auto/corelib/thread/qthread/BLACKLIST +++ b/tests/auto/corelib/thread/qthread/BLACKLIST @@ -1,2 +1,4 @@ [wait3_slowDestructor] windows +[create] +ubuntu-18.04 diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro index 94a0a01e94..d5c09ebc84 100644 --- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro +++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro @@ -1,5 +1,13 @@ SOURCES += crashOnExit.cpp -DESTDIR = ./ +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/crashOnExit_helper + } else { + TARGET = ../../release/crashOnExit_helper + } +} else { + TARGET = ../crashOnExit_helper +} QT = core CONFIG -= app_bundle CONFIG += console diff --git a/tests/auto/corelib/thread/qthreadstorage/test/test.pro b/tests/auto/corelib/thread/qthreadstorage/test/test.pro index d7190f7e7b..d2f21f48f0 100644 --- a/tests/auto/corelib/thread/qthreadstorage/test/test.pro +++ b/tests/auto/corelib/thread/qthreadstorage/test/test.pro @@ -1,9 +1,16 @@ CONFIG += testcase -TARGET = ../tst_qthreadstorage -CONFIG -= debug_and_release_target +debug_and_release { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qthreadstorage + !android:!winrt: TEST_HELPER_INSTALLS = ../../debug/crashonexit_helper + } else { + TARGET = ../../release/tst_qthreadstorage + !android:!winrt: TEST_HELPER_INSTALLS = ../../release/crashonexit_helper + } +} else { + TARGET = ../tst_qthreadstorage + !android:!winrt: TEST_HELPER_INSTALLS = ../crashonexit_helper +} CONFIG += console QT = core testlib SOURCES = ../tst_qthreadstorage.cpp - -!android:!winrt: TEST_HELPER_INSTALLS = ../crashonexit/crashonexit - diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp index 403e28b07b..ef5d3452d5 100644 --- a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp +++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp @@ -48,7 +48,6 @@ class tst_QThreadStorage : public QObject { Q_OBJECT private slots: - void initTestCase(); void hasLocalData(); void localData(); void localData_const(); @@ -60,9 +59,6 @@ private slots: void leakInDestructor(); void resetInDestructor(); void valueBased(); - -private: - QString m_crashOnExit; }; class Pointer @@ -74,22 +70,6 @@ public: }; int Pointer::count = 0; -void tst_QThreadStorage::initTestCase() -{ -#if QT_CONFIG(process) - const QString crashOnExitDir = QFINDTESTDATA("crashonexit"); - QVERIFY2(!crashOnExitDir.isEmpty(), - qPrintable(QString::fromLatin1("Could not find 'crashonexit' starting from '%1'") - .arg(QDir::toNativeSeparators(QDir::currentPath())))); - m_crashOnExit = crashOnExitDir + QStringLiteral("/crashonexit"); -#ifdef Q_OS_WIN - m_crashOnExit += QStringLiteral(".exe"); -#endif - QVERIFY2(QFileInfo(m_crashOnExit).isExecutable(), - qPrintable(QDir::toNativeSeparators(m_crashOnExit) + QStringLiteral(" does not exist or is not executable."))); -#endif -} - void tst_QThreadStorage::hasLocalData() { QThreadStorage<Pointer *> pointers; @@ -329,7 +309,7 @@ void tst_QThreadStorage::crashOnExit() QSKIP("No qprocess support", SkipAll); #else QString errorMessage; - QVERIFY2(runCrashOnExit(m_crashOnExit, &errorMessage), + QVERIFY2(runCrashOnExit("crashOnExit_helper", &errorMessage), qPrintable(errorMessage)); #endif } diff --git a/tests/auto/corelib/tools/collections/tst_collections.cpp b/tests/auto/corelib/tools/collections/tst_collections.cpp index 38366e86ff..b40b1f0624 100644 --- a/tests/auto/corelib/tools/collections/tst_collections.cpp +++ b/tests/auto/corelib/tools/collections/tst_collections.cpp @@ -2620,6 +2620,8 @@ void testLinkedListLikeStlIterators() QVERIFY(i2 == c2); QVERIFY(c2 == i2); QVERIFY(c2 == c2); + QVERIFY(1 + i1 == i1 + 1); + QVERIFY(1 + c1 == c1 + 1); QVERIFY(i1 != i2); QVERIFY(i1 != c2); @@ -2731,6 +2733,8 @@ void testMapLikeStlIterators() QVERIFY(i2 == c2); QVERIFY(c2 == i2); QVERIFY(c2 == c2); + QVERIFY(1 + i1 == i1 + 1); + QVERIFY(1 + c1 == c1 + 1); QVERIFY(i1 != i2); QVERIFY(i1 != c2); diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp index 338adaabf7..ecfa331141 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -141,6 +141,8 @@ private slots: #endif void toUpperLower_data(); void toUpperLower(); + void isUpper(); + void isLower(); void macTypes(); @@ -856,15 +858,38 @@ void tst_QByteArray::qstricmp() if ( actual != 0 ) { actual = (actual < 0 ? -1 : 1); } - QCOMPARE(expected, actual); + QCOMPARE(actual, expected); + + actual = ::qstricmp("012345679abcd" + str1.toLatin1(), "012345679AbCd" + str2.toLatin1()); + if ( actual != 0 ) { + actual = (actual < 0 ? -1 : 1); + } + QCOMPARE(actual, expected); + + actual = str1.toLatin1().compare(str2.toLatin1(), Qt::CaseInsensitive); + if ( actual != 0 ) { + actual = (actual < 0 ? -1 : 1); + } + QCOMPARE(actual, expected); + + actual = str1.toLatin1().compare(str2.toLatin1().constData(), Qt::CaseInsensitive); + if ( actual != 0 ) { + actual = (actual < 0 ? -1 : 1); + } + QCOMPARE(actual, expected); } void tst_QByteArray::qstricmp_singularities() { QCOMPARE(::qstricmp(0, 0), 0); - QVERIFY(::qstricmp(0, "a") != 0); - QVERIFY(::qstricmp("a", 0) != 0); + QVERIFY(::qstricmp(0, "a") < 0); + QVERIFY(::qstricmp("a", 0) > 0); QCOMPARE(::qstricmp("", ""), 0); + QCOMPARE(QByteArray().compare(nullptr, Qt::CaseInsensitive), 0); + QCOMPARE(QByteArray().compare("", Qt::CaseInsensitive), 0); + QVERIFY(QByteArray("a").compare(nullptr, Qt::CaseInsensitive) > 0); + QVERIFY(QByteArray("a").compare("", Qt::CaseInsensitive) > 0); + QVERIFY(QByteArray().compare("a", Qt::CaseInsensitive) < 0); } void tst_QByteArray::qstrnicmp_singularities() @@ -874,6 +899,9 @@ void tst_QByteArray::qstrnicmp_singularities() QVERIFY(::qstrnicmp("a", 0, 123) != 0); QCOMPARE(::qstrnicmp("", "", 123), 0); QCOMPARE(::qstrnicmp("a", "B", 0), 0); + QCOMPARE(QByteArray().compare(QByteArray(), Qt::CaseInsensitive), 0); + QVERIFY(QByteArray().compare(QByteArray("a"), Qt::CaseInsensitive) < 0); + QVERIFY(QByteArray("a").compare(QByteArray(), Qt::CaseInsensitive) > 0); } void tst_QByteArray::chop_data() @@ -1757,6 +1785,12 @@ void tst_QByteArray::compare() const bool isLess = result < 0; const bool isGreater = result > 0; + int cmp = str1.compare(str2); + if (cmp) + cmp = (cmp < 0 ? -1 : 1); + + QCOMPARE(cmp, result); + // basic tests: QCOMPARE(str1 == str2, isEqual); QCOMPARE(str1 < str2, isLess); @@ -2188,6 +2222,51 @@ void tst_QByteArray::toUpperLower() QCOMPARE(qMove(copy).toUpper(), upper); } +void tst_QByteArray::isUpper() +{ + QVERIFY(!QByteArray().isUpper()); + QVERIFY(!QByteArray("").isUpper()); + QVERIFY(QByteArray("TEXT").isUpper()); + QVERIFY(QByteArray("\xD0\xDE").isUpper()); + QVERIFY(!QByteArray("\xD7").isUpper()); // multiplication sign is not upper + QVERIFY(!QByteArray("\xDF").isUpper()); // sz ligature is not upper + QVERIFY(!QByteArray("text").isUpper()); + QVERIFY(!QByteArray("Text").isUpper()); + QVERIFY(!QByteArray("tExt").isUpper()); + QVERIFY(!QByteArray("teXt").isUpper()); + QVERIFY(!QByteArray("texT").isUpper()); + QVERIFY(!QByteArray("TExt").isUpper()); + QVERIFY(!QByteArray("teXT").isUpper()); + QVERIFY(!QByteArray("tEXt").isUpper()); + QVERIFY(!QByteArray("tExT").isUpper()); + QVERIFY(!QByteArray("@ABYZ[").isUpper()); + QVERIFY(!QByteArray("@abyz[").isUpper()); + QVERIFY(!QByteArray("`ABYZ{").isUpper()); + QVERIFY(!QByteArray("`abyz{").isUpper()); +} + +void tst_QByteArray::isLower() +{ + QVERIFY(!QByteArray().isLower()); + QVERIFY(!QByteArray("").isLower()); + QVERIFY(QByteArray("text").isLower()); + QVERIFY(QByteArray("\xE0\xFF").isLower()); + QVERIFY(!QByteArray("\xF7").isLower()); // division sign is not lower + QVERIFY(!QByteArray("Text").isLower()); + QVERIFY(!QByteArray("tExt").isLower()); + QVERIFY(!QByteArray("teXt").isLower()); + QVERIFY(!QByteArray("texT").isLower()); + QVERIFY(!QByteArray("TExt").isLower()); + QVERIFY(!QByteArray("teXT").isLower()); + QVERIFY(!QByteArray("tEXt").isLower()); + QVERIFY(!QByteArray("tExT").isLower()); + QVERIFY(!QByteArray("TEXT").isLower()); + QVERIFY(!QByteArray("@ABYZ[").isLower()); + QVERIFY(!QByteArray("@abyz[").isLower()); + QVERIFY(!QByteArray("`ABYZ{").isLower()); + QVERIFY(!QByteArray("`abyz{").isLower()); +} + void tst_QByteArray::macTypes() { #ifndef Q_OS_MAC diff --git a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp index 17a0f3edd9..3eef7631c8 100644 --- a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp +++ b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp @@ -29,6 +29,7 @@ #include <QtCore/QCoreApplication> #include <QtTest/QtTest> +#include <QtCore/QMetaEnum> Q_DECLARE_METATYPE(QCryptographicHash::Algorithm) @@ -45,6 +46,7 @@ private slots: void sha3(); void files_data(); void files(); + void hashLength(); }; void tst_QCryptographicHash::repeated_result_data() @@ -291,6 +293,15 @@ void tst_QCryptographicHash::files() } } +void tst_QCryptographicHash::hashLength() +{ + auto metaEnum = QMetaEnum::fromType<QCryptographicHash::Algorithm>(); + for (int i = 0, value = metaEnum.value(i); value != -1; value = metaEnum.value(++i)) { + auto algorithm = QCryptographicHash::Algorithm(value); + QByteArray output = QCryptographicHash::hash(QByteArrayLiteral("test"), algorithm); + QCOMPARE(QCryptographicHash::hashLength(algorithm), output.length()); + } +} QTEST_MAIN(tst_QCryptographicHash) #include "tst_qcryptographichash.moc" diff --git a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp index f545ead1f1..e89e634841 100644 --- a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp +++ b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp @@ -159,6 +159,8 @@ void tst_QExplicitlySharedDataPointer::data() const { QExplicitlySharedDataPointer<const MyClass> pointer; QCOMPARE(pointer.data(), static_cast<const MyClass *>(0)); + QVERIFY(pointer == nullptr); + QVERIFY(nullptr == pointer); } /* On const pointer. Must not mutate the pointer. */ @@ -168,6 +170,9 @@ void tst_QExplicitlySharedDataPointer::data() const /* Check that this cast is possible. */ static_cast<const MyClass *>(pointer.data()); + + QVERIFY(! (pointer == nullptr)); + QVERIFY(! (nullptr == pointer)); } /* On mutatable pointer. Must not mutate the pointer. */ diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro deleted file mode 100644 index a27286ff20..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qregularexpression_alwaysoptimize -QT = core testlib -HEADERS = ../tst_qregularexpression.h -SOURCES = \ - tst_qregularexpression_alwaysoptimize.cpp \ - ../tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp deleted file mode 100644 index 6d2ae48235..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include "../tst_qregularexpression.h" - -class tst_QRegularExpression_AlwaysOptimize : public tst_QRegularExpression -{ - Q_OBJECT - -private slots: - void initTestCase(); -}; - -QT_BEGIN_NAMESPACE -extern Q_CORE_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count; // from qregularexpression.cpp -QT_END_NAMESPACE - -void tst_QRegularExpression_AlwaysOptimize::initTestCase() -{ - qt_qregularexpression_optimize_after_use_count = 1; -} - -QTEST_APPLESS_MAIN(tst_QRegularExpression_AlwaysOptimize) - -#include "tst_qregularexpression_alwaysoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro deleted file mode 100644 index 0b36c79c1b..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qregularexpression_defaultoptimize -QT = core testlib -HEADERS = ../tst_qregularexpression.h -SOURCES = \ - tst_qregularexpression_defaultoptimize.cpp \ - ../tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp deleted file mode 100644 index a815c6cab9..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include "../tst_qregularexpression.h" - -class tst_QRegularExpression_DefaultOptimize : public tst_QRegularExpression -{ - Q_OBJECT -}; - -QTEST_APPLESS_MAIN(tst_QRegularExpression_DefaultOptimize) - -#include "tst_qregularexpression_defaultoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro b/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro deleted file mode 100644 index 1db77781dd..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -TARGET = tst_qregularexpression_forceoptimize -QT = core testlib -HEADERS = ../tst_qregularexpression.h -SOURCES = \ - tst_qregularexpression_forceoptimize.cpp \ - ../tst_qregularexpression.cpp -DEFINES += forceOptimize=true diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp deleted file mode 100644 index 38a3a64fa3..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include "../tst_qregularexpression.h" - -class tst_QRegularExpression_ForceOptimize : public tst_QRegularExpression -{ - Q_OBJECT -}; - -QTEST_APPLESS_MAIN(tst_QRegularExpression_ForceOptimize) - -#include "tst_qregularexpression_forceoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro index e1840808ff..ec8189717e 100644 --- a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro +++ b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro @@ -1,3 +1,4 @@ -TEMPLATE = subdirs -SUBDIRS = defaultoptimize forceoptimize -qtConfig(private_tests): SUBDIRS += alwaysoptimize +CONFIG += testcase +TARGET = tst_qregularexpression +QT = core testlib +SOURCES = tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp index c828551e44..5130b7cfcd 100644 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp @@ -33,11 +33,60 @@ #include <qstringlist.h> #include <qhash.h> -#include "tst_qregularexpression.h" +#include <qobject.h> +#include <qregularexpression.h> +#include <qthread.h> -#ifndef forceOptimize -#define forceOptimize false -#endif +Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) +Q_DECLARE_METATYPE(QRegularExpression::MatchType) +Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) + +class tst_QRegularExpression : public QObject +{ + Q_OBJECT + +private slots: + void defaultConstructors(); + void gettersSetters_data(); + void gettersSetters(); + void escape_data(); + void escape(); + void validity_data(); + void validity(); + void patternOptions_data(); + void patternOptions(); + void normalMatch_data(); + void normalMatch(); + void partialMatch_data(); + void partialMatch(); + void globalMatch_data(); + void globalMatch(); + void serialize_data(); + void serialize(); + void operatoreq_data(); + void operatoreq(); + void captureCount_data(); + void captureCount(); + void captureNames_data(); + void captureNames(); + void pcreJitStackUsage_data(); + void pcreJitStackUsage(); + void regularExpressionMatch_data(); + void regularExpressionMatch(); + void JOptionUsage_data(); + void JOptionUsage(); + void QStringAndQStringRefEquivalence(); + void threadSafety_data(); + void threadSafety(); + + void wildcard_data(); + void wildcard(); + void testInvalidWildcard_data(); + void testInvalidWildcard(); + +private: + void provideRegularExpressions(); +}; struct Match { @@ -292,9 +341,6 @@ static void testMatch(const QRegularExpression ®exp, QRegularExpression::MatchOptions matchOptions, const Result &result) { - if (forceOptimize) - regexp.optimize(); - // test with QString as subject type testMatchImpl<QREMatch>(regexp, matchingMethodForString, subject, offset, matchType, matchOptions, result); @@ -401,30 +447,22 @@ void tst_QRegularExpression::gettersSetters() { QRegularExpression re; re.setPattern(pattern); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); } { QRegularExpression re; re.setPatternOptions(patternOptions); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), QString()); QCOMPARE(re.patternOptions(), patternOptions); } { QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); } { QRegularExpression re(pattern, patternOptions); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), patternOptions); } @@ -465,8 +503,6 @@ void tst_QRegularExpression::escape() QFETCH(QString, escaped); QCOMPARE(QRegularExpression::escape(string), escaped); QRegularExpression re(escaped); - if (forceOptimize) - re.optimize(); QCOMPARE(re.isValid(), true); } @@ -497,8 +533,6 @@ void tst_QRegularExpression::validity() QFETCH(QString, pattern); QFETCH(bool, validity); QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); QCOMPARE(re.isValid(), validity); if (!validity) QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); @@ -585,9 +619,6 @@ void tst_QRegularExpression::patternOptions() QFETCH(QString, subject); QFETCH(Match, match); - if (forceOptimize) - regexp.optimize(); - QRegularExpressionMatch m = regexp.match(subject); consistencyCheck(m); QVERIFY(m == match); @@ -1403,9 +1434,6 @@ void tst_QRegularExpression::serialize() QFETCH(QRegularExpression::PatternOptions, patternOptions); QRegularExpression outRe(pattern, patternOptions); - if (forceOptimize) - outRe.optimize(); - QByteArray buffer; { QDataStream out(&buffer, QIODevice::WriteOnly); @@ -1468,33 +1496,18 @@ void tst_QRegularExpression::operatoreq() QRegularExpression re1(pattern); QRegularExpression re2(pattern); - if (forceOptimize) - re1.optimize(); - if (forceOptimize) - re2.optimize(); - verifyEquality(re1, re2); } { QRegularExpression re1(QString(), patternOptions); QRegularExpression re2(QString(), patternOptions); - if (forceOptimize) - re1.optimize(); - if (forceOptimize) - re2.optimize(); - verifyEquality(re1, re2); } { QRegularExpression re1(pattern, patternOptions); QRegularExpression re2(pattern, patternOptions); - if (forceOptimize) - re1.optimize(); - if (forceOptimize) - re2.optimize(); - verifyEquality(re1, re2); } } @@ -1524,9 +1537,6 @@ void tst_QRegularExpression::captureCount() QFETCH(QString, pattern); QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QTEST(re.captureCount(), "captureCount"); if (!re.isValid()) QCOMPARE(re.captureCount(), -1); @@ -1595,9 +1605,6 @@ void tst_QRegularExpression::captureNames() QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QStringList namedCaptureGroups = re.namedCaptureGroups(); int namedCaptureGroupsCount = namedCaptureGroups.size(); @@ -1633,9 +1640,6 @@ void tst_QRegularExpression::pcreJitStackUsage() QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QVERIFY(re.isValid()); QRegularExpressionMatch match = re.match(subject); consistencyCheck(match); @@ -1663,9 +1667,6 @@ void tst_QRegularExpression::regularExpressionMatch() QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QVERIFY(re.isValid()); QRegularExpressionMatch match = re.match(subject); consistencyCheck(match); @@ -1705,8 +1706,6 @@ void tst_QRegularExpression::JOptionUsage() QRegularExpression re(pattern); if (isValid && JOptionUsed) QTest::ignoreMessage(QtWarningMsg, qPrintable(warningMessage.arg(pattern))); - if (forceOptimize) - re.optimize(); QCOMPARE(re.isValid(), isValid); } @@ -2066,3 +2065,166 @@ void tst_QRegularExpression::QStringAndQStringRefEquivalence() } } } + +class MatcherThread : public QThread +{ +public: + explicit MatcherThread(const QRegularExpression &re, const QString &subject, QObject *parent = nullptr) + : QThread(parent), + m_re(re), + m_subject(subject) + { + } + +private: + static const int MATCH_ITERATIONS = 50; + + void run() override + { + yieldCurrentThread(); + for (int i = 0; i < MATCH_ITERATIONS; ++i) + m_re.match(m_subject); + } + + const QRegularExpression &m_re; + const QString &m_subject; +}; + +void tst_QRegularExpression::threadSafety_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<QString>("subject"); + + int i = 0; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abcd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abbbbcccd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abababcd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abcabcd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abccccccababd"; + + { + QString subject(512*1024, QLatin1Char('x')); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } + + { + QString subject = "ab"; + subject.append(QString(512*1024, QLatin1Char('x'))); + subject.append("c"); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } + + { + QString subject = "ab"; + subject.append(QString(512*1024, QLatin1Char('x'))); + subject.append("cd"); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } + + QTest::addRow("pattern%d", ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaabcde"; + QTest::addRow("pattern%d", ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaaaaabcde"; +} + +void tst_QRegularExpression::threadSafety() +{ + QFETCH(QString, pattern); + QFETCH(QString, subject); + + static const int THREAD_SAFETY_ITERATIONS = 50; + + const int threadCount = qMax(QThread::idealThreadCount(), 4); + + for (int threadSafetyIteration = 0; threadSafetyIteration < THREAD_SAFETY_ITERATIONS; ++threadSafetyIteration) { + QRegularExpression re(pattern); + + QVector<MatcherThread *> threads; + for (int i = 0; i < threadCount; ++i) { + MatcherThread *thread = new MatcherThread(re, subject); + thread->start(); + threads.push_back(thread); + } + + for (int i = 0; i < threadCount; ++i) + threads[i]->wait(); + + qDeleteAll(threads); + } +} + +void tst_QRegularExpression::wildcard_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<QString>("string"); + QTest::addColumn<int>("foundIndex"); + + auto addRow = [](const char *pattern, const char *string, int foundIndex) { + QTest::newRow(pattern) << pattern << string << foundIndex; + }; + + addRow("*.html", "test.html", 0); + addRow("*.html", "test.htm", -1); + addRow("bar*", "foobarbaz", 3); + addRow("*", "Qt Rocks!", 0); + addRow(".html", "test.html", 4); + addRow(".h", "test.cpp", -1); + addRow(".???l", "test.html", 4); + addRow("?", "test.html", 0); + addRow("?m", "test.html", 6); + addRow(".h[a-z]ml", "test.html", 4); + addRow(".h[A-Z]ml", "test.html", -1); + addRow(".h[A-Z]ml", "test.hTml", 4); + addRow(".h[!A-Z]ml", "test.hTml", -1); + addRow(".h[!A-Z]ml", "test.html", 4); + addRow(".h[!T]ml", "test.hTml", -1); + addRow(".h[!T]ml", "test.html", 4); + addRow(".h[!T]m[!L]", "test.htmL", -1); + addRow(".h[!T]m[!L]", "test.html", 4); + addRow(".h[][!]", "test.h]ml", 4); + addRow(".h[][!]", "test.h[ml", 4); + addRow(".h[][!]", "test.h!ml", 4); +} + +void tst_QRegularExpression::wildcard() +{ + QFETCH(QString, pattern); + QFETCH(QString, string); + QFETCH(int, foundIndex); + + QRegularExpression re; + re.setWildcardPattern(pattern); + + QRegularExpressionMatch match = re.match(string); + + QCOMPARE(match.capturedStart(), foundIndex); +} + +void tst_QRegularExpression::testInvalidWildcard_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<bool>("isValid"); + + QTest::newRow("valid []") << "[abc]" << true; + QTest::newRow("valid ending ]") << "abc]" << true; + QTest::newRow("invalid [") << "[abc" << false; + QTest::newRow("ending [") << "abc[" << false; + QTest::newRow("ending [^") << "abc[^" << false; + QTest::newRow("ending [\\") << "abc[\\" << false; + QTest::newRow("ending []") << "abc[]" << false; + QTest::newRow("ending [[") << "abc[[" << false; +} + +void tst_QRegularExpression::testInvalidWildcard() +{ + QFETCH(QString, pattern); + + QRegularExpression re; + re.setWildcardPattern(pattern); + + QFETCH(bool, isValid); + QCOMPARE(re.isValid(), isValid); +} + +QTEST_APPLESS_MAIN(tst_QRegularExpression) + +#include "tst_qregularexpression.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h deleted file mode 100644 index 8bb4aa0cce..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <qobject.h> -#include <qregularexpression.h> - -Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) -Q_DECLARE_METATYPE(QRegularExpression::MatchType) -Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) - -class tst_QRegularExpression : public QObject -{ - Q_OBJECT - -private slots: - void defaultConstructors(); - void gettersSetters_data(); - void gettersSetters(); - void escape_data(); - void escape(); - void validity_data(); - void validity(); - void patternOptions_data(); - void patternOptions(); - void normalMatch_data(); - void normalMatch(); - void partialMatch_data(); - void partialMatch(); - void globalMatch_data(); - void globalMatch(); - void serialize_data(); - void serialize(); - void operatoreq_data(); - void operatoreq(); - void captureCount_data(); - void captureCount(); - void captureNames_data(); - void captureNames(); - void pcreJitStackUsage_data(); - void pcreJitStackUsage(); - void regularExpressionMatch_data(); - void regularExpressionMatch(); - void JOptionUsage_data(); - void JOptionUsage(); - void QStringAndQStringRefEquivalence(); - -private: - void provideRegularExpressions(); -}; diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 2074c9789a..cdabd51d43 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -450,6 +450,8 @@ private slots: void trimmed(); void toUpper(); void toLower(); + void isUpper(); + void isLower(); void toCaseFolded(); void rightJustified(); void leftJustified(); @@ -529,10 +531,8 @@ private slots: void integer_conversion(); void tortureSprintfDouble(); void toNum(); -#if !defined(Q_OS_WIN) void localeAwareCompare_data(); void localeAwareCompare(); -#endif void reverseIterators(); void split_data(); void split(); @@ -2315,6 +2315,46 @@ void tst_QString::toLower() #endif } +void tst_QString::isUpper() +{ + QVERIFY(!QString().isUpper()); + QVERIFY(!QString("").isUpper()); + QVERIFY(QString("TEXT").isUpper()); + QVERIFY(!QString("text").isUpper()); + QVERIFY(!QString("Text").isUpper()); + QVERIFY(!QString("tExt").isUpper()); + QVERIFY(!QString("teXt").isUpper()); + QVERIFY(!QString("texT").isUpper()); + QVERIFY(!QString("TExt").isUpper()); + QVERIFY(!QString("teXT").isUpper()); + QVERIFY(!QString("tEXt").isUpper()); + QVERIFY(!QString("tExT").isUpper()); + QVERIFY(!QString("@ABYZ[").isUpper()); + QVERIFY(!QString("@abyz[").isUpper()); + QVERIFY(!QString("`ABYZ{").isUpper()); + QVERIFY(!QString("`abyz{").isUpper()); +} + +void tst_QString::isLower() +{ + QVERIFY(!QString().isLower()); + QVERIFY(!QString("").isLower()); + QVERIFY(QString("text").isLower()); + QVERIFY(!QString("Text").isLower()); + QVERIFY(!QString("tExt").isLower()); + QVERIFY(!QString("teXt").isLower()); + QVERIFY(!QString("texT").isLower()); + QVERIFY(!QString("TExt").isLower()); + QVERIFY(!QString("teXT").isLower()); + QVERIFY(!QString("tEXt").isLower()); + QVERIFY(!QString("tExT").isLower()); + QVERIFY(!QString("TEXT").isLower()); + QVERIFY(!QString("@ABYZ[").isLower()); + QVERIFY(!QString("@abyz[").isLower()); + QVERIFY(!QString("`ABYZ{").isLower()); + QVERIFY(!QString("`abyz{").isLower()); +} + void tst_QString::toCaseFolded() { QCOMPARE( QString().toCaseFolded(), QString() ); @@ -5468,8 +5508,6 @@ void tst_QString::tortureSprintfDouble() #include <locale.h> -#if !defined(Q_OS_WIN) -// On Q_OS_WIN, we cannot set the system or user locale void tst_QString::localeAwareCompare_data() { QTest::addColumn<QString>("locale"); @@ -5477,6 +5515,15 @@ void tst_QString::localeAwareCompare_data() QTest::addColumn<QString>("s2"); QTest::addColumn<int>("result"); + // Compare decomposed and composed form + { + // From ES6 test262 test suite (built-ins/String/prototype/localeCompare/15.5.4.9_CE.js), addressing from Unicode 5.0, chapter 3.12. Boils + // down to this one-liner: console.log("\u1111\u1171\u11B6".localeCompare("\ud4db") + QTest::newRow("normalize") << QString() << QString::fromUtf8("\xED\x93\x9B") << QString::fromUtf8("\xE1\x84\x91\xE1\x85\xB1\xE1\x86\xB6") << 0; + } + +#if !defined(Q_OS_WIN) +// On Q_OS_WIN, we cannot set the system or user locale /* The C locale performs pure byte comparisons for Latin-1-specific characters (I think). Compare with Swedish @@ -5537,6 +5584,7 @@ void tst_QString::localeAwareCompare_data() QTest::newRow("german2") << QString("de_DE") << QString::fromLatin1("\xe4") << QString::fromLatin1("\xf6") << -1; QTest::newRow("german3") << QString("de_DE") << QString::fromLatin1("z") << QString::fromLatin1("\xf6") << 1; #endif +#endif //!defined(Q_OS_WIN) } void tst_QString::localeAwareCompare() @@ -5549,17 +5597,17 @@ void tst_QString::localeAwareCompare() QStringRef r1(&s1, 0, s1.length()); QStringRef r2(&s2, 0, s2.length()); + if (!locale.isEmpty()) { #if defined (Q_OS_DARWIN) || defined(QT_USE_ICU) - QSKIP("Setting the locale is not supported on OS X or ICU (you can set the C locale, but that won't affect localeAwareCompare)"); + QSKIP("Setting the locale is not supported on OS X or ICU (you can set the C locale, but that won't affect localeAwareCompare)"); #else - if (!locale.isEmpty()) { const char *newLocale = setlocale(LC_ALL, locale.toLatin1()); if (!newLocale) { setlocale(LC_ALL, ""); QSKIP("Please install the proper locale on this machine to test properly"); } - } #endif + } #ifdef QT_USE_ICU // ### for c1, ICU disagrees with libc on how to compare @@ -5614,7 +5662,6 @@ void tst_QString::localeAwareCompare() if (!locale.isEmpty()) setlocale(LC_ALL, ""); } -#endif //!defined(Q_OS_WIN) void tst_QString::reverseIterators() { diff --git a/tests/auto/corelib/tools/qtimezone/qtimezone.pro b/tests/auto/corelib/tools/qtimezone/qtimezone.pro index 19ebc13306..5ec8d008e7 100644 --- a/tests/auto/corelib/tools/qtimezone/qtimezone.pro +++ b/tests/auto/corelib/tools/qtimezone/qtimezone.pro @@ -3,7 +3,7 @@ TARGET = tst_qtimezone QT = core-private testlib SOURCES = tst_qtimezone.cpp qtConfig(icu) { - DEFINES += QT_USE_ICU + QMAKE_USE_PRIVATE += icu } darwin { diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp index ed80d4528d..d335dae7bc 100644 --- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp @@ -31,6 +31,10 @@ #include <private/qtimezoneprivate_p.h> #include <qlocale.h> +#if defined(Q_OS_WIN) && !QT_CONFIG(icu) +# define USING_WIN_TZ +#endif + class tst_QTimeZone : public QObject { Q_OBJECT @@ -413,7 +417,7 @@ void tst_QTimeZone::specificTransition_data() QTest::addColumn<int>("dstoff"); // Moscow ditched DST on 2010-10-31 but has since changed standard offset twice. -#ifdef Q_OS_WIN +#ifdef USING_WIN_TZ // Win7 is too old to know about this transition: if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) #endif @@ -477,8 +481,9 @@ void tst_QTimeZone::transitionEachZone_data() }; QString name; + const auto zones = QTimeZone::availableTimeZoneIds(); for (int k = sizeof(table) / sizeof(table[0]); k-- > 0; ) { - foreach (QByteArray zone, QTimeZone::availableTimeZoneIds()) { + for (const QByteArray &zone : zones) { name.sprintf("%s@%d", zone.constData(), table[k].year); QTest::newRow(name.toUtf8().constData()) << zone @@ -500,7 +505,7 @@ void tst_QTimeZone::transitionEachZone() QTimeZone named(zone); for (int i = start; i < stop; i++) { -#ifdef Q_OS_WIN +#ifdef USING_WIN_TZ // See QTBUG-64985: MS's TZ APIs' misdescription of Europe/Samara leads // to mis-disambiguation of its fall-back here. if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::Windows7 @@ -833,7 +838,7 @@ void tst_QTimeZone::utcTest() void tst_QTimeZone::icuTest() { -#if defined(QT_BUILD_INTERNAL) && defined(QT_USE_ICU) +#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(icu) // Known datetimes qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); @@ -881,7 +886,7 @@ void tst_QTimeZone::icuTest() testCetPrivate(tzp); testEpochTranPrivate(QIcuTimeZonePrivate("America/Toronto")); -#endif // QT_USE_ICU +#endif // icu } void tst_QTimeZone::tzTest() @@ -907,7 +912,7 @@ void tst_QTimeZone::tzTest() QLocale enUS("en_US"); // Only test names in debug mode, names used can vary by ICU version installed if (debug) { -#ifdef QT_USE_ICU +#if QT_CONFIG(icu) QCOMPARE(tzp.displayName(QTimeZone::StandardTime, QTimeZone::LongName, enUS), QString("Central European Standard Time")); QCOMPARE(tzp.displayName(QTimeZone::StandardTime, QTimeZone::ShortName, enUS), @@ -946,7 +951,7 @@ void tst_QTimeZone::tzTest() QString("CET")); QCOMPARE(tzp.displayName(QTimeZone::GenericTime, QTimeZone::OffsetName, enUS), QString("CET")); -#endif // QT_USE_ICU +#endif // icu // Test Abbreviations QCOMPARE(tzp.abbreviation(std), QString("CET")); @@ -1123,7 +1128,7 @@ void tst_QTimeZone::darwinTypes() void tst_QTimeZone::winTest() { -#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_WIN) +#if defined(QT_BUILD_INTERNAL) && defined(USING_WIN_TZ) // Known datetimes qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); @@ -1174,7 +1179,7 @@ void tst_QTimeZone::winTest() testCetPrivate(tzp); testEpochTranPrivate(QWinTimeZonePrivate("America/Toronto")); -#endif // Q_OS_WIN +#endif // QT_BUILD_INTERNAL && USING_WIN_TZ } #ifdef QT_BUILD_INTERNAL @@ -1286,7 +1291,7 @@ void tst_QTimeZone::testEpochTranPrivate(const QTimeZonePrivate &tzp) // 1970-04-26 02:00 EST, -5 -> -4 const QDateTime after = QDateTime(QDate(1970, 4, 26), QTime(2, 0), Qt::OffsetFromUTC, -5 * 3600); const QDateTime found = QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC); -#ifdef Q_OS_WIN // MS gets the date wrong: 5th April instead of 26th. +#ifdef USING_WIN_TZ // MS gets the date wrong: 5th April instead of 26th. QCOMPARE(found.toOffsetFromUtc(-5 * 3600).time(), after.time()); #else QCOMPARE(found, after); |