diff options
Diffstat (limited to 'tests/auto/corelib')
160 files changed, 6780 insertions, 1261 deletions
diff --git a/tests/auto/corelib/animation/qparallelanimationgroup/BLACKLIST b/tests/auto/corelib/animation/qparallelanimationgroup/BLACKLIST index fe1d5bd968..b5b37b4498 100644 --- a/tests/auto/corelib/animation/qparallelanimationgroup/BLACKLIST +++ b/tests/auto/corelib/animation/qparallelanimationgroup/BLACKLIST @@ -1,2 +1,3 @@ [deleteChildrenWithRunningGroup] osx-10.12 +osx-10.13 diff --git a/tests/auto/corelib/animation/qsequentialanimationgroup/BLACKLIST b/tests/auto/corelib/animation/qsequentialanimationgroup/BLACKLIST index 2a31afd735..4c999ff88f 100644 --- a/tests/auto/corelib/animation/qsequentialanimationgroup/BLACKLIST +++ b/tests/auto/corelib/animation/qsequentialanimationgroup/BLACKLIST @@ -3,5 +3,6 @@ windows [finishWithUncontrolledAnimation] windows osx-10.12 +osx-10.13 [groupWithZeroDurationAnimations] osx diff --git a/tests/auto/corelib/codecs/qtextcodec/echo/echo.pro b/tests/auto/corelib/codecs/qtextcodec/echo/echo.pro index bf791ffc61..512da8939b 100644 --- a/tests/auto/corelib/codecs/qtextcodec/echo/echo.pro +++ b/tests/auto/corelib/codecs/qtextcodec/echo/echo.pro @@ -1,6 +1,4 @@ SOURCES += main.cpp QT = core -CONFIG -= app_bundle debug_and_release_target -CONFIG += console - +load(qt_test_helper) diff --git a/tests/auto/corelib/codecs/qtextcodec/qtextcodec.pro b/tests/auto/corelib/codecs/qtextcodec/qtextcodec.pro index 302d887fc7..15de02a42d 100644 --- a/tests/auto/corelib/codecs/qtextcodec/qtextcodec.pro +++ b/tests/auto/corelib/codecs/qtextcodec/qtextcodec.pro @@ -1,2 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = test echo +SUBDIRS = test.pro +unix: SUBDIRS += echo diff --git a/tests/auto/corelib/codecs/qtextcodec/test.pro b/tests/auto/corelib/codecs/qtextcodec/test.pro new file mode 100644 index 0000000000..7505c5ad51 --- /dev/null +++ b/tests/auto/corelib/codecs/qtextcodec/test.pro @@ -0,0 +1,6 @@ +CONFIG += testcase +QT = core testlib +SOURCES = tst_qtextcodec.cpp + +TARGET = tst_qtextcodec +TESTDATA += *.txt diff --git a/tests/auto/corelib/codecs/qtextcodec/test/test.pro b/tests/auto/corelib/codecs/qtextcodec/test/test.pro deleted file mode 100644 index e0a1bbd88e..0000000000 --- a/tests/auto/corelib/codecs/qtextcodec/test/test.pro +++ /dev/null @@ -1,13 +0,0 @@ -CONFIG += testcase -QT = core testlib -SOURCES = ../tst_qtextcodec.cpp - -TARGET = ../tst_qtextcodec -win32 { - CONFIG(debug, debug|release) { - TARGET = ../../debug/tst_qtextcodec - } else { - TARGET = ../../release/tst_qtextcodec - } -} -TESTDATA += ../*.txt diff --git a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp index f8f9387abb..6cadebfd7f 100644 --- a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp +++ b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp @@ -2092,7 +2092,7 @@ void tst_QTextCodec::toLocal8Bit() QSKIP("No qprocess support", SkipAll); #else QProcess process; - process.start("echo/echo"); + process.start("echo_helper"); QString string(QChar(0x410)); process.write((const char*)string.utf16(), string.length()*2); @@ -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/codecs/utf8/tst_utf8.cpp b/tests/auto/corelib/codecs/utf8/tst_utf8.cpp index 8f78aa937c..9ce1748e72 100644 --- a/tests/auto/corelib/codecs/utf8/tst_utf8.cpp +++ b/tests/auto/corelib/codecs/utf8/tst_utf8.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -71,7 +71,7 @@ void tst_Utf8::initTestCase() // is the locale UTF-8? if (QString(QChar(QChar::ReplacementCharacter)).toLocal8Bit() == "\xEF\xBF\xBD") { QTest::newRow("localecodec") << true; - qDebug() << "locale is utf8"; + qInfo() << "locale is utf8"; } } @@ -227,6 +227,15 @@ void tst_Utf8::invalidUtf8() // GNU libc's iconv is known to accept U+FFFF and U+FFFE encoded as UTF-8 // OS X's iconv is known to accept those, plus surrogates and codepoints above U+10FFFF if (!useLocale) + QVERIFY(decoder->hasFailure() || decoder->needsMoreData()); + else if (!decoder->hasFailure() && !decoder->needsMoreData()) + qWarning("System codec does not report failure when it should. Should report bug upstream."); + + // add a continuation character and test that we don't accidentally use it + // (buffer overrun) + utf8 += char(0x80 | 0x3f); + decoder->toUnicode(utf8.constData(), utf8.size() - 1); + if (!useLocale) QVERIFY(decoder->hasFailure()); else if (!decoder->hasFailure()) qWarning("System codec does not report failure when it should. Should report bug upstream."); diff --git a/tests/auto/corelib/codecs/utf8/utf8data.cpp b/tests/auto/corelib/codecs/utf8/utf8data.cpp index 2267dc8514..221e1d5579 100644 --- a/tests/auto/corelib/codecs/utf8/utf8data.cpp +++ b/tests/auto/corelib/codecs/utf8/utf8data.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -29,15 +30,24 @@ void loadInvalidUtf8Rows() { - QTest::newRow("1char") << QByteArray("\x80"); - QTest::newRow("2chars-1") << QByteArray("\xC2\xC0"); - QTest::newRow("2chars-2") << QByteArray("\xC3\xDF"); - QTest::newRow("2chars-3") << QByteArray("\xC7\xF0"); - QTest::newRow("3chars-1") << QByteArray("\xE0\xA0\xC0"); - QTest::newRow("3chars-2") << QByteArray("\xE0\xC0\xA0"); - QTest::newRow("4chars-1") << QByteArray("\xF0\x90\x80\xC0"); - QTest::newRow("4chars-2") << QByteArray("\xF0\x90\xC0\x80"); - QTest::newRow("4chars-3") << QByteArray("\xF0\xC0\x80\x80"); + // Wrong continuations + QTest::newRow("bad-continuation-1char") << QByteArray("\x80"); + QTest::newRow("bad-continuation-2chars-1") << QByteArray("\xC2\xC0"); + QTest::newRow("bad-continuation-2chars-2") << QByteArray("\xC3\xDF"); + QTest::newRow("bad-continuation-2chars-3") << QByteArray("\xC7\xF0"); + QTest::newRow("bad-continuation-3chars-1") << QByteArray("\xE0\xA0\xC0"); + QTest::newRow("bad-continuation-3chars-2") << QByteArray("\xE0\xC0\xA0"); + QTest::newRow("bad-continuation-4chars-1") << QByteArray("\xF0\x90\x80\xC0"); + QTest::newRow("bad-continuation-4chars-2") << QByteArray("\xF0\x90\xC0\x80"); + QTest::newRow("bad-continuation-4chars-3") << QByteArray("\xF0\xC0\x80\x80"); + + // Too short + QTest::newRow("too-short-2chars") << QByteArray("\xC2"); + QTest::newRow("too-short-3chars-1") << QByteArray("\xE0"); + QTest::newRow("too-short-3chars-2") << QByteArray("\xE0\xA0"); + QTest::newRow("too-short-4chars-1") << QByteArray("\xF0"); + QTest::newRow("too-short-4chars-2") << QByteArray("\xF0\x90"); + QTest::newRow("too-short-4chars-3") << QByteArray("\xF0\x90\x80"); // Surrogate pairs must now be present either // U+D800: 1101 10 0000 00 0000 diff --git a/tests/auto/corelib/global/qflags/tst_qflags.cpp b/tests/auto/corelib/global/qflags/tst_qflags.cpp index 2f1b56629a..72b086350e 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 @@ -120,7 +121,7 @@ void tst_QFlags::constExpr() QVERIFY(verifyConstExpr<uint(Qt::MouseButtons(Qt::RightButton) & 0xff)>(Qt::RightButton)); QVERIFY(verifyConstExpr<uint(Qt::MouseButtons(Qt::RightButton) | 0xff)>(0xff)); - QVERIFY(!verifyConstExpr<Qt::RightButton>(!Qt::MouseButtons(Qt::LeftButton))); + QVERIFY(!verifyConstExpr<Qt::RightButton>(~Qt::MouseButtons(Qt::LeftButton))); #if defined(__cpp_constexpr) && __cpp_constexpr-0 >= 201304 QVERIFY(verifyConstExpr<uint(testRelaxedConstExpr())>(Qt::MiddleButton)); @@ -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/qgetputenv/tst_qgetputenv.cpp b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp index f02e902468..544cb1bf07 100644 --- a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp +++ b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp @@ -160,8 +160,10 @@ void tst_QGetPutEnv::intValue_data() // some repetition from what is tested in getSetCheck() QTest::newRow("empty") << QByteArray() << 0 << false; - QTest::newRow("spaces-heading") << QByteArray(" 1") << 1 << true; - QTest::newRow("spaces-trailing") << QByteArray("1 ") << 0 << false; + QTest::newRow("spaces-heading") << QByteArray(" \n\r\t1") << 1 << true; + QTest::newRow("spaces-trailing") << QByteArray("1 \n\r\t") << 1 << true; + QTest::newRow("junk-heading") << QByteArray("x1") << 0 << false; + QTest::newRow("junk-trailing") << QByteArray("1x") << 0 << false; #define ROW(x, i, b) \ QTest::newRow(#x) << QByteArray(#x) << (i) << (b) 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/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp index 7a6842d144..7c04611823 100644 --- a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp +++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp @@ -52,7 +52,7 @@ // values chosen at random static const quint32 RandomValue32 = 0x4d1169f1U; static const quint64 RandomValue64 = Q_UINT64_C(0x3ce63161b998aa91); -static const double RandomValueFP = double(0.3010463714599609f); +static const double RandomValueFP = double(0.3010463714599609); static void setRNGControl(uint v) { diff --git a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp index 6934818dcf..2345bb39c1 100644 --- a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp +++ b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp @@ -39,9 +39,17 @@ class tst_QtEndian: public QObject private slots: void fromBigEndian(); void fromLittleEndian(); + void fromBigEndianRegion_data(); + void fromBigEndianRegion(); + void fromLittleEndianRegion_data() { fromBigEndianRegion_data(); } + void fromLittleEndianRegion(); void toBigEndian(); void toLittleEndian(); + void toBigEndianRegion_data() { fromBigEndianRegion_data(); } + void toBigEndianRegion(); + void toLittleEndianRegion_data() { fromBigEndianRegion_data(); } + void toLittleEndianRegion(); void endianIntegers_data(); void endianIntegers(); @@ -56,18 +64,58 @@ struct TestData quint16 data16; quint8 data8; + float dataFloat; + double dataDouble; + quint8 reserved; }; +template <typename T> T getData(const TestData &d); +template <> quint8 getData(const TestData &d) { return d.data8; } +template <> quint16 getData(const TestData &d) { return d.data16; } +template <> quint32 getData(const TestData &d) { return d.data32; } +template <> quint64 getData(const TestData &d) { return d.data64; } +template <> float getData(const TestData &d) { return d.dataFloat; } + union RawTestData { uchar rawData[sizeof(TestData)]; TestData data; }; -static const TestData inNativeEndian = { Q_UINT64_C(0x0123456789abcdef), 0x00c0ffee, 0xcafe, 0xcf, '\0' }; -static const RawTestData inBigEndian = { "\x01\x23\x45\x67\x89\xab\xcd\xef" "\x00\xc0\xff\xee" "\xca\xfe" "\xcf" }; -static const RawTestData inLittleEndian = { "\xef\xcd\xab\x89\x67\x45\x23\x01" "\xee\xff\xc0\x00" "\xfe\xca" "\xcf" }; +template <typename Float> +Float int2Float(typename QIntegerForSizeof<Float>::Unsigned i) +{ + Float result = 0; + memcpy(reinterpret_cast<char *>(&result), reinterpret_cast<const char *>(&i), sizeof (Float)); + return result; +} + +static const TestData inNativeEndian = { + Q_UINT64_C(0x0123456789abcdef), + 0x00c0ffee, + 0xcafe, + 0xcf, + int2Float<float>(0x00c0ffeeU), + int2Float<double>(Q_UINT64_C(0x0123456789abcdef)), + '\0' +}; +static const RawTestData inBigEndian = { + "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\x00\xc0\xff\xee" + "\xca\xfe" + "\xcf" + "\x00\xc0\xff\xee" + "\x01\x23\x45\x67\x89\xab\xcd\xef" +}; +static const RawTestData inLittleEndian = { + "\xef\xcd\xab\x89\x67\x45\x23\x01" + "\xee\xff\xc0\x00" + "\xfe\xca" + "\xcf" + "\xee\xff\xc0\x00" + "\xef\xcd\xab\x89\x67\x45\x23\x01" +}; #define EXPAND_ENDIAN_TEST(endian) \ do { \ @@ -108,6 +156,106 @@ void tst_QtEndian::fromLittleEndian() #undef ENDIAN_TEST +template <typename T> +void transformRegion_template(T (*transformOne)(T), void (*transformRegion)(const void *, qsizetype, void *)) +{ + enum { Size = 64 }; + T source[Size]; + T dest[Size]; + T expected = transformOne(getData<T>(inNativeEndian)); + std::fill_n(source, +Size, getData<T>(inNativeEndian)); + memset(dest, 0, sizeof(dest)); + + auto checkBounds = [&](int from) { + for ( ; from < Size; ++from) + QCOMPARE(dest[from], T(0)); + }; + + transformRegion(source, 1, dest); + QCOMPARE(dest[0], expected); + checkBounds(1); + memset(dest, 0, sizeof(T)); + + transformRegion(source, 2, dest); + QCOMPARE(dest[0], expected); + QCOMPARE(dest[1], expected); + checkBounds(2); + memset(dest, 0, sizeof(T) * 2); + + transformRegion(source, 3, dest); + QCOMPARE(dest[0], expected); + QCOMPARE(dest[1], expected); + QCOMPARE(dest[2], expected); + checkBounds(3); + memset(dest, 0, sizeof(T) * 3); + + transformRegion(source, 4, dest); + QCOMPARE(dest[0], expected); + QCOMPARE(dest[1], expected); + QCOMPARE(dest[2], expected); + QCOMPARE(dest[3], expected); + checkBounds(4); + memset(dest, 0, sizeof(T) * 4); + + transformRegion(source, 8, dest); + for (int i = 0; i < 8; ++i) + QCOMPARE(dest[i], expected); + checkBounds(8); + memset(dest, 0, sizeof(T) * 8); + + transformRegion(source, 16, dest); + for (int i = 0; i < 16; ++i) + QCOMPARE(dest[i], expected); + checkBounds(16); + memset(dest, 0, sizeof(T) * 16); + + transformRegion(source, 32, dest); + for (int i = 0; i < 32; ++i) + QCOMPARE(dest[i], expected); + checkBounds(32); + memset(dest, 0, sizeof(T) * 32); + + transformRegion(source, 64, dest); + for (int i = 0; i < 64; ++i) + QCOMPARE(dest[i], expected); + + // check transforming in-place + memcpy(dest, source, sizeof(dest)); + transformRegion(dest, 64, dest); + for (int i = 0; i < 64; ++i) + QCOMPARE(dest[i], expected); +} + +void tst_QtEndian::fromBigEndianRegion_data() +{ + QTest::addColumn<int>("size"); + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("4") << 4; + QTest::newRow("8") << 8; +} + +void tst_QtEndian::fromBigEndianRegion() +{ + QFETCH(int, size); + switch (size) { + case 1: return transformRegion_template<quint8>(qFromBigEndian<quint8>, qFromBigEndian<quint8>); + case 2: return transformRegion_template<quint16>(qFromBigEndian<quint16>, qFromBigEndian<quint16>); + case 4: return transformRegion_template<quint32>(qFromBigEndian<quint32>, qFromBigEndian<quint32>); + case 8: return transformRegion_template<quint64>(qFromBigEndian<quint64>, qFromBigEndian<quint64>); + } +} + +void tst_QtEndian::fromLittleEndianRegion() +{ + QFETCH(int, size); + switch (size) { + case 1: return transformRegion_template<quint8>(qFromLittleEndian<quint8>, qFromLittleEndian<quint8>); + case 2: return transformRegion_template<quint16>(qFromLittleEndian<quint16>, qFromLittleEndian<quint16>); + case 4: return transformRegion_template<quint32>(qFromLittleEndian<quint32>, qFromLittleEndian<quint32>); + case 8: return transformRegion_template<quint64>(qFromLittleEndian<quint64>, qFromLittleEndian<quint64>); + } +} #define ENDIAN_TEST(endian, type, size) \ do { \ @@ -135,6 +283,28 @@ void tst_QtEndian::toLittleEndian() #undef ENDIAN_TEST +void tst_QtEndian::toBigEndianRegion() +{ + QFETCH(int, size); + switch (size) { + case 1: return transformRegion_template<quint8>(qToBigEndian<quint8>, qToBigEndian<quint8>); + case 2: return transformRegion_template<quint16>(qToBigEndian<quint16>, qToBigEndian<quint16>); + case 4: return transformRegion_template<quint32>(qToBigEndian<quint32>, qToBigEndian<quint32>); + case 8: return transformRegion_template<quint64>(qToBigEndian<quint64>, qToBigEndian<quint64>); + } +} + +void tst_QtEndian::toLittleEndianRegion() +{ + QFETCH(int, size); + switch (size) { + case 1: return transformRegion_template<quint8>(qToLittleEndian<quint8>, qToLittleEndian<quint8>); + case 2: return transformRegion_template<quint16>(qToLittleEndian<quint16>, qToLittleEndian<quint16>); + case 4: return transformRegion_template<quint32>(qToLittleEndian<quint32>, qToLittleEndian<quint32>); + case 8: return transformRegion_template<quint64>(qToLittleEndian<quint64>, qToLittleEndian<quint64>); + } +} + void tst_QtEndian::endianIntegers_data() { QTest::addColumn<int>("val"); diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index b43ea7cfa5..7b8b1df166 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -38,6 +38,13 @@ class tst_QDebug: public QObject { Q_OBJECT +public: + enum EnumType { EnumValue1 = 1, EnumValue2 = 2 }; + enum FlagType { EnumFlag1 = 1, EnumFlag2 = 2 }; + Q_ENUM(EnumType) + Q_DECLARE_FLAGS(Flags, FlagType) + Q_FLAG(Flags) + private slots: void assignment() const; void warningWithoutDebug() const; @@ -637,6 +644,15 @@ void tst_QDebug::qDebugQFlags() const QCOMPARE(s_line, line); QCOMPARE(QString::fromLatin1(s_function), function); + // Test the output of QFlags with an enum not declared with Q_DECLARE_FLAGS and Q_FLAGS + QFlags<EnumType> flags2(EnumValue2); + qDebug() << flags2; + QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::EnumType>(EnumValue2)")); + + // A now for one that was fully declared + tst_QDebug::Flags flags3(EnumFlag1); + qDebug() << flags3; + QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::FlagType>(EnumFlag1)")); } void tst_QDebug::textStreamModifiers() const diff --git a/tests/auto/corelib/io/qdir/tst_qdir.cpp b/tests/auto/corelib/io/qdir/tst_qdir.cpp index afa15fe895..30f0e447ad 100644 --- a/tests/auto/corelib/io/qdir/tst_qdir.cpp +++ b/tests/auto/corelib/io/qdir/tst_qdir.cpp @@ -33,7 +33,6 @@ #include <qdebug.h> #include <qdir.h> #include <qfileinfo.h> -#include <qregexp.h> #include <qstringlist.h> #if defined(Q_OS_WIN) 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..91c5c15f66 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.pro +!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..512da8939b 100644 --- a/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro +++ b/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro @@ -1,8 +1,4 @@ SOURCES += main.cpp QT = core -CONFIG -= app_bundle debug_and_release_target -CONFIG += console -# This app is testdata for tst_qfile -target.path = $$[QT_INSTALL_TESTS]/tst_qfile/$$TARGET -INSTALLS += target +load(qt_test_helper) diff --git a/tests/auto/corelib/io/qfile/test.pro b/tests/auto/corelib/io/qfile/test.pro new file mode 100644 index 0000000000..95389ab3e2 --- /dev/null +++ b/tests/auto/corelib/io/qfile/test.pro @@ -0,0 +1,26 @@ +CONFIG += testcase +QT = core-private testlib +qtHaveModule(network): QT += network +else: DEFINES += QT_NO_NETWORK + +contains(CONFIG, builtin_testdata) { + DEFINES += BUILTIN_TESTDATA +} + +TESTDATA += BLACKLIST + +TARGET = tst_qfile + +SOURCES = tst_qfile.cpp +INCLUDEPATH += ../../../../shared/ +HEADERS += ../../../../shared/emulationdetector.h + +RESOURCES += qfile.qrc rename-fallback.qrc copy-fallback.qrc + +TESTDATA += \ + dosfile.txt noendofline.txt testfile.txt \ + testlog.txt two.dots.file tst_qfile.cpp \ + Makefile forCopying.txt forRenaming.txt \ + resources/file1.ext1 + +win32:!winrt: LIBS += -lole32 -luuid diff --git a/tests/auto/corelib/io/qfile/test/test.pro b/tests/auto/corelib/io/qfile/test/test.pro deleted file mode 100644 index 1472ddbb83..0000000000 --- a/tests/auto/corelib/io/qfile/test/test.pro +++ /dev/null @@ -1,25 +0,0 @@ -CONFIG += testcase -CONFIG -= debug_and_release_target -QT = core-private core testlib -qtHaveModule(network): QT += network -else: DEFINES += QT_NO_NETWORK - -contains(CONFIG, builtin_testdata) { - DEFINES += BUILTIN_TESTDATA -} - -TESTDATA += ../BLACKLIST - -TARGET = ../tst_qfile -SOURCES = ../tst_qfile.cpp -INCLUDEPATH += ../../../../../shared/ -HEADERS += ../../../../../shared/emulationdetector.h - -RESOURCES += ../qfile.qrc ../rename-fallback.qrc ../copy-fallback.qrc - -TESTDATA += ../dosfile.txt ../noendofline.txt ../testfile.txt \ - ../testlog.txt ../two.dots.file ../tst_qfile.cpp \ - ../Makefile ../forCopying.txt ../forRenaming.txt \ - ../resources/file1.ext1 - -win32:!winrt: LIBS+=-lole32 -luuid diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index 6665200585..678a80c3f7 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())); @@ -1297,6 +1293,12 @@ void tst_QFile::append() f.putChar('a'); f.close(); QCOMPARE(int(f.size()), 2); + + QVERIFY2(f.open(QIODevice::Append | QIODevice::Truncate), msgOpenFailed(f).constData()); + QCOMPARE(f.pos(), 0); + f.putChar('a'); + f.close(); + QCOMPARE(int(f.size()), 1); } void tst_QFile::permissions_data() @@ -1833,13 +1835,14 @@ void tst_QFile::encodeName() void tst_QFile::truncate() { - for (int i = 0; i < 2; ++i) { + const QIODevice::OpenModeFlag modes[] = { QFile::ReadWrite, QIODevice::WriteOnly, QIODevice::Append }; + for (auto mode : modes) { QFile file("truncate.txt"); QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData()); file.write(QByteArray(200, '@')); file.close(); - QVERIFY2(file.open((i ? QFile::WriteOnly : QFile::ReadWrite) | QFile::Truncate), msgOpenFailed(file).constData()); + QVERIFY2(file.open(mode | QFile::Truncate), msgOpenFailed(file).constData()); file.write(QByteArray(100, '$')); file.close(); @@ -2210,7 +2213,8 @@ public: uint ownerId(FileOwner) const { return 0; } QString owner(FileOwner) const { return QString(); } QDateTime fileTime(FileTime) const { return QDateTime(); } - bool setFileTime(const QDateTime &newDate, FileTime time) { return false; } + bool setFileTime(const QDateTime &newDate, FileTime time) + { Q_UNUSED(newDate) Q_UNUSED(time) return false; } private: int number; @@ -2755,19 +2759,20 @@ void tst_QFile::renameMultiple() void tst_QFile::appendAndRead() { - QFile writeFile(QLatin1String("appendfile.txt")); - QVERIFY2(writeFile.open(QIODevice::WriteOnly | QIODevice::Truncate), msgOpenFailed(writeFile).constData()); + const QString fileName(QStringLiteral("appendfile.txt")); + QFile writeFile(fileName); + QVERIFY2(writeFile.open(QIODevice::Append | QIODevice::Truncate), msgOpenFailed(writeFile).constData()); - QFile readFile(QLatin1String("appendfile.txt")); + QFile readFile(fileName); QVERIFY2(readFile.open(QIODevice::ReadOnly), msgOpenFailed(readFile).constData()); // Write to the end of the file, then read that character back, and so on. for (int i = 0; i < 100; ++i) { char c = '\0'; - writeFile.putChar(char(i % 256)); + writeFile.putChar(char(i)); writeFile.flush(); QVERIFY(readFile.getChar(&c)); - QCOMPARE(c, char(i % 256)); + QCOMPARE(c, char(i)); QCOMPARE(readFile.pos(), writeFile.pos()); } @@ -2778,8 +2783,6 @@ void tst_QFile::appendAndRead() writeFile.flush(); QCOMPARE(readFile.read(size).size(), size); } - - readFile.close(); } void tst_QFile::miscWithUncPathAsCurrentDir() diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index 87d5675e7a..017eebe153 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -199,7 +199,8 @@ private slots: void fileTimes_data(); void fileTimes(); - void fileTimes_oldFile(); + void fakeFileTimes_data(); + void fakeFileTimes(); void isSymLink_data(); void isSymLink(); @@ -629,6 +630,16 @@ void tst_QFileInfo::canonicalFilePath() info.canonicalFilePath(); #if defined(Q_OS_UNIX) + // If this file exists, you can't log in to run this test ... + const QString notExtantPath(QStringLiteral("/etc/nologin")); + QFileInfo notExtant(notExtantPath); + QCOMPARE(notExtant.canonicalFilePath(), QString()); + + // A path with a non-directory as a directory component also doesn't exist: + const QString badDirPath(QStringLiteral("/dev/null/sub/dir/n'existe.pas")); + QFileInfo badDir(badDirPath); + QCOMPARE(badDir.canonicalFilePath(), QString()); + // This used to crash on Mac QFileInfo dontCrash(QLatin1String("/")); QCOMPARE(dontCrash.canonicalFilePath(), QLatin1String("/")); @@ -1212,12 +1223,23 @@ void tst_QFileInfo::fileTimes() QVERIFY(writeTime < beforeRead); } -void tst_QFileInfo::fileTimes_oldFile() +void tst_QFileInfo::fakeFileTimes_data() { + QTest::addColumn<QDateTime>("when"); + // This is 2^{31} seconds before 1970-01-01 15:14:8, // i.e. shortly after the start of time_t, in any time-zone: - const QDateTime early(QDate(1901, 12, 14), QTime(12, 0)); - QFile file("ancientfile.txt"); + QTest::newRow("early") << QDateTime(QDate(1901, 12, 14), QTime(12, 0)); + + // QTBUG-12006 claims XP handled this (2010-Mar-26 8:46:10) wrong due to an MS API bug: + QTest::newRow("XP-bug") << QDateTime::fromTime_t(1269593170); +} + +void tst_QFileInfo::fakeFileTimes() +{ + QFETCH(QDateTime, when); + + QFile file("faketimefile.txt"); file.open(QIODevice::WriteOnly); file.write("\n", 1); file.close(); @@ -1228,15 +1250,13 @@ void tst_QFileInfo::fileTimes_oldFile() modification time, so need to re-open for read in order to setFileTime(). */ file.open(QIODevice::ReadOnly); - bool ok = file.setFileTime(early, QFileDevice::FileModificationTime); + bool ok = file.setFileTime(when, QFileDevice::FileModificationTime); file.close(); - if (ok) { - QFileInfo info(file.fileName()); - QCOMPARE(info.lastModified(), early); - } else { - QSKIP("Unable to set file metadata to ancient values"); - } + if (ok) + QCOMPARE(QFileInfo(file.fileName()).lastModified(), when); + else + QSKIP("Unable to set file metadata to contrived values"); } void tst_QFileInfo::isSymLink_data() diff --git a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp index fed05698fd..da5327594c 100644 --- a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp +++ b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp @@ -209,9 +209,6 @@ void tst_QIODevice::read_QByteArray() //-------------------------------------------------------------------- void tst_QIODevice::unget() { -#if defined(Q_OS_MAC) - QSKIP("The unget network test is unstable on Mac. See QTBUG-39983."); -#endif QBuffer buffer; buffer.open(QBuffer::ReadWrite); buffer.write("ZXCV"); diff --git a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp index 5b61a6007d..a10e706ed7 100644 --- a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp +++ b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp @@ -187,6 +187,13 @@ private slots: "default=false"); QCOMPARE(parser.rules().size(), 1); + // QSettings escapes * to %2A when writing. + parser.setContent("[Rules]\n" + "module.%2A=false"); + QCOMPARE(parser.rules().size(), 1); + QCOMPARE(parser.rules().first().category, QString("module.")); + QCOMPARE(parser.rules().first().flags, QLoggingRule::LeftFilter); + parser.setContent("[OtherSection]\n" "default=false"); QCOMPARE(parser.rules().size(), 0); diff --git a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp index b78fa29fb6..17c51eaaf4 100644 --- a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp +++ b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp @@ -65,7 +65,7 @@ void tst_QNoDebug::streaming() const { QDateTime dt(QDate(1,2,3),QTime(4,5,6)); const QByteArray debugString = dt.toString(QStringViewLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1(); - const QByteArray message = "QDateTime(" + debugString + " Qt::TimeSpec(LocalTime))"; + const QByteArray message = "QDateTime(" + debugString + " Qt::LocalTime)"; QTest::ignoreMessage(QtWarningMsg, message.constData()); qWarning() << dt; } diff --git a/tests/auto/corelib/io/qresourceengine/.gitattributes b/tests/auto/corelib/io/qresourceengine/.gitattributes index add3716d81..88edd3681a 100644 --- a/tests/auto/corelib/io/qresourceengine/.gitattributes +++ b/tests/auto/corelib/io/qresourceengine/.gitattributes @@ -1 +1,2 @@ testqrc/test.qrc -crlf +*.txt -crlf 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/testqrc/aliasdir/aliasdir.txt b/tests/auto/corelib/io/qresourceengine/testqrc/aliasdir/aliasdir.txt index 21a3dfa0b8..dcf7937f0a 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/aliasdir/aliasdir.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/aliasdir/aliasdir.txt @@ -1 +1 @@ -"This is a korean text file" +"This is a korean text file" diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/blahblah.txt b/tests/auto/corelib/io/qresourceengine/testqrc/blahblah.txt index 436c4d11c3..19f0805d8d 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/blahblah.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/blahblah.txt @@ -1 +1 @@ -qwerty +qwerty diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/currentdir.txt b/tests/auto/corelib/io/qresourceengine/testqrc/currentdir.txt index 38e389979a..65f1f43def 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/currentdir.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/currentdir.txt @@ -1 +1 @@ -"This is the current dir" +"This is the current dir" diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/currentdir2.txt b/tests/auto/corelib/io/qresourceengine/testqrc/currentdir2.txt index 6ac16a3306..7d89108011 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/currentdir2.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/currentdir2.txt @@ -1 +1 @@ -"This is also the current dir" +"This is also the current dir" diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/otherdir/otherdir.txt b/tests/auto/corelib/io/qresourceengine/testqrc/otherdir/otherdir.txt index b0e4a124ee..e1b430f33b 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/otherdir/otherdir.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/otherdir/otherdir.txt @@ -1 +1 @@ -"This is the other dir" +"This is the other dir" diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/subdir/subdir.txt b/tests/auto/corelib/io/qresourceengine/testqrc/subdir/subdir.txt index b6115207a2..4506acf413 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/subdir/subdir.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/subdir/subdir.txt @@ -1 +1 @@ -"This is in the sub directory" +"This is in the sub directory" diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test1.txt b/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test1.txt index adc01d1354..8baef1b4ab 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test1.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test1.txt @@ -1 +1 @@ -abc +abc diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test2.txt b/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test2.txt index 3f48e3cdc3..24c5735c3e 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test2.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/test/test/test2.txt @@ -1 +1 @@ -def +def diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir.txt b/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir.txt index 40ee68dccb..b8cb3a8c01 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir.txt @@ -1 +1 @@ -"This is in the test directory" +"This is in the test directory" diff --git a/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir2.txt b/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir2.txt index 051430298a..dccfdc9bcf 100644 --- a/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir2.txt +++ b/tests/auto/corelib/io/qresourceengine/testqrc/test/testdir2.txt @@ -1 +1 @@ -"This is another file in this directory" +"This is another file in this directory" diff --git a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp index b7e85e8f05..0b50c391b8 100644 --- a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp +++ b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -57,6 +58,7 @@ private slots: void doubleSlashInRoot(); void setLocale(); void lastModified(); + void resourcesInStaticPlugins(); private: const QString m_runtimeResourceRcc; @@ -103,7 +105,7 @@ void tst_QResourceEngine::cleanupTestCase() void tst_QResourceEngine::checkStructure_data() { QTest::addColumn<QString>("pathName"); - QTest::addColumn<QString>("contents"); + QTest::addColumn<QByteArray>("contents"); QTest::addColumn<QStringList>("containedFiles"); QTest::addColumn<QStringList>("containedDirs"); QTest::addColumn<QLocale>("locale"); @@ -119,6 +121,7 @@ void tst_QResourceEngine::checkStructure_data() << QLatin1String("searchpath1") << QLatin1String("searchpath2") << QLatin1String("secondary_root") + << QLatin1String("staticplugin") << QLatin1String("test") << QLatin1String("withoutslashes"); @@ -127,12 +130,12 @@ void tst_QResourceEngine::checkStructure_data() #endif #if defined(BUILTIN_TESTDATA) - rootContents.insert(8, QLatin1String("testqrc")); + rootContents.insert(9, QLatin1String("testqrc")); #endif QTest::newRow("root dir") << QString(":/") - << QString() + << QByteArray() << (QStringList() #if defined(BUILTIN_TESTDATA) << "parentdir.txt" @@ -144,7 +147,7 @@ void tst_QResourceEngine::checkStructure_data() << qlonglong(0); QTest::newRow("secondary root") << QString(":/secondary_root/") - << QString() + << QByteArray() << QStringList() << (QStringList() << QLatin1String("runtime_resource")) << QLocale::c() @@ -155,73 +158,73 @@ void tst_QResourceEngine::checkStructure_data() for(int i = 0; i < roots.size(); ++i) { const QString root = roots.at(i); - QTest::newRow(QString(root + "prefix dir").toLatin1().constData()) << QString(root + "test/abc/123/+++") - << QString() + QTest::addRow("%s prefix dir", qPrintable(root)) << QString(root + "test/abc/123/+++") + << QByteArray() << (QStringList() << QLatin1String("currentdir.txt") << QLatin1String("currentdir2.txt") << QLatin1String("parentdir.txt")) << (QStringList() << QLatin1String("subdir")) << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "parent to prefix").toLatin1().constData()) << QString(root + "test/abc/123") - << QString() + QTest::addRow("%s parent to prefix", qPrintable(root)) << QString(root + "test/abc/123") + << QByteArray() << QStringList() << (QStringList() << QLatin1String("+++")) << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "two parents prefix").toLatin1().constData()) << QString(root + "test/abc") - << QString() + QTest::addRow("%s two parents prefix", qPrintable(root)) << QString(root + "test/abc") + << QByteArray() << QStringList() << QStringList(QLatin1String("123")) << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "test dir ").toLatin1().constData()) << QString(root + "test") - << QString() + QTest::addRow("%s test dir ", qPrintable(root)) << QString(root + "test") + << QByteArray() << (QStringList() << QLatin1String("testdir.txt")) << (QStringList() << QLatin1String("abc") << QLatin1String("test")) << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "prefix no slashes").toLatin1().constData()) << QString(root + "withoutslashes") - << QString() + QTest::addRow("%s prefix no slashes", qPrintable(root)) << QString(root + "withoutslashes") + << QByteArray() << QStringList("blahblah.txt") << QStringList() << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "other dir").toLatin1().constData()) << QString(root + "otherdir") - << QString() + QTest::addRow("%s other dir", qPrintable(root)) << QString(root + "otherdir") + << QByteArray() << QStringList(QLatin1String("otherdir.txt")) << QStringList() << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "alias dir").toLatin1().constData()) << QString(root + "aliasdir") - << QString() + QTest::addRow("%s alias dir", qPrintable(root)) << QString(root + "aliasdir") + << QByteArray() << QStringList(QLatin1String("aliasdir.txt")) << QStringList() << QLocale::c() << qlonglong(0); - QTest::newRow(QString(root + "second test dir").toLatin1().constData()) << QString(root + "test/test") - << QString() + QTest::addRow("%s second test dir", qPrintable(root)) << QString(root + "test/test") + << QByteArray() << (QStringList() << QLatin1String("test1.txt") << QLatin1String("test2.txt")) << QStringList() << QLocale::c() << qlonglong(0); info = QFileInfo(QFINDTESTDATA("testqrc/test/test/test1.txt")); - QTest::newRow(QString(root + "test1 text").toLatin1().constData()) << QString(root + "test/test/test1.txt") - << QString("abc") + QTest::addRow("%s test1 text", qPrintable(root)) << QString(root + "test/test/test1.txt") + << QByteArray("abc\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/blahblah.txt")); - QTest::newRow(QString(root + "text no slashes").toLatin1().constData()) << QString(root + "withoutslashes/blahblah.txt") - << QString("qwerty") + QTest::addRow("%s text no slashes", qPrintable(root)) << QString(root + "withoutslashes/blahblah.txt") + << QByteArray("qwerty\n") << QStringList() << QStringList() << QLocale::c() @@ -229,96 +232,96 @@ void tst_QResourceEngine::checkStructure_data() info = QFileInfo(QFINDTESTDATA("testqrc/test/test/test2.txt")); - QTest::newRow(QString(root + "test1 text").toLatin1().constData()) << QString(root + "test/test/test2.txt") - << QString("def") + QTest::addRow("%s test1 text", qPrintable(root)) << QString(root + "test/test/test2.txt") + << QByteArray("def\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/currentdir.txt")); - QTest::newRow(QString(root + "currentdir text").toLatin1().constData()) << QString(root + "test/abc/123/+++/currentdir.txt") - << QString("\"This is the current dir\" ") + QTest::addRow("%s currentdir text", qPrintable(root)) << QString(root + "test/abc/123/+++/currentdir.txt") + << QByteArray("\"This is the current dir\"\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/currentdir2.txt")); - QTest::newRow(QString(root + "currentdir text2").toLatin1().constData()) << QString(root + "test/abc/123/+++/currentdir2.txt") - << QString("\"This is also the current dir\" ") + QTest::addRow("%s currentdir text2", qPrintable(root)) << QString(root + "test/abc/123/+++/currentdir2.txt") + << QByteArray("\"This is also the current dir\"\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("parentdir.txt")); - QTest::newRow(QString(root + "parentdir text").toLatin1().constData()) << QString(root + "test/abc/123/+++/parentdir.txt") - << QString("abcdefgihklmnopqrstuvwxyz ") + QTest::addRow("%s parentdir text", qPrintable(root)) << QString(root + "test/abc/123/+++/parentdir.txt") + << QByteArray("abcdefgihklmnopqrstuvwxyz \n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/subdir/subdir.txt")); - QTest::newRow(QString(root + "subdir text").toLatin1().constData()) << QString(root + "test/abc/123/+++/subdir/subdir.txt") - << QString("\"This is in the sub directory\" ") + QTest::addRow("%s subdir text", qPrintable(root)) << QString(root + "test/abc/123/+++/subdir/subdir.txt") + << QByteArray("\"This is in the sub directory\"\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/test/testdir.txt")); - QTest::newRow(QString(root + "testdir text").toLatin1().constData()) << QString(root + "test/testdir.txt") - << QString("\"This is in the test directory\" ") + QTest::addRow("%s testdir text", qPrintable(root)) << QString(root + "test/testdir.txt") + << QByteArray("\"This is in the test directory\"\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/otherdir/otherdir.txt")); - QTest::newRow(QString(root + "otherdir text").toLatin1().constData()) << QString(root + "otherdir/otherdir.txt") - << QString("\"This is the other dir\" ") + QTest::addRow("%s otherdir text", qPrintable(root)) << QString(root + "otherdir/otherdir.txt") + << QByteArray("\"This is the other dir\"\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/test/testdir2.txt")); - QTest::newRow(QString(root + "alias text").toLatin1().constData()) << QString(root + "aliasdir/aliasdir.txt") - << QString("\"This is another file in this directory\" ") + QTest::addRow("%s alias text", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt") + << QByteArray("\"This is another file in this directory\"\n") << QStringList() << QStringList() << QLocale::c() << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/aliasdir/aliasdir.txt")); - QTest::newRow(QString(root + "korean text").toLatin1().constData()) << QString(root + "aliasdir/aliasdir.txt") - << QString("\"This is a korean text file\" ") + QTest::addRow("%s korean text", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt") + << QByteArray("\"This is a korean text file\"\n") << QStringList() << QStringList() << QLocale("ko") << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/aliasdir/aliasdir.txt")); - QTest::newRow(QString(root + "korean text 2").toLatin1().constData()) << QString(root + "aliasdir/aliasdir.txt") - << QString("\"This is a korean text file\" ") + QTest::addRow("%s korean text 2", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt") + << QByteArray("\"This is a korean text file\"\n") << QStringList() << QStringList() << QLocale("ko_KR") << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/test/german.txt")); - QTest::newRow(QString(root + "german text").toLatin1().constData()) << QString(root + "aliasdir/aliasdir.txt") - << QString("Deutsch") + QTest::addRow("%s german text", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt") + << QByteArray("Deutsch\n") << QStringList() << QStringList() << QLocale("de") << qlonglong(info.size()); info = QFileInfo(QFINDTESTDATA("testqrc/test/german.txt")); - QTest::newRow(QString(root + "german text 2").toLatin1().constData()) << QString(root + "aliasdir/aliasdir.txt") - << QString("Deutsch") + QTest::addRow("%s german text 2", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt") + << QByteArray("Deutsch\n") << QStringList() << QStringList() << QLocale("de_DE") @@ -327,8 +330,8 @@ void tst_QResourceEngine::checkStructure_data() QFile file(QFINDTESTDATA("testqrc/aliasdir/compressme.txt")); file.open(QFile::ReadOnly); info = QFileInfo(QFINDTESTDATA("testqrc/aliasdir/compressme.txt")); - QTest::newRow(QString(root + "compressed text").toLatin1().constData()) << QString(root + "aliasdir/aliasdir.txt") - << QString(file.readAll()) + QTest::addRow("%s compressed text", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt") + << file.readAll() << QStringList() << QStringList() << QLocale("de_CH") @@ -339,7 +342,7 @@ void tst_QResourceEngine::checkStructure_data() void tst_QResourceEngine::checkStructure() { QFETCH(QString, pathName); - QFETCH(QString, contents); + QFETCH(QByteArray, contents); QFETCH(QStringList, containedFiles); QFETCH(QStringList, containedDirs); QFETCH(QLocale, locale); @@ -399,8 +402,18 @@ void tst_QResourceEngine::checkStructure() QFile file(pathName); QVERIFY(file.open(QFile::ReadOnly)); - QByteArray ba = file.readAll(); - QVERIFY(QString(ba).startsWith(contents)); + // check contents + QCOMPARE(file.readAll(), contents); + + // check memory map too + uchar *ptr = file.map(0, file.size(), QFile::MapPrivateOption); + QVERIFY2(ptr, qPrintable(file.errorString())); + QByteArray ba = QByteArray::fromRawData(reinterpret_cast<const char *>(ptr), file.size()); + QCOMPARE(ba, contents); + + // check that it is still valid after closing the file + file.close(); + QCOMPARE(ba, contents); } QLocale::setDefault(QLocale::system()); } @@ -520,6 +533,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/BLACKLIST b/tests/auto/corelib/io/qsettings/BLACKLIST deleted file mode 100644 index 36d68bd918..0000000000 --- a/tests/auto/corelib/io/qsettings/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[isWritable:native] -osx-10.11 diff --git a/tests/auto/corelib/io/qsettings/qsettings.pro b/tests/auto/corelib/io/qsettings/qsettings.pro index 5b4cc8a691..79552b62df 100644 --- a/tests/auto/corelib/io/qsettings/qsettings.pro +++ b/tests/auto/corelib/io/qsettings/qsettings.pro @@ -6,4 +6,6 @@ RESOURCES += qsettings.qrc INCLUDEPATH += $$PWD/../../kernel/qmetatype msvc: LIBS += advapi32.lib +darwin: LIBS += -framework CoreFoundation + DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index db756ada39..5357194406 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; @@ -1186,6 +1186,10 @@ static void testMetaTypesHelper(QSettings::Format format) F(QJsonArray) \ F(QJsonDocument) \ F(QPersistentModelIndex) \ + F(QCborSimpleType) \ + F(QCborValue) \ + F(QCborArray) \ + F(QCborMap) \ #define EXCLUDE_NON_SUPPORTED_METATYPES(MetaTypeName) \ template<> void testMetaTypesHelper<QMetaType::MetaTypeName>(QSettings::Format) \ 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/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 62af907037..84af1c255a 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -180,6 +180,8 @@ private slots: void testThreading(); void matches_data(); void matches(); + void ipv6_zoneId_data(); + void ipv6_zoneId(); private: void testThreadingHelper(); @@ -1876,6 +1878,24 @@ void tst_QUrl::ipv6_data() QTest::newRow("encoded-digit") << "//[::%31]" << true << "//[::1]"; QTest::newRow("encoded-colon") << "//[%3A%3A]" << true << "//[::]"; + + QTest::newRow("full ipv6 with zone id (decoded %)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%eth0]") << true + << "//[56:56:56:56:56:56:56:56%25eth0]"; + + QTest::newRow("full ipv6 with zone id (encoded %)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%25eth0]") << true + << "//[56:56:56:56:56:56:56:56%25eth0]"; + + QTest::newRow("full ipv6 with invalid zone id") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%]") << false << ""; + + QTest::newRow("full ipv6 with invalid zone id (encoded)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%25]") << false << ""; + + QTest::newRow("full ipv6 with zone id 25 (encoded)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%2525]") << true << "//[56:56:56:56:56:56:56:56%2525]"; + + QTest::newRow("case 4 with less and ip4 and port and useinfo and zone id") + << QString::fromLatin1("//user:pass@[56::56:56:56:127.0.0.1%ethernet_1]:99") << true + << "//user:pass@[56::56:56:56:7f00:1%25ethernet_1]:99"; + + QTest::newRow("encoded-digit including zone id") << "//[::%31%25eth0]" << true << "//[::1%25eth0]"; } void tst_QUrl::ipv6() @@ -4149,6 +4169,38 @@ void tst_QUrl::matches() QCOMPARE(urlOne.matches(urlTwo, QUrl::FormattingOptions(options)), matches); } +void tst_QUrl::ipv6_zoneId_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QString>("decodedHost"); + QTest::addColumn<QString>("prettyHost"); + QTest::addColumn<QString>("encodedHost"); + + QTest::newRow("digit") << QUrl("x://[::%251]") << "::%1" << "::%251" << "::%251"; + QTest::newRow("eth0") << QUrl("x://[::%25eth0]") << "::%eth0" << "::%25eth0" << "::%25eth0"; + QTest::newRow("space") << QUrl("x://[::%25%20]") << "::% " << "::%25 " << "::%25%20"; + QTest::newRow("subdelims") << QUrl("x://[::%25eth%2B]") << "::%eth+" << "::%25eth%2B" << "::%25eth%2B"; + QTest::newRow("other") << QUrl("x://[::%25^]") << "::%^" << "::%25%5E" << "::%25%5E"; + QTest::newRow("control") << QUrl("x://[::%25%7F]") << "::%\x7f" << "::%25%7F" << "::%25%7F"; + QTest::newRow("unicode") << QUrl("x://[::%25wlán0]") << "::%wlán0" << "::%25wlán0" << "::%25wl%C3%A1n0"; + QTest::newRow("non-utf8") << QUrl("x://[::%25%80]") << QString("::%") + QChar(QChar::ReplacementCharacter) << "::%25%80" << "::%25%80"; + } + +void tst_QUrl::ipv6_zoneId() +{ + QFETCH(QUrl, url); + QFETCH(QString, decodedHost); + QFETCH(QString, prettyHost); + QFETCH(QString, encodedHost); + + QVERIFY2(url.isValid(), qPrintable(url.errorString())); + QCOMPARE(url.host(QUrl::FullyDecoded), decodedHost); + QCOMPARE(url.host(), decodedHost); + QCOMPARE(url.host(QUrl::FullyEncoded), encodedHost); + QCOMPARE(url.toString(), "x://[" + prettyHost + "]"); + QCOMPARE(url.toString(QUrl::FullyEncoded), "x://[" + encodedHost + "]"); +} + QTEST_MAIN(tst_QUrl) #include "tst_qurl.moc" diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro index a09f03a7b4..cca350ad43 100644 --- a/tests/auto/corelib/itemmodels/itemmodels.pro +++ b/tests/auto/corelib/itemmodels/itemmodels.pro @@ -1,9 +1,9 @@ TEMPLATE=subdirs -SUBDIRS = qabstractitemmodel \ - qstringlistmodel \ +SUBDIRS = qstringlistmodel qtHaveModule(gui): SUBDIRS += \ + qabstractitemmodel \ qabstractproxymodel \ qidentityproxymodel \ qitemselectionmodel \ @@ -11,7 +11,8 @@ qtHaveModule(gui): SUBDIRS += \ qtHaveModule(widgets) { SUBDIRS += \ - qsortfilterproxymodel + qsortfilterproxymodel_regexp \ + qsortfilterproxymodel_regularexpression qtHaveModule(sql): SUBDIRS += \ qitemmodel diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore deleted file mode 100644 index d3672fe4ae..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tst_qsortfilterproxymodel diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro deleted file mode 100644 index dfa8b9fa1b..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro +++ /dev/null @@ -1,9 +0,0 @@ -CONFIG += testcase -TARGET = tst_qsortfilterproxymodel - -QT += widgets testlib -mtdir = ../../../other/qabstractitemmodelutils - -INCLUDEPATH += $$PWD/$${mtdir} -SOURCES += tst_qsortfilterproxymodel.cpp $${mtdir}/dynamictreemodel.cpp -HEADERS += $${mtdir}/dynamictreemodel.h diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.cpp index bced3f5790..82cd26971b 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.cpp @@ -27,147 +27,17 @@ ****************************************************************************/ #include <QtTest/QtTest> +#include "tst_qsortfilterproxymodel.h" #include "dynamictreemodel.h" #include <QtCore/QCoreApplication> #include <QtGui/QStandardItem> +#include <QtWidgets/QComboBox> #include <QtWidgets/QTreeView> #include <QtWidgets/QTableView> #include <qdebug.h> -typedef QList<int> IntList; -typedef QPair<int, int> IntPair; -typedef QList<IntPair> IntPairList; - -Q_DECLARE_METATYPE(QList<QPersistentModelIndex>) - -class tst_QSortFilterProxyModel : public QObject -{ - Q_OBJECT -public: - tst_QSortFilterProxyModel(); - -public slots: - void initTestCase(); - void cleanupTestCase(); - void cleanup(); - -private slots: - void getSetCheck(); - void sort_data(); - void sort(); - void sortHierarchy_data(); - void sortHierarchy(); - - void insertRows_data(); - void insertRows(); - void prependRow(); - void removeRows_data(); - void removeRows(); - void removeColumns_data(); - void removeColumns(); - void insertAfterSelect(); - void removeAfterSelect(); - void filter_data(); - void filter(); - void filterHierarchy_data(); - void filterHierarchy(); - void filterColumns_data(); - void filterColumns(); - - void filterTable(); - void filterCurrent(); - void filter_qtbug30662(); - - void changeSourceLayout(); - void changeSourceLayoutFilteredOut(); - void removeSourceRows_data(); - void removeSourceRows(); - void insertSourceRows_data(); - void insertSourceRows(); - void changeFilter_data(); - void changeFilter(); - void changeSourceData_data(); - void changeSourceData(); - void changeSourceDataKeepsStableSorting_qtbug1548(); - void changeSourceDataForwardsRoles_qtbug35440(); - void resortingDoesNotBreakTreeModels(); - void dynamicFilterWithoutSort(); - void sortFilterRole(); - void selectionFilteredOut(); - void match_data(); - void match(); - void insertIntoChildrenlessItem(); - void invalidateMappedChildren(); - void insertRowIntoFilteredParent(); - void filterOutParentAndFilterInChild(); - - void sourceInsertRows(); - void sourceModelDeletion(); - - void sortColumnTracking1(); - void sortColumnTracking2(); - - void sortStable(); - - void hiddenColumns(); - void insertRowsSort(); - void staticSorting(); - void dynamicSorting(); - void fetchMore(); - void hiddenChildren(); - void mapFromToSource(); - void removeRowsRecursive(); - void doubleProxySelectionSetSourceModel(); - void appearsAndSort(); - void unnecessaryDynamicSorting(); - void unnecessaryMapCreation(); - void resetInvalidate_data(); - void resetInvalidate(); - - void testMultipleProxiesWithSelection(); - void mapSelectionFromSource(); - void testResetInternalData(); - void filteredColumns(); - void headerDataChanged(); - - void testParentLayoutChanged(); - void moveSourceRows(); - - void hierarchyFilterInvalidation(); - void simpleFilterInvalidation(); - - void chainedProxyModelRoleNames(); - - void noMapAfterSourceDelete(); - void forwardDropApi(); - void canDropMimeData(); - void filterHint(); - - void sourceLayoutChangeLeavesValidPersistentIndexes(); - void rowMoveLeavesValidPersistentIndexes(); - - void emitLayoutChangedOnlyIfSortingChanged_data(); - void emitLayoutChangedOnlyIfSortingChanged(); - - void checkSetNewModel(); - void filterAndInsertRow_data(); - void filterAndInsertRow(); - void filterAndInsertColumn_data(); - void filterAndInsertColumn(); - -protected: - void buildHierarchy(const QStringList &data, QAbstractItemModel *model); - void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); - -private: - QStandardItemModel *m_model; - QSortFilterProxyModel *m_proxy; -}; - -Q_DECLARE_METATYPE(QAbstractItemModel::LayoutChangeHint) - // Testing get/set functions void tst_QSortFilterProxyModel::getSetCheck() { @@ -205,7 +75,15 @@ void tst_QSortFilterProxyModel::cleanupTestCase() void tst_QSortFilterProxyModel::cleanup() { - m_proxy->setFilterRegExp(QRegExp()); + switch (m_filterType) { + case FilterType::RegExp: + m_proxy->setFilterRegExp(QRegExp()); + break; + case FilterType::RegularExpression: + m_proxy->setFilterRegularExpression(QRegularExpression()); + break; + } + m_proxy->sort(-1, Qt::AscendingOrder); m_model->clear(); m_model->insertColumns(0, 1); @@ -629,6 +507,58 @@ void tst_QSortFilterProxyModel::prependRow() QCOMPARE(proxy.rowCount(QModelIndex()), 1); //only the "root" item is there } +void tst_QSortFilterProxyModel::appendRowFromCombobox_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<QStringList>("initial"); + QTest::addColumn<QString>("newitem"); + QTest::addColumn<QStringList>("expected"); + + QTest::newRow("filter_out_second_last_item") + << "^[0-9]*$" + << (QStringList() << "a" << "1") + << "2" + << (QStringList() << "a" << "1" << "2"); + + QTest::newRow("filter_out_everything") + << "^c*$" + << (QStringList() << "a" << "b") + << "c" + << (QStringList() << "a" << "b" << "c"); + + QTest::newRow("no_filter") + << "" + << (QStringList() << "0" << "1") + << "2" + << (QStringList() << "0" << "1" << "2"); + + QTest::newRow("filter_out_last_item") + << "^[a-z]*$" + << (QStringList() << "a" << "1") + << "b" + << (QStringList() << "a" << "1" << "b"); +} + +void tst_QSortFilterProxyModel::appendRowFromCombobox() +{ + QFETCH(QString, pattern); + QFETCH(QStringList, initial); + QFETCH(QString, newitem); + QFETCH(QStringList, expected); + + QStringListModel model(initial); + + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + proxy.setFilterRegExp(pattern); + + QComboBox comboBox; + comboBox.setModel(&proxy); + comboBox.addItem(newitem); + + QCOMPARE(model.stringList(), expected); +} + void tst_QSortFilterProxyModel::removeRows_data() { QTest::addColumn<QStringList>("initial"); @@ -916,8 +846,9 @@ void tst_QSortFilterProxyModel::removeRows() if (sortOrder != -1) proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); + if (!filter.isEmpty()) - proxy.setFilterRegExp(QRegExp(filter)); + setupFilter(&proxy, filter); // remove the rows QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success); @@ -939,14 +870,29 @@ class MyFilteredColumnProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - MyFilteredColumnProxyModel(QObject *parent = 0) - : QSortFilterProxyModel(parent) { } + MyFilteredColumnProxyModel(FilterType filterType, QObject *parent = 0) : + QSortFilterProxyModel(parent), + m_filterType(filterType) + { } + protected: bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const { QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString(); - return key.contains(filterRegExp()); + bool result = false; + switch (m_filterType) { + case FilterType::RegExp: + result = key.contains(filterRegExp()); + break; + case FilterType::RegularExpression: + result = key.contains(filterRegularExpression()); + break; + } + return result; } + +private: + FilterType m_filterType; }; void tst_QSortFilterProxyModel::removeColumns_data() @@ -1155,10 +1101,10 @@ void tst_QSortFilterProxyModel::removeColumns() QFETCH(QStringList, expectedSource); QStandardItemModel model; - MyFilteredColumnProxyModel proxy; + MyFilteredColumnProxyModel proxy(m_filterType); proxy.setSourceModel(&model); if (!filter.isEmpty()) - proxy.setFilterRegExp(QRegExp(filter)); + setupFilter(&proxy, filter); // prepare model model.setHorizontalHeaderLabels(initial); @@ -1226,7 +1172,8 @@ void tst_QSortFilterProxyModel::filterColumns() QModelIndex index = m_model->index(0, col, QModelIndex()); m_model->setData(index, initial.at(col), Qt::DisplayRole); } - m_proxy->setFilterRegExp(pattern); + setupFilter(m_proxy, pattern); + m_proxy->setFilterKeyColumn(-1); // make sure the model is unchanged for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) { @@ -1292,6 +1239,7 @@ void tst_QSortFilterProxyModel::filter() QFETCH(QString, pattern); QFETCH(QStringList, initial); QFETCH(QStringList, expected); + // prepare model QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex())); QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); @@ -1301,7 +1249,7 @@ void tst_QSortFilterProxyModel::filter() QModelIndex index = m_model->index(row, 0, QModelIndex()); m_model->setData(index, initial.at(row), Qt::DisplayRole); } - m_proxy->setFilterRegExp(pattern); + setupFilter(m_proxy, pattern); // make sure the proxy is unfiltered QCOMPARE(m_proxy->columnCount(QModelIndex()), 1); QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count()); @@ -1340,7 +1288,7 @@ void tst_QSortFilterProxyModel::filterHierarchy() QFETCH(QStringList, initial); QFETCH(QStringList, expected); buildHierarchy(initial, m_model); - m_proxy->setFilterRegExp(pattern); + setupFilter(m_proxy, pattern); checkHierarchy(initial, m_model); checkHierarchy(expected, m_proxy); } @@ -1405,6 +1353,18 @@ void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbst } } +void tst_QSortFilterProxyModel::setupFilter(QSortFilterProxyModel *model, const QString& pattern) +{ + switch (m_filterType) { + case FilterType::RegExp: + model->setFilterRegExp(pattern); + break; + case FilterType::RegularExpression: + model->setFilterRegularExpression(pattern); + break; + } +} + class TestModel: public QAbstractTableModel { public: @@ -1423,7 +1383,7 @@ void tst_QSortFilterProxyModel::filterTable() TestModel model; QSortFilterProxyModel filter; filter.setSourceModel(&model); - filter.setFilterRegExp("9"); + setupFilter(&filter, QLatin1String("9")); for (int i = 0; i < filter.rowCount(); ++i) QVERIFY(filter.data(filter.index(i, 0)).toString().contains(QLatin1Char('9'))); @@ -1487,7 +1447,7 @@ void tst_QSortFilterProxyModel::filterCurrent() view.setCurrentIndex(proxy.index(0, 0)); QCOMPARE(spy.count(), 1); - proxy.setFilterRegExp(QRegExp("^B")); + setupFilter(&proxy, QLatin1String("^B")); QCOMPARE(spy.count(), 2); } @@ -1498,7 +1458,7 @@ void tst_QSortFilterProxyModel::filter_qtbug30662() proxy.setSourceModel(&model); // make sure the filter does not match any entry - proxy.setFilterRegExp(QRegExp("[0-9]+")); + setupFilter(&proxy, QLatin1String("[0-9]+")); QStringList slSource; slSource << "z" << "x" << "a" << "b"; @@ -1508,7 +1468,7 @@ void tst_QSortFilterProxyModel::filter_qtbug30662() model.setStringList(slSource); // without fix for QTBUG-30662 this will make all entries visible - but unsorted - proxy.setFilterRegExp(QRegExp("[a-z]+")); + setupFilter(&proxy, QLatin1String("[a-z]+")); QStringList slResult; for (int i = 0; i < proxy.rowCount(); ++i) @@ -1554,7 +1514,8 @@ void tst_QSortFilterProxyModel::changeSourceLayoutFilteredOut() QSignalSpy removeSpy(&proxy, &QSortFilterProxyModel::rowsRemoved); // Filter everything out - proxy.setFilterRegExp(QRegExp("c")); + setupFilter(&proxy, QLatin1String("c")); + QCOMPARE(removeSpy.count(), 1); QCOMPARE(0, proxy.rowCount()); @@ -1563,7 +1524,8 @@ void tst_QSortFilterProxyModel::changeSourceLayoutFilteredOut() QSignalSpy insertSpy(&proxy, &QSortFilterProxyModel::rowsInserted); // Remove filter; we expect an insert - proxy.setFilterRegExp(QRegExp("")); + setupFilter(&proxy, ""); + QCOMPARE(insertSpy.count(), 1); QCOMPARE(beforeSortFilter, proxy.rowCount()); } @@ -1843,7 +1805,7 @@ void tst_QSortFilterProxyModel::changeFilter() QVERIFY(initialRemoveSpy.isValid()); QVERIFY(initialInsertSpy.isValid()); - proxy.setFilterRegExp(initialFilter); + setupFilter(&proxy, initialFilter); QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count()); QCOMPARE(initialInsertSpy.count(), 0); @@ -1867,7 +1829,7 @@ void tst_QSortFilterProxyModel::changeFilter() QVERIFY(finalRemoveSpy.isValid()); QVERIFY(finalInsertSpy.isValid()); - proxy.setFilterRegExp(finalFilter); + setupFilter(&proxy, finalFilter); QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count()); for (int i = 0; i < finalRemoveSpy.count(); ++i) { @@ -2061,7 +2023,7 @@ void tst_QSortFilterProxyModel::changeSourceData() proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); (void)proxy.rowCount(QModelIndex()); // force mapping - proxy.setFilterRegExp(filter); + setupFilter(&proxy, filter); QCOMPARE(proxy.rowCount(), expectedInitialProxyItems.count()); for (int i = 0; i < expectedInitialProxyItems.count(); ++i) { @@ -2254,7 +2216,8 @@ void tst_QSortFilterProxyModel::sortFilterRole() model.setData(index, sourceItems.at(i).second, Qt::UserRole); } - proxy.setFilterRegExp("2"); + setupFilter(&proxy, QLatin1String("2")); + QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role proxy.setFilterRole(Qt::UserRole); @@ -2263,7 +2226,8 @@ void tst_QSortFilterProxyModel::sortFilterRole() proxy.setFilterRole(Qt::DisplayRole); QCOMPARE(proxy.rowCount(), 0); - proxy.setFilterRegExp("1|2|3"); + setupFilter(&proxy, QLatin1String("1|2|3")); + QCOMPARE(proxy.rowCount(), 0); proxy.setFilterRole(Qt::UserRole); @@ -2274,7 +2238,8 @@ void tst_QSortFilterProxyModel::sortFilterRole() proxy.setSortRole(Qt::UserRole); proxy.setFilterRole(Qt::DisplayRole); - proxy.setFilterRegExp("a|c"); + setupFilter(&proxy, QLatin1String("a|c")); + QCOMPARE(proxy.rowCount(), orderedItems.count()); for (int i = 0; i < proxy.rowCount(); ++i) { QModelIndex index = proxy.index(i, 0, QModelIndex()); @@ -2298,7 +2263,8 @@ void tst_QSortFilterProxyModel::selectionFilteredOut() view.setCurrentIndex(proxy.index(0, 0)); QCOMPARE(spy.count(), 1); - proxy.setFilterRegExp(QRegExp("^B")); + + setupFilter(&proxy, QLatin1String("^B")); QCOMPARE(spy.count(), 2); } @@ -2384,7 +2350,7 @@ void tst_QSortFilterProxyModel::match() } proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); - proxy.setFilterRegExp(filter); + setupFilter(&proxy, filter); QModelIndex startIndex = proxy.index(proxyStartRow, 0); QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what, @@ -2509,7 +2475,8 @@ void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild() QSortFilterProxyModel proxy; proxy.setSourceModel(&model); - proxy.setFilterRegExp("A|B"); + setupFilter(&proxy, QLatin1String("A|B")); + QStandardItem *itemA = new QStandardItem("A"); model.appendRow(itemA); // not filtered QStandardItem *itemB = new QStandardItem("B"); @@ -2523,7 +2490,7 @@ void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild() QVERIFY(removedSpy.isValid()); QVERIFY(insertedSpy.isValid()); - proxy.setFilterRegExp("C"); // A and B will be filtered out, C filtered in + setupFilter(&proxy, QLatin1String("C")); // A and B will be filtered out, C filtered in // we should now have been notified that the subtree represented by itemA has been removed QCOMPARE(removedSpy.count(), 1); @@ -2936,7 +2903,7 @@ void tst_QSortFilterProxyModel::hiddenChildren() itemA->appendRow(itemB); QStandardItem *itemC = new QStandardItem("C"); itemA->appendRow(itemC); - proxy.setFilterRegExp("VISIBLE"); + setupFilter(&proxy, QLatin1String("VISIBLE")); QCOMPARE(proxy.rowCount(QModelIndex()) , 1); QPersistentModelIndex indexA = proxy.index(0,0); @@ -2963,7 +2930,8 @@ void tst_QSortFilterProxyModel::hiddenChildren() QModelIndex indexC = proxy.index(0, 0, indexA); QCOMPARE(proxy.data(indexC).toString(), QString::fromLatin1("C VISIBLE")); - proxy.setFilterRegExp("C"); + setupFilter(&proxy, QLatin1String("C")); + QCOMPARE(proxy.rowCount(QModelIndex()), 0); itemC->setText("invisible"); itemA->setText("AC"); @@ -3285,7 +3253,8 @@ void tst_QSortFilterProxyModel::mapSelectionFromSource() QSortFilterProxyModel proxy; proxy.setDynamicSortFilter(true); - proxy.setFilterRegExp("d.*"); + setupFilter(&proxy, QLatin1String("d.*")); + proxy.setSourceModel(&model); // Only "delta" remains. @@ -3840,13 +3809,13 @@ void tst_QSortFilterProxyModel::moveSourceRows() filterProxy.setDynamicSortFilter(true); filterProxy.sort(0, Qt::AscendingOrder); filterProxy.setSourceModel(&proxy); - filterProxy.setFilterRegExp("6"); // One of the parents + setupFilter(&filterProxy, QLatin1String("6")); // One of the parents QSortFilterProxyModel filterBothProxy; filterBothProxy.setDynamicSortFilter(true); filterBothProxy.sort(0, Qt::AscendingOrder); filterBothProxy.setSourceModel(&proxy); - filterBothProxy.setFilterRegExp("5"); // The parents are 6 and 3. This filters both out. + setupFilter(&filterBothProxy, QLatin1String("5")); // The parents are 6 and 3. This filters both out. QSignalSpy modelBeforeSpy(&model, &DynamicTreeModel::rowsAboutToBeMoved); QSignalSpy modelAfterSpy(&model, &DynamicTreeModel::rowsMoved); @@ -4244,7 +4213,7 @@ void tst_QSortFilterProxyModel::filterHint() QSortFilterProxyModel proxy2; proxy2.setSourceModel(&proxy1); proxy2.setFilterRole(Qt::DisplayRole); - proxy2.setFilterRegExp("^[^ ]*$"); + setupFilter(&proxy2, QLatin1String("^[^ ]*$")); proxy2.setDynamicSortFilter(true); QSignalSpy proxy1BeforeSpy(&proxy1, &QSortFilterProxyModel::layoutAboutToBeChanged); @@ -4372,8 +4341,7 @@ void tst_QSortFilterProxyModel::sourceLayoutChangeLeavesValidPersistentIndexes() QSortFilterProxyModel proxy1; proxy1.setSourceModel(&model); Q_SET_OBJECT_NAME(proxy1); - - proxy1.setFilterRegExp("1|2"); + setupFilter(&proxy1, QLatin1String("1|2")); // The current state of things: // model proxy @@ -4420,7 +4388,7 @@ void tst_QSortFilterProxyModel::rowMoveLeavesValidPersistentIndexes() proxy1.setSourceModel(&model); Q_SET_OBJECT_NAME(proxy1); - proxy1.setFilterRegExp("1|2"); + setupFilter(&proxy1, QLatin1String("1|2")); auto item5 = model.match(model.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first(); auto item3 = model.match(model.index(0, 0), Qt::DisplayRole, "3", 1, Qt::MatchRecursive).first(); @@ -4482,6 +4450,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); @@ -4491,45 +4497,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) @@ -4567,6 +4534,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()) + setupFilter(&proxy, 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; @@ -4849,5 +4947,4 @@ void tst_QSortFilterProxyModel::filterAndInsertRow() } } -QTEST_MAIN(tst_QSortFilterProxyModel) #include "tst_qsortfilterproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.h b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.h new file mode 100644 index 0000000000..82d4b7344e --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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$ +** +****************************************************************************/ + +#ifndef TST_QSORTFILTERPROXYMODEL_H +#define TST_QSORTFILTERPROXYMODEL_H + +#include <QtTest/QtTest> +#include "dynamictreemodel.h" + +#include <QtCore/QCoreApplication> +#include <QtGui/QStandardItem> +#include <QtWidgets/QTreeView> +#include <QtWidgets/QTableView> + +#include <qdebug.h> + +typedef QList<int> IntList; +typedef QPair<int, int> IntPair; +typedef QList<IntPair> IntPairList; + +enum class FilterType { + RegExp, + RegularExpression +}; + +Q_DECLARE_METATYPE(QList<QPersistentModelIndex>) + +class tst_QSortFilterProxyModel : public QObject +{ + Q_OBJECT + +public: + tst_QSortFilterProxyModel(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void cleanup(); + +private slots: + void getSetCheck(); + void sort_data(); + void sort(); + void sortHierarchy_data(); + void sortHierarchy(); + + void insertRows_data(); + void insertRows(); + void prependRow(); + void appendRowFromCombobox_data(); + void appendRowFromCombobox(); + void removeRows_data(); + void removeRows(); + void removeColumns_data(); + void removeColumns(); + void insertAfterSelect(); + void removeAfterSelect(); + void filter_data(); + void filter(); + void filterHierarchy_data(); + void filterHierarchy(); + void filterColumns_data(); + void filterColumns(); + + void filterTable(); + void filterCurrent(); + void filter_qtbug30662(); + + void changeSourceLayout(); + void changeSourceLayoutFilteredOut(); + void removeSourceRows_data(); + void removeSourceRows(); + void insertSourceRows_data(); + void insertSourceRows(); + void changeFilter_data(); + void changeFilter(); + void changeSourceData_data(); + void changeSourceData(); + void changeSourceDataKeepsStableSorting_qtbug1548(); + void changeSourceDataForwardsRoles_qtbug35440(); + void resortingDoesNotBreakTreeModels(); + void dynamicFilterWithoutSort(); + void sortFilterRole(); + void selectionFilteredOut(); + void match_data(); + void match(); + void insertIntoChildrenlessItem(); + void invalidateMappedChildren(); + void insertRowIntoFilteredParent(); + void filterOutParentAndFilterInChild(); + + void sourceInsertRows(); + void sourceModelDeletion(); + + void sortColumnTracking1(); + void sortColumnTracking2(); + + void sortStable(); + + void hiddenColumns(); + void insertRowsSort(); + void staticSorting(); + void dynamicSorting(); + void fetchMore(); + void hiddenChildren(); + void mapFromToSource(); + void removeRowsRecursive(); + void doubleProxySelectionSetSourceModel(); + void appearsAndSort(); + void unnecessaryDynamicSorting(); + void unnecessaryMapCreation(); + void resetInvalidate_data(); + void resetInvalidate(); + + void testMultipleProxiesWithSelection(); + void mapSelectionFromSource(); + void testResetInternalData(); + void filteredColumns(); + void headerDataChanged(); + + void testParentLayoutChanged(); + void moveSourceRows(); + + void hierarchyFilterInvalidation(); + void simpleFilterInvalidation(); + + void chainedProxyModelRoleNames(); + + void noMapAfterSourceDelete(); + void forwardDropApi(); + void canDropMimeData(); + void filterHint(); + + void sourceLayoutChangeLeavesValidPersistentIndexes(); + void rowMoveLeavesValidPersistentIndexes(); + + void emitLayoutChangedOnlyIfSortingChanged_data(); + void emitLayoutChangedOnlyIfSortingChanged(); + + void checkSetNewModel(); + void filterAndInsertRow_data(); + void filterAndInsertRow(); + void filterAndInsertColumn_data(); + void filterAndInsertColumn(); + + void removeIntervals_data(); + void removeIntervals(); + +protected: + void buildHierarchy(const QStringList &data, QAbstractItemModel *model); + void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); + void setupFilter(QSortFilterProxyModel *model, const QString& pattern); + +protected: + FilterType m_filterType; + +private: + QStandardItemModel *m_model; + QSortFilterProxyModel *m_proxy; +}; + +Q_DECLARE_METATYPE(QAbstractItemModel::LayoutChangeHint) + +#endif // TST_QSORTFILTERPROXYMODEL_H diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/.gitignore new file mode 100644 index 0000000000..4fdaebc09d --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/.gitignore @@ -0,0 +1 @@ +tst_qsortfilterproxymodel_regexp diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/qsortfilterproxymodel_regexp.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/qsortfilterproxymodel_regexp.pro new file mode 100644 index 0000000000..7c510930f4 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/qsortfilterproxymodel_regexp.pro @@ -0,0 +1,16 @@ +CONFIG += testcase +TARGET = tst_qsortfilterproxymodel_regexp + +QT += widgets testlib +mtdir = ../../../other/qabstractitemmodelutils +qsfpmdir = ../qsortfilterproxymodel_common + +INCLUDEPATH += $$PWD/$${mtdir} $$PWD/$${qsfpmdir} +SOURCES += \ + tst_qsortfilterproxymodel_regexp.cpp \ + $${qsfpmdir}/tst_qsortfilterproxymodel.cpp \ + $${mtdir}/dynamictreemodel.cpp + +HEADERS += \ + $${qsfpmdir}/tst_qsortfilterproxymodel.h \ + $${mtdir}/dynamictreemodel.h diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/tst_qsortfilterproxymodel_regexp.cpp index a815c6cab9..38607f1378 100644 --- a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/tst_qsortfilterproxymodel_regexp.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -27,13 +27,43 @@ ****************************************************************************/ #include <QtTest/QtTest> -#include "../tst_qregularexpression.h" -class tst_QRegularExpression_DefaultOptimize : public tst_QRegularExpression +#include "tst_qsortfilterproxymodel.h" + +class tst_QSortFilterProxyModelRegExp : public tst_QSortFilterProxyModel { Q_OBJECT +public: + tst_QSortFilterProxyModelRegExp(); +private slots: + void tst_invalid(); + void tst_caseSensitivity(); }; -QTEST_APPLESS_MAIN(tst_QRegularExpression_DefaultOptimize) +tst_QSortFilterProxyModelRegExp::tst_QSortFilterProxyModelRegExp() : + tst_QSortFilterProxyModel() +{ + m_filterType = FilterType::RegExp; +} + +void tst_QSortFilterProxyModelRegExp::tst_invalid() +{ + const QLatin1String pattern("test"); + QSortFilterProxyModel model; + model.setFilterRegExp(pattern); + QCOMPARE(model.filterRegExp(), QRegExp(pattern)); + model.setFilterRegularExpression(pattern); + QCOMPARE(model.filterRegExp(), QRegExp()); +} + +void tst_QSortFilterProxyModelRegExp::tst_caseSensitivity() +{ + const QLatin1String pattern("test"); + QSortFilterProxyModel model; + model.setFilterCaseSensitivity(Qt::CaseInsensitive); + model.setFilterRegExp(pattern); + QCOMPARE(model.filterCaseSensitivity(), Qt::CaseInsensitive); +} -#include "tst_qregularexpression_defaultoptimize.moc" +QTEST_MAIN(tst_QSortFilterProxyModelRegExp) +#include "tst_qsortfilterproxymodel_regexp.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/.gitignore new file mode 100644 index 0000000000..286771e250 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/.gitignore @@ -0,0 +1 @@ +tst_qsortfilterproxymodel_regularexpression diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/qsortfilterproxymodel_regularexpression.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/qsortfilterproxymodel_regularexpression.pro new file mode 100644 index 0000000000..e993d07126 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/qsortfilterproxymodel_regularexpression.pro @@ -0,0 +1,16 @@ +CONFIG += testcase +TARGET = tst_qsortfilterproxymodel_regularexpression + +QT += widgets testlib +mtdir = ../../../other/qabstractitemmodelutils +qsfpmdir = ../qsortfilterproxymodel_common + +INCLUDEPATH += $$PWD/$${mtdir} $${qsfpmdir} +SOURCES += \ + tst_qsortfilterproxymodel_regularexpression.cpp \ + $${qsfpmdir}/tst_qsortfilterproxymodel.cpp \ + $${mtdir}/dynamictreemodel.cpp + +HEADERS += \ + $${qsfpmdir}/tst_qsortfilterproxymodel.h \ + $${mtdir}/dynamictreemodel.h diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/tst_qsortfilterproxymodel_regularexpression.cpp index 6d2ae48235..821e199bcb 100644 --- a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/tst_qsortfilterproxymodel_regularexpression.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -27,25 +27,33 @@ ****************************************************************************/ #include <QtTest/QtTest> -#include "../tst_qregularexpression.h" -class tst_QRegularExpression_AlwaysOptimize : public tst_QRegularExpression +#include "tst_qsortfilterproxymodel.h" + +class tst_QSortFilterProxyModelRegularExpression : public tst_QSortFilterProxyModel { Q_OBJECT - +public: + tst_QSortFilterProxyModelRegularExpression(); private slots: - void initTestCase(); + void tst_invalid(); }; -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() +tst_QSortFilterProxyModelRegularExpression::tst_QSortFilterProxyModelRegularExpression() : + tst_QSortFilterProxyModel() { - qt_qregularexpression_optimize_after_use_count = 1; + m_filterType = FilterType::RegularExpression; } -QTEST_APPLESS_MAIN(tst_QRegularExpression_AlwaysOptimize) +void tst_QSortFilterProxyModelRegularExpression::tst_invalid() +{ + const QLatin1String pattern("test"); + QSortFilterProxyModel model; + model.setFilterRegularExpression(pattern); + QCOMPARE(model.filterRegularExpression(), QRegularExpression(pattern)); + model.setFilterRegExp(pattern); + QCOMPARE(model.filterRegularExpression(), QRegularExpression()); +} -#include "tst_qregularexpression_alwaysoptimize.moc" +QTEST_MAIN(tst_QSortFilterProxyModelRegularExpression) +#include "tst_qsortfilterproxymodel_regularexpression.moc" diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp index 5e9dbdd226..a53501b9dd 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp @@ -415,7 +415,7 @@ void tst_QCoreApplication::removePostedEvents() expected.clear(); } -#ifndef QT_NO_THREAD +#if QT_CONFIG(thread) class DeliverInDefinedOrderThread : public QThread { Q_OBJECT @@ -532,7 +532,7 @@ void tst_QCoreApplication::deliverInDefinedOrder() QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit())); app.exec(); } -#endif // QT_NO_QTHREAD +#endif // QT_CONFIG(thread) void tst_QCoreApplication::applicationPid() { diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h index b6c20a915f..105cca5174 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h @@ -43,7 +43,7 @@ private slots: void argc(); void postEvent(); void removePostedEvents(); -#ifndef QT_NO_THREAD +#if QT_CONFIG(thread) void deliverInDefinedOrder(); #endif void applicationPid(); diff --git a/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST b/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST index 4cd3c2f0c8..4dd71ca9f4 100644 --- a/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST +++ b/tests/auto/corelib/kernel/qelapsedtimer/BLACKLIST @@ -1,3 +1,4 @@ [elapsed] windows osx-10.12 +osx-10.13 diff --git a/tests/auto/corelib/kernel/qeventdispatcher/BLACKLIST b/tests/auto/corelib/kernel/qeventdispatcher/BLACKLIST index 402d87b82f..06588188d4 100644 --- a/tests/auto/corelib/kernel/qeventdispatcher/BLACKLIST +++ b/tests/auto/corelib/kernel/qeventdispatcher/BLACKLIST @@ -1,6 +1,6 @@ [sendPostedEvents] windows -osx [registerTimer] windows +winrt osx diff --git a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp index 5784f0728c..49c10c6a24 100644 --- a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp +++ b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp @@ -28,6 +28,7 @@ #ifdef QT_GUI_LIB # include <QtGui/QGuiApplication> +# define tst_QEventDispatcher tst_QGuiEventDispatcher #else # include <QtCore/QCoreApplication> #endif @@ -92,77 +93,151 @@ void tst_QEventDispatcher::initTestCase() } } +class TimerManager { + Q_DISABLE_COPY(TimerManager) + +public: + TimerManager(QAbstractEventDispatcher *eventDispatcher, QObject *parent) + : m_eventDispatcher(eventDispatcher), m_parent(parent) + { + } + + ~TimerManager() + { + if (!registeredTimers().isEmpty()) + m_eventDispatcher->unregisterTimers(m_parent); + } + + TimerManager(TimerManager &&) = delete; + TimerManager &operator=(TimerManager &&) = delete; + + int preciseTimerId() const { return m_preciseTimerId; } + int coarseTimerId() const { return m_coarseTimerId; } + int veryCoarseTimerId() const { return m_veryCoarseTimerId; } + + bool foundPrecise() const { return m_preciseTimerId > 0; } + bool foundCoarse() const { return m_coarseTimerId > 0; } + bool foundVeryCoarse() const { return m_veryCoarseTimerId > 0; } + + QList<QAbstractEventDispatcher::TimerInfo> registeredTimers() const + { + return m_eventDispatcher->registeredTimers(m_parent); + } + + void registerAll() + { + // start 3 timers, each with the different timer types and different intervals + m_preciseTimerId = m_eventDispatcher->registerTimer( + PreciseTimerInterval, Qt::PreciseTimer, m_parent); + m_coarseTimerId = m_eventDispatcher->registerTimer( + CoarseTimerInterval, Qt::CoarseTimer, m_parent); + m_veryCoarseTimerId = m_eventDispatcher->registerTimer( + VeryCoarseTimerInterval, Qt::VeryCoarseTimer, m_parent); + QVERIFY(m_preciseTimerId > 0); + QVERIFY(m_coarseTimerId > 0); + QVERIFY(m_veryCoarseTimerId > 0); + findTimers(); + } + + void unregister(int timerId) + { + m_eventDispatcher->unregisterTimer(timerId); + findTimers(); + } + + void unregisterAll() + { + m_eventDispatcher->unregisterTimers(m_parent); + findTimers(); + } + +private: + void findTimers() + { + bool foundPrecise = false; + bool foundCoarse = false; + bool foundVeryCoarse = false; + const QList<QAbstractEventDispatcher::TimerInfo> timers = registeredTimers(); + for (int i = 0; i < timers.count(); ++i) { + const QAbstractEventDispatcher::TimerInfo &timerInfo = timers.at(i); + if (timerInfo.timerId == m_preciseTimerId) { + QCOMPARE(timerInfo.interval, int(PreciseTimerInterval)); + QCOMPARE(timerInfo.timerType, Qt::PreciseTimer); + foundPrecise = true; + } else if (timerInfo.timerId == m_coarseTimerId) { + QCOMPARE(timerInfo.interval, int(CoarseTimerInterval)); + QCOMPARE(timerInfo.timerType, Qt::CoarseTimer); + foundCoarse = true; + } else if (timerInfo.timerId == m_veryCoarseTimerId) { + QCOMPARE(timerInfo.interval, int(VeryCoarseTimerInterval)); + QCOMPARE(timerInfo.timerType, Qt::VeryCoarseTimer); + foundVeryCoarse = true; + } + } + if (!foundPrecise) + m_preciseTimerId = -1; + if (!foundCoarse) + m_coarseTimerId = -1; + if (!foundVeryCoarse) + m_veryCoarseTimerId = -1; + } + + QAbstractEventDispatcher *m_eventDispatcher = nullptr; + + int m_preciseTimerId = -1; + int m_coarseTimerId = -1; + int m_veryCoarseTimerId = -1; + + QObject *m_parent = nullptr; +}; + // test that the eventDispatcher's timer implementation is complete and working void tst_QEventDispatcher::registerTimer() { -#define FIND_TIMERS() \ - do { \ - foundPrecise = false; \ - foundCoarse = false; \ - foundVeryCoarse = false; \ - for (int i = 0; i < registeredTimers.count(); ++i) { \ - const QAbstractEventDispatcher::TimerInfo &timerInfo = registeredTimers.at(i); \ - if (timerInfo.timerId == preciseTimerId) { \ - QCOMPARE(timerInfo.interval, int(PreciseTimerInterval)); \ - QCOMPARE(timerInfo.timerType, Qt::PreciseTimer); \ - foundPrecise = true; \ - } else if (timerInfo.timerId == coarseTimerId) { \ - QCOMPARE(timerInfo.interval, int(CoarseTimerInterval)); \ - QCOMPARE(timerInfo.timerType, Qt::CoarseTimer); \ - foundCoarse = true; \ - } else if (timerInfo.timerId == veryCoarseTimerId) { \ - QCOMPARE(timerInfo.interval, int(VeryCoarseTimerInterval)); \ - QCOMPARE(timerInfo.timerType, Qt::VeryCoarseTimer); \ - foundVeryCoarse = true; \ - } \ - } \ - } while (0) - - // start 3 timers, each with the different timer types and different intervals - int preciseTimerId = eventDispatcher->registerTimer(PreciseTimerInterval, Qt::PreciseTimer, this); - int coarseTimerId = eventDispatcher->registerTimer(CoarseTimerInterval, Qt::CoarseTimer, this); - int veryCoarseTimerId = eventDispatcher->registerTimer(VeryCoarseTimerInterval, Qt::VeryCoarseTimer, this); - QVERIFY(preciseTimerId > 0); - QVERIFY(coarseTimerId > 0); - QVERIFY(veryCoarseTimerId > 0); + TimerManager timers(eventDispatcher, this); + timers.registerAll(); + if (QTest::currentTestFailed()) + return; // check that all 3 are present in the eventDispatcher's registeredTimer() list - QList<QAbstractEventDispatcher::TimerInfo> registeredTimers = eventDispatcher->registeredTimers(this); - QCOMPARE(registeredTimers.count(), 3); - bool foundPrecise, foundCoarse, foundVeryCoarse; - FIND_TIMERS(); - QVERIFY(foundPrecise && foundCoarse && foundVeryCoarse); + QCOMPARE(timers.registeredTimers().count(), 3); + QVERIFY(timers.foundPrecise()); + QVERIFY(timers.foundCoarse()); + QVERIFY(timers.foundVeryCoarse()); // process events, waiting for the next event... this should only fire the precise timer receivedEventType = -1; timerIdFromEvent = -1; QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), PreciseTimerInterval * 2); - QCOMPARE(timerIdFromEvent, preciseTimerId); + QCOMPARE(timerIdFromEvent, timers.preciseTimerId()); // now unregister it and make sure it's gone - eventDispatcher->unregisterTimer(preciseTimerId); - registeredTimers = eventDispatcher->registeredTimers(this); - QCOMPARE(registeredTimers.count(), 2); - FIND_TIMERS(); - QVERIFY(!foundPrecise && foundCoarse && foundVeryCoarse); + timers.unregister(timers.preciseTimerId()); + if (QTest::currentTestFailed()) + return; + QCOMPARE(timers.registeredTimers().count(), 2); + QVERIFY(!timers.foundPrecise()); + QVERIFY(timers.foundCoarse()); + QVERIFY(timers.foundVeryCoarse()); // do the same again for the coarse timer receivedEventType = -1; timerIdFromEvent = -1; QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), CoarseTimerInterval * 2); - QCOMPARE(timerIdFromEvent, coarseTimerId); + QCOMPARE(timerIdFromEvent, timers.coarseTimerId()); // now unregister it and make sure it's gone - eventDispatcher->unregisterTimer(coarseTimerId); - registeredTimers = eventDispatcher->registeredTimers(this); - QCOMPARE(registeredTimers.count(), 1); - FIND_TIMERS(); - QVERIFY(!foundPrecise && !foundCoarse && foundVeryCoarse); + timers.unregister(timers.coarseTimerId()); + if (QTest::currentTestFailed()) + return; + QCOMPARE(timers.registeredTimers().count(), 1); + QVERIFY(!timers.foundPrecise()); + QVERIFY(!timers.foundCoarse()); + QVERIFY(timers.foundVeryCoarse()); // not going to wait for the VeryCoarseTimer, would take too long, just unregister it - eventDispatcher->unregisterTimers(this); - registeredTimers = eventDispatcher->registeredTimers(this); - QVERIFY(registeredTimers.isEmpty()); - -#undef FIND_TIMERS + timers.unregisterAll(); + if (QTest::currentTestFailed()) + return; + QVERIFY(timers.registeredTimers().isEmpty()); } void tst_QEventDispatcher::sendPostedEvents_data() diff --git a/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp b/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp index e4aa8b80c0..6ed0a6caa9 100644 --- a/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp +++ b/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp @@ -37,19 +37,33 @@ class tst_QMetaEnum : public QObject Q_OBJECT public: enum SuperEnum { SuperValue1 = 1 , SuperValue2 = 2 }; + enum Flag { Flag1 = 1 , Flag2 = 2 }; + Q_DECLARE_FLAGS(Flags, Flag) Q_ENUM(SuperEnum) + Q_FLAG(Flags) private slots: void fromType(); void valuesToKeys_data(); void valuesToKeys(); + void defaultConstructed(); }; void tst_QMetaEnum::fromType() { QMetaEnum meta = QMetaEnum::fromType<SuperEnum>(); QVERIFY(meta.isValid()); + QVERIFY(!meta.isFlag()); QCOMPARE(meta.name(), "SuperEnum"); + QCOMPARE(meta.enumName(), "SuperEnum"); + QCOMPARE(meta.enclosingMetaObject(), &staticMetaObject); + QCOMPARE(meta.keyCount(), 2); + + meta = QMetaEnum::fromType<Flags>(); + QVERIFY(meta.isValid()); + QVERIFY(meta.isFlag()); + QCOMPARE(meta.name(), "Flags"); + QCOMPARE(meta.enumName(), "Flag"); QCOMPARE(meta.enclosingMetaObject(), &staticMetaObject); QCOMPARE(meta.keyCount(), 2); } @@ -86,6 +100,15 @@ void tst_QMetaEnum::valuesToKeys() QCOMPARE(me.valueToKeys(windowFlags), expected); } +void tst_QMetaEnum::defaultConstructed() +{ + QMetaEnum e; + QVERIFY(!e.isValid()); + QVERIFY(!e.isScoped()); + QVERIFY(!e.isFlag()); + QCOMPARE(e.name(), QByteArray()); +} + Q_STATIC_ASSERT(QtPrivate::IsQEnumHelper<tst_QMetaEnum::SuperEnum>::Value); Q_STATIC_ASSERT(QtPrivate::IsQEnumHelper<Qt::WindowFlags>::Value); Q_STATIC_ASSERT(QtPrivate::IsQEnumHelper<Qt::Orientation>::Value); diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 431a9ebdea..9855bec520 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -54,6 +54,11 @@ namespace MyNamespace { MyEnum2, MyEnum3 }; + enum class MyScopedEnum { + Enum1, + Enum2, + Enum3 + }; enum MyAnotherEnum { MyAnotherEnum1 = 1, MyAnotherEnum2 = 2, @@ -64,7 +69,13 @@ namespace MyNamespace { MyFlag2 = 0x02, MyFlag3 = 0x04 }; + enum class MyScopedFlag { + MyFlag1 = 0x10, + MyFlag2 = 0x20, + MyFlag3 = 0x40 + }; Q_DECLARE_FLAGS(MyFlags, MyFlag) + Q_DECLARE_FLAGS(MyScopedFlags, MyScopedFlag) MyEnum myEnum() const { return m_enum; } void setMyEnum(MyEnum val) { m_enum = val; } @@ -79,8 +90,10 @@ namespace MyNamespace { { } private: Q_ENUM(MyEnum) + Q_ENUM(MyScopedEnum) Q_ENUM(MyAnotherEnum) Q_FLAG(MyFlags) + Q_FLAG(MyScopedFlags) MyEnum m_enum; MyFlags m_flags; @@ -1730,20 +1743,35 @@ void tst_QMetaObject::signalIndex() void tst_QMetaObject::enumDebugStream() { - QTest::ignoreMessage(QtDebugMsg, "hello MyNamespace::MyClass::MyEnum(MyEnum2) world "); - MyNamespace::MyClass::MyEnum e = MyNamespace::MyClass::MyEnum2; - qDebug() << "hello" << e << "world"; + QTest::ignoreMessage(QtDebugMsg, "hello MyNamespace::MyClass::MyEnum2 world "); + qDebug() << "hello" << MyNamespace::MyClass::MyEnum2 << "world"; + + QTest::ignoreMessage(QtDebugMsg, "hello MyNamespace::MyClass::MyScopedEnum::Enum3 scoped world "); + qDebug() << "hello" << MyNamespace::MyClass::MyScopedEnum::Enum3 << "scoped world"; - QTest::ignoreMessage(QtDebugMsg, "Qt::WindowType(WindowTitleHint) Qt::WindowType(Window) Qt::WindowType(Desktop) Qt::WindowType(WindowSystemMenuHint)"); - qDebug() << Qt::WindowTitleHint << Qt::Window <<Qt::Desktop << Qt::WindowSystemMenuHint; + QTest::ignoreMessage(QtDebugMsg, "Qt::WindowTitleHint Qt::Window Qt::Desktop Qt::WindowSystemMenuHint"); + qDebug() << Qt::WindowTitleHint << Qt::Window << Qt::Desktop << Qt::WindowSystemMenuHint; - QTest::ignoreMessage(QtDebugMsg, "hello QFlags<MyNamespace::MyClass::MyFlags>(MyFlag1) world"); + QTest::ignoreMessage(QtDebugMsg, "hello QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1) world"); MyNamespace::MyClass::MyFlags f1 = MyNamespace::MyClass::MyFlag1; qDebug() << "hello" << f1 << "world"; MyNamespace::MyClass::MyFlags f2 = MyNamespace::MyClass::MyFlag2 | MyNamespace::MyClass::MyFlag3; - QTest::ignoreMessage(QtDebugMsg, "QFlags<MyNamespace::MyClass::MyFlags>(MyFlag1) QFlags<MyNamespace::MyClass::MyFlags>(MyFlag2|MyFlag3)"); + QTest::ignoreMessage(QtDebugMsg, "QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1) QFlags<MyNamespace::MyClass::MyFlag>(MyFlag2|MyFlag3)"); qDebug() << f1 << f2; + + QTest::ignoreMessage(QtDebugMsg, "QFlags<MyNamespace::MyClass::MyScopedFlag>(MyFlag2)"); + MyNamespace::MyClass::MyScopedFlags f3 = MyNamespace::MyClass::MyScopedFlag::MyFlag2; + qDebug() << f3; + + QTest::ignoreMessage(QtDebugMsg, "QFlags<MyNamespace::MyClass::MyScopedFlag>(MyFlag2|MyFlag3)"); + f3 |= MyNamespace::MyClass::MyScopedFlag::MyFlag3; + qDebug() << f3; + + // Single flag recognized as enum: + QTest::ignoreMessage(QtDebugMsg, "MyNamespace::MyClass::MyFlag1"); + MyNamespace::MyClass::MyFlag f4 = MyNamespace::MyClass::MyFlag1; + qDebug() << f4; } void tst_QMetaObject::inherits_data() diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 6bb031e357..56623773a2 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -830,6 +830,7 @@ void tst_QMetaObjectBuilder::enumerator() // Modify the attributes on enum1. enum1.setIsFlag(true); enum1.setIsScoped(true); + enum1.setEnumName(QByteArrayLiteral("fooFlag")); QCOMPARE(enum1.addKey("ABC", 0), 0); QCOMPARE(enum1.addKey("DEF", 1), 1); QCOMPARE(enum1.addKey("GHI", -1), 2); @@ -838,6 +839,7 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(enum1.isFlag()); QVERIFY(enum1.isScoped()); + QCOMPARE(enum1.enumName(), QByteArray("fooFlag")); QCOMPARE(enum1.keyCount(), 3); QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.key(0), QByteArray("ABC")); diff --git a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro index ad148ccc7f..d70befecfd 100644 --- a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro +++ b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qmetatype -QT = core testlib +QT = core-private testlib INCLUDEPATH += $$PWD/../../../other/qvariant_common SOURCES = tst_qmetatype.cpp TESTDATA=./typeFlags.bin diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index e312199980..5d9b5ca95c 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -29,6 +29,7 @@ #include <QtCore> #include <QtTest/QtTest> +#include <QtCore/private/qmetaobjectbuilder_p.h> #include "tst_qmetatype.h" #include "tst_qvariant_common.h" @@ -38,6 +39,7 @@ #endif #include <algorithm> +#include <memory> // mingw gcc 4.8 also takes way too long, letting the CI system abort the test #if defined(__MINGW32__) @@ -52,12 +54,19 @@ class tst_QMetaType: public QObject Q_PROPERTY(QList<QVariant> prop READ prop WRITE setProp) public: + struct GadgetPropertyType { + QByteArray type; + QByteArray name; + QVariant testData; + }; + tst_QMetaType() { propList << 42 << "Hello"; } QList<QVariant> prop() const { return propList; } void setProp(const QList<QVariant> &list) { propList = list; } private: + void registerGadget(const char * name, const QVector<GadgetPropertyType> &gadgetProperties); QList<QVariant> propList; private slots: @@ -89,6 +98,7 @@ private slots: void flagsBinaryCompatibility5_0(); void construct_data(); void construct(); + void typedConstruct(); void constructCopy_data(); void constructCopy(); void typedefs(); @@ -115,6 +125,128 @@ private slots: void customDebugStream(); }; +struct BaseGenericType +{ + int m_typeId = -1; + virtual void *constructor(int typeId, void *where, const void *copy) = 0; + virtual void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) = 0; + virtual void saveOperator(QDataStream & out) const = 0; + virtual void loadOperator(QDataStream &in) = 0; + virtual ~BaseGenericType() {} +}; + +struct GenericGadgetType : BaseGenericType +{ + void *constructor(int typeId, void *where, const void *copy) override + { + GenericGadgetType *ret = where ? new(where) GenericGadgetType : new GenericGadgetType; + ret->m_typeId = typeId; + if (copy) { + Q_ASSERT(ret->m_typeId == reinterpret_cast<const GenericGadgetType*>(copy)->m_typeId); + *ret = *reinterpret_cast<const GenericGadgetType*>(copy); + } else { + ret->properties = properties; + } + return ret; + } + + void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) override + { + if (_c == QMetaObject::ReadProperty) { + if (_id < properties.size()) { + const auto &prop = properties.at(_id); + QMetaType::destruct(int(prop.userType()), _a[0]); + QMetaType::construct(int(prop.userType()), _a[0], prop.constData()); + } + } else if (_c == QMetaObject::WriteProperty) { + if (_id < properties.size()) { + auto & prop = properties[_id]; + prop = QVariant(prop.userType(), _a[0]); + } + } + } + + void saveOperator(QDataStream & out) const override + { + for (const auto &prop : properties) + out << prop; + } + + void loadOperator(QDataStream &in) override + { + for (auto &prop : properties) + in >> prop; + } + QVector<QVariant> properties; +}; + +struct GenericPODType : BaseGenericType +{ + // BaseGenericType interface + void *constructor(int typeId, void *where, const void *copy) override + { + GenericPODType *ret = where ? new(where) GenericPODType : new GenericPODType; + ret->m_typeId = typeId; + if (copy) { + Q_ASSERT(ret->m_typeId == reinterpret_cast<const GenericPODType*>(copy)->m_typeId); + *ret = *reinterpret_cast<const GenericPODType*>(copy); + } else { + ret->podData = podData; + } + return ret; + } + + void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) override + { + Q_UNUSED(_c); + Q_UNUSED(_id); + Q_UNUSED(_a); + Q_ASSERT(false); + } + + void saveOperator(QDataStream &out) const override + { + out << podData; + } + void loadOperator(QDataStream &in) override + { + in >> podData; + } + QByteArray podData; +}; + +using RegisteredType = QPair<std::shared_ptr<BaseGenericType>, std::shared_ptr<QMetaObject>>; +static QHash<int, RegisteredType> s_managedTypes; + +static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + reinterpret_cast<BaseGenericType*>(_o)->staticMetacallFunction(_c, _id, _a); +} + +static void GadgetTypedDestructor(int typeId, void *ptr) +{ + QCOMPARE(typeId, reinterpret_cast<BaseGenericType*>(ptr)->m_typeId); + reinterpret_cast<BaseGenericType*>(ptr)->~BaseGenericType(); +} + +static void *GadgetTypedConstructor(int type, void *where, const void *copy) +{ + auto it = s_managedTypes.find(type); + if (it == s_managedTypes.end()) + return nullptr; // crash the test + return it->first->constructor(type, where, copy); +} + +static void GadgetSaveOperator(QDataStream & out, const void *data) +{ + reinterpret_cast<const BaseGenericType *>(data)->saveOperator(out); +} + +static void GadgetLoadOperator(QDataStream &in, void *data) +{ + reinterpret_cast<BaseGenericType *>(data)->loadOperator(in); +} + struct Foo { int i; }; @@ -149,6 +281,34 @@ class GadgetDerivedAndTyped : public CustomGadget {}; Q_DECLARE_METATYPE(GadgetDerivedAndTyped<int>) Q_DECLARE_METATYPE(GadgetDerivedAndTyped<int>*) +void tst_QMetaType::registerGadget(const char *name, const QVector<GadgetPropertyType> &gadgetProperties) +{ + QMetaObjectBuilder gadgetBuilder; + gadgetBuilder.setClassName(name); + QMetaObjectBuilder::MetaObjectFlags metaObjectflags = QMetaObjectBuilder::DynamicMetaObject | QMetaObjectBuilder::PropertyAccessInStaticMetaCall; + gadgetBuilder.setFlags(metaObjectflags); + auto dynamicGadgetProperties = std::make_shared<GenericGadgetType>(); + for (const auto &prop : gadgetProperties) { + int propertyType = QMetaType::type(prop.type); + dynamicGadgetProperties->properties.push_back(QVariant(QVariant::Type(propertyType))); + auto dynamicPropery = gadgetBuilder.addProperty(prop.name, prop.type); + dynamicPropery.setWritable(true); + dynamicPropery.setReadable(true); + } + auto meta = gadgetBuilder.toMetaObject(); + meta->d.static_metacall = &GadgetsStaticMetacallFunction; + meta->d.superdata = nullptr; + const auto flags = QMetaType::WasDeclaredAsMetaType | QMetaType::IsGadget | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction; + int gadgetTypeId = QMetaType::registerType(name, + &GadgetTypedDestructor, + &GadgetTypedConstructor, + sizeof(GenericGadgetType), + flags, meta); + QVERIFY(gadgetTypeId > 0); + QMetaType::registerStreamOperators(gadgetTypeId, &GadgetSaveOperator, &GadgetLoadOperator); + s_managedTypes[gadgetTypeId] = qMakePair(dynamicGadgetProperties, std::shared_ptr<QMetaObject>{meta, [](QMetaObject *ptr){ ::free(ptr); }}); +} + void tst_QMetaType::defined() { QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1); @@ -906,6 +1066,90 @@ FOR_EACH_CORE_METATYPE(RETURN_CONSTRUCT_FUNCTION) TypeTestFunctionGetter::get(type)(); } +void tst_QMetaType::typedConstruct() +{ + auto testMetaObjectWriteOnGadget = [](QVariant &gadget, const QVector<GadgetPropertyType> &properties) + { + auto metaObject = QMetaType::metaObjectForType(gadget.userType()); + QVERIFY(metaObject != nullptr); + QCOMPARE(metaObject->methodCount(), 0); + QCOMPARE(metaObject->propertyCount(), properties.size()); + for (int i = 0; i < metaObject->propertyCount(); ++i) { + auto prop = metaObject->property(i); + QCOMPARE(properties[i].name, prop.name()); + QCOMPARE(properties[i].type, prop.typeName()); + prop.writeOnGadget(gadget.data(), properties[i].testData); + } + }; + + auto testMetaObjectReadOnGadget = [](QVariant gadget, const QVector<GadgetPropertyType> &properties) + { + auto metaObject = QMetaType::metaObjectForType(gadget.userType()); + QVERIFY(metaObject != nullptr); + QCOMPARE(metaObject->methodCount(), 0); + QCOMPARE(metaObject->propertyCount(), properties.size()); + for (int i = 0; i < metaObject->propertyCount(); ++i) { + auto prop = metaObject->property(i); + QCOMPARE(properties[i].name, prop.name()); + QCOMPARE(properties[i].type, prop.typeName()); + if (!QMetaType::typeFlags(prop.userType()).testFlag(QMetaType::IsGadget)) + QCOMPARE(properties[i].testData, prop.readOnGadget(gadget.constData())); + } + }; + + QVector<GadgetPropertyType> dynamicGadget1 = { + {"int", "int_prop", 34526}, + {"float", "float_prop", 1.23f}, + {"QString", "string_prop", QString{"Test QString"}} + }; + registerGadget("DynamicGadget1", dynamicGadget1); + + QVariant testGadget1(QVariant::Type(QMetaType::type("DynamicGadget1"))); + testMetaObjectWriteOnGadget(testGadget1, dynamicGadget1); + testMetaObjectReadOnGadget(testGadget1, dynamicGadget1); + + + QVector<GadgetPropertyType> dynamicGadget2 = { + {"int", "int_prop", 512}, + {"double", "double_prop", 4.56}, + {"QString", "string_prop", QString{"Another String"}}, + {"DynamicGadget1", "dynamicGadget1_prop", testGadget1} + }; + registerGadget("DynamicGadget2", dynamicGadget2); + QVariant testGadget2(QVariant::Type(QMetaType::type("DynamicGadget2"))); + testMetaObjectWriteOnGadget(testGadget2, dynamicGadget2); + testMetaObjectReadOnGadget(testGadget2, dynamicGadget2); + auto g2mo = QMetaType::metaObjectForType(testGadget2.userType()); + auto dynamicGadget1_prop = g2mo->property(g2mo->indexOfProperty("dynamicGadget1_prop")); + testMetaObjectReadOnGadget(dynamicGadget1_prop.readOnGadget(testGadget2.constData()), dynamicGadget1); + + + // Register POD + const QByteArray myPodTesData = "My POD test data"; + const char podTypeName[] = "DynamicPOD"; + auto dynamicGadgetProperties = std::make_shared<GenericPODType>(); + dynamicGadgetProperties->podData = myPodTesData; + const auto flags = QMetaType::NeedsConstruction | QMetaType::NeedsDestruction; + int podTypeId = QMetaType::registerType(podTypeName, + &GadgetTypedDestructor, + &GadgetTypedConstructor, + sizeof(GenericGadgetType), + flags, nullptr); + QVERIFY(podTypeId > 0); + QMetaType::registerStreamOperators(podTypeId, &GadgetSaveOperator, &GadgetLoadOperator); + s_managedTypes[podTypeId] = qMakePair(dynamicGadgetProperties, std::shared_ptr<QMetaObject>{}); + + // Test POD + QCOMPARE(podTypeId, QMetaType::type(podTypeName)); + QVariant podVariant{QVariant::Type(podTypeId)}; + QCOMPARE(myPodTesData, static_cast<const GenericPODType *>(reinterpret_cast<const BaseGenericType *>(podVariant.constData()))->podData); + + QVariant podVariant1{podVariant}; + podVariant1.detach(); // Test stream operators + static_cast<GenericPODType *>(reinterpret_cast<BaseGenericType *>(podVariant.data()))->podData.clear(); + QCOMPARE(myPodTesData, static_cast<const GenericPODType *>(reinterpret_cast<const BaseGenericType *>(podVariant1.constData()))->podData); +} + template<int ID> static void testConstructCopyHelper() { @@ -1574,6 +1818,9 @@ DECLARE_NONSTREAMABLE(QJsonValue) DECLARE_NONSTREAMABLE(QJsonObject) DECLARE_NONSTREAMABLE(QJsonArray) DECLARE_NONSTREAMABLE(QJsonDocument) +DECLARE_NONSTREAMABLE(QCborValue) +DECLARE_NONSTREAMABLE(QCborArray) +DECLARE_NONSTREAMABLE(QCborMap) DECLARE_NONSTREAMABLE(QObject*) DECLARE_NONSTREAMABLE(QWidget*) diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h index 93ff33bb67..6bda9638f7 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h @@ -273,6 +273,24 @@ template<> struct TestValueFactory<QMetaType::QJsonDocument> { ); } }; + +template<> struct TestValueFactory<QMetaType::QCborSimpleType> { + static QCborSimpleType *create() { return new QCborSimpleType(QCborSimpleType::True); } +}; +template<> struct TestValueFactory<QMetaType::QCborValue> { + static QCborValue *create() { return new QCborValue(123.); } +}; +template<> struct TestValueFactory<QMetaType::QCborMap> { + static QCborMap *create() { + return new QCborMap{{0, 0}, {"Hello", 1}, {1, nullptr}}; + } +}; +template<> struct TestValueFactory<QMetaType::QCborArray> { + static QCborArray *create() { + return new QCborArray{0, 1, -2, 2.5, false, nullptr, "Hello", QByteArray("World") }; + } +}; + template<> struct TestValueFactory<QMetaType::QVariant> { static QVariant *create() { return new QVariant(QStringList(QStringList() << "Q" << "t")); } }; 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/qobject.pro b/tests/auto/corelib/kernel/qobject/qobject.pro index 978aab86c1..75ad7b5f14 100644 --- a/tests/auto/corelib/kernel/qobject/qobject.pro +++ b/tests/auto/corelib/kernel/qobject/qobject.pro @@ -1,8 +1,4 @@ TEMPLATE = subdirs -!winrt { - test.depends = signalbug - SUBDIRS += signalbug -} - -SUBDIRS += test +SUBDIRS += test.pro +!winrt: SUBDIRS += signalbug diff --git a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro index cc51b4c661..d21b3a62a9 100644 --- a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro +++ b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.pro @@ -1,11 +1,6 @@ -CONFIG -= app_bundle debug_and_release -CONFIG += console -DESTDIR = ./ QT = core HEADERS += signalbug.h SOURCES += signalbug.cpp -# This app is testdata for tst_qobject -target.path = $$[QT_INSTALL_TESTS]/tst_qobject/$$TARGET -INSTALLS += target +load(qt_test_helper) diff --git a/tests/auto/corelib/kernel/qobject/test/test.pro b/tests/auto/corelib/kernel/qobject/test.pro index 4e77cb48c5..af5203e152 100644 --- a/tests/auto/corelib/kernel/qobject/test/test.pro +++ b/tests/auto/corelib/kernel/qobject/test.pro @@ -1,10 +1,10 @@ CONFIG += testcase console -TARGET = ../tst_qobject + QT = core-private network testlib -SOURCES = ../tst_qobject.cpp +TARGET = tst_qobject +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 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 ec57522f48..effc82261b 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(); @@ -104,6 +103,7 @@ private slots: void deleteQObjectWhenDeletingEvent(); void overloads(); void isSignalConnected(); + void isSignalConnectedAfterDisconnection(); void qMetaObjectConnect(); void qMetaObjectDisconnectOne(); void sameName(); @@ -145,6 +145,7 @@ private slots: void disconnectDoesNotLeakFunctor(); void contextDoesNotLeakFunctor(); void connectBase(); + void connectWarnings(); void qmlConnect(); void exceptions(); void noDeclarativeParentChangedOnDestruction(); @@ -282,14 +283,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; @@ -3026,7 +3019,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()); @@ -3843,6 +3836,58 @@ void tst_QObject::isSignalConnected() QVERIFY(!o.isSignalConnected(QMetaMethod())); } +void tst_QObject::isSignalConnectedAfterDisconnection() +{ + ManySignals o; + const QMetaObject *meta = o.metaObject(); + + const QMetaMethod sig00 = meta->method(meta->indexOfSignal("sig00()")); + QVERIFY(!o.isSignalConnected(sig00)); + QObject::connect(&o, &ManySignals::sig00, qt_noop); + QVERIFY(o.isSignalConnected(sig00)); + QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0)); + QVERIFY(!o.isSignalConnected(sig00)); + + const QMetaMethod sig69 = meta->method(meta->indexOfSignal("sig69()")); + QVERIFY(!o.isSignalConnected(sig69)); + QObject::connect(&o, &ManySignals::sig69, qt_noop); + QVERIFY(o.isSignalConnected(sig69)); + QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0)); + QVERIFY(!o.isSignalConnected(sig69)); + + { + ManySignals o2; + QObject::connect(&o, &ManySignals::sig00, &o2, &ManySignals::sig00); + QVERIFY(o.isSignalConnected(sig00)); + // o2 is destructed + } + QVERIFY(!o.isSignalConnected(sig00)); + + const QMetaMethod sig01 = meta->method(meta->indexOfSignal("sig01()")); + QObject::connect(&o, &ManySignals::sig00, qt_noop); + QObject::connect(&o, &ManySignals::sig01, qt_noop); + QObject::connect(&o, &ManySignals::sig69, qt_noop); + QVERIFY(o.isSignalConnected(sig00)); + QVERIFY(o.isSignalConnected(sig01)); + QVERIFY(o.isSignalConnected(sig69)); + QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0)); + QVERIFY(o.isSignalConnected(sig00)); + QVERIFY(o.isSignalConnected(sig01)); + QVERIFY(!o.isSignalConnected(sig69)); + QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0)); + QVERIFY(!o.isSignalConnected(sig00)); + QVERIFY(o.isSignalConnected(sig01)); + QVERIFY(!o.isSignalConnected(sig69)); + QObject::connect(&o, &ManySignals::sig69, qt_noop); + QVERIFY(!o.isSignalConnected(sig00)); + QVERIFY(o.isSignalConnected(sig01)); + QVERIFY(o.isSignalConnected(sig69)); + QVERIFY(QObject::disconnect(&o, &ManySignals::sig01, 0, 0)); + QVERIFY(!o.isSignalConnected(sig00)); + QVERIFY(!o.isSignalConnected(sig01)); + QVERIFY(o.isSignalConnected(sig69)); +} + void tst_QObject::qMetaObjectConnect() { SenderObject *s = new SenderObject; @@ -6697,6 +6742,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/qsharedmemory/sharedmemoryhelper/main.cpp b/tests/auto/corelib/kernel/qsharedmemory/producerconsumer/main.cpp index ffbad37d82..ffbad37d82 100644 --- a/tests/auto/corelib/kernel/qsharedmemory/sharedmemoryhelper/main.cpp +++ b/tests/auto/corelib/kernel/qsharedmemory/producerconsumer/main.cpp diff --git a/tests/auto/corelib/kernel/qsharedmemory/producerconsumer/producerconsumer.pro b/tests/auto/corelib/kernel/qsharedmemory/producerconsumer/producerconsumer.pro new file mode 100644 index 0000000000..a6156ed5b6 --- /dev/null +++ b/tests/auto/corelib/kernel/qsharedmemory/producerconsumer/producerconsumer.pro @@ -0,0 +1,5 @@ +QT = core testlib + +SOURCES += main.cpp + +load(qt_test_helper) diff --git a/tests/auto/corelib/kernel/qsharedmemory/qsharedmemory.pro b/tests/auto/corelib/kernel/qsharedmemory/qsharedmemory.pro index 3a4697750e..323d5bbd37 100644 --- a/tests/auto/corelib/kernel/qsharedmemory/qsharedmemory.pro +++ b/tests/auto/corelib/kernel/qsharedmemory/qsharedmemory.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs qtConfig(sharedmemory) { - !winrt: SUBDIRS = sharedmemoryhelper - SUBDIRS += test + !winrt: SUBDIRS = producerconsumer + SUBDIRS += test.pro } diff --git a/tests/auto/corelib/kernel/qsharedmemory/sharedmemoryhelper/sharedmemoryhelper.pro b/tests/auto/corelib/kernel/qsharedmemory/sharedmemoryhelper/sharedmemoryhelper.pro deleted file mode 100644 index 389015d504..0000000000 --- a/tests/auto/corelib/kernel/qsharedmemory/sharedmemoryhelper/sharedmemoryhelper.pro +++ /dev/null @@ -1,17 +0,0 @@ -QT = core testlib - -win32: CONFIG += console -mac:CONFIG -= app_bundle - -SOURCES += main.cpp -TARGET = helperbinary - -CONFIG(debug_and_release) { - CONFIG(debug, debug|release) { - DESTDIR = ../debug - } else { - DESTDIR = ../release - } -} else { - DESTDIR = .. -} diff --git a/tests/auto/corelib/kernel/qsharedmemory/test.pro b/tests/auto/corelib/kernel/qsharedmemory/test.pro new file mode 100644 index 0000000000..8b3badadb0 --- /dev/null +++ b/tests/auto/corelib/kernel/qsharedmemory/test.pro @@ -0,0 +1,8 @@ +CONFIG += testcase + +QT = core-private testlib + +TARGET = tst_qsharedmemory +SOURCES += tst_qsharedmemory.cpp + +linux: LIBS += -lrt diff --git a/tests/auto/corelib/kernel/qsharedmemory/test/test.pro b/tests/auto/corelib/kernel/qsharedmemory/test/test.pro deleted file mode 100644 index 61124c27ee..0000000000 --- a/tests/auto/corelib/kernel/qsharedmemory/test/test.pro +++ /dev/null @@ -1,18 +0,0 @@ -CONFIG += testcase - -QT = core-private testlib - -linux:LIBS += -lrt - -SOURCES += tst_qsharedmemory.cpp -TARGET = tst_qsharedmemory - -CONFIG(debug_and_release) { - CONFIG(debug, debug|release) { - DESTDIR = ../debug - } else { - DESTDIR = ../release - } -} else { - DESTDIR = .. -} diff --git a/tests/auto/corelib/kernel/qsharedmemory/test/tst_qsharedmemory.cpp b/tests/auto/corelib/kernel/qsharedmemory/tst_qsharedmemory.cpp index f81324b894..55deb8eb1a 100644 --- a/tests/auto/corelib/kernel/qsharedmemory/test/tst_qsharedmemory.cpp +++ b/tests/auto/corelib/kernel/qsharedmemory/tst_qsharedmemory.cpp @@ -51,7 +51,6 @@ public: public Q_SLOTS: void init(); - void initTestCase(); void cleanup(); @@ -101,7 +100,6 @@ private slots: void uniqueKey(); protected: - static QString helperBinary(); int remove(const QString &key); QString rememberKey(const QString &key) @@ -125,7 +123,7 @@ private: tst_QSharedMemory::tst_QSharedMemory() : existingSharedMemory(0) - , m_helperBinary(tst_QSharedMemory::helperBinary()) + , m_helperBinary("producerconsumer_helper") { } @@ -133,13 +131,6 @@ tst_QSharedMemory::~tst_QSharedMemory() { } -void tst_QSharedMemory::initTestCase() -{ -#if QT_CONFIG(process) - QVERIFY2(!m_helperBinary.isEmpty(), "Could not find helper binary"); -#endif -} - void tst_QSharedMemory::init() { existingSharedMemory = new QSharedMemory(EXISTING_SHARE); @@ -179,15 +170,6 @@ void tst_QSharedMemory::cleanup() #include <errno.h> #endif -QString tst_QSharedMemory::helperBinary() -{ - QString binary = QStringLiteral("helperbinary"); -#ifdef Q_OS_WIN - binary += QStringLiteral(".exe"); -#endif - return QFINDTESTDATA(binary); -} - int tst_QSharedMemory::remove(const QString &key) { #ifdef Q_OS_WIN diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/acquirerelease/acquirerelease.pro b/tests/auto/corelib/kernel/qsystemsemaphore/acquirerelease/acquirerelease.pro new file mode 100644 index 0000000000..a6156ed5b6 --- /dev/null +++ b/tests/auto/corelib/kernel/qsystemsemaphore/acquirerelease/acquirerelease.pro @@ -0,0 +1,5 @@ +QT = core testlib + +SOURCES += main.cpp + +load(qt_test_helper) diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/systemsemaphorehelper/main.cpp b/tests/auto/corelib/kernel/qsystemsemaphore/acquirerelease/main.cpp index 7bfb6b16cc..7bfb6b16cc 100644 --- a/tests/auto/corelib/kernel/qsystemsemaphore/systemsemaphorehelper/main.cpp +++ b/tests/auto/corelib/kernel/qsystemsemaphore/acquirerelease/main.cpp diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/qsystemsemaphore.pro b/tests/auto/corelib/kernel/qsystemsemaphore/qsystemsemaphore.pro index f8a49254d2..70526426aa 100644 --- a/tests/auto/corelib/kernel/qsystemsemaphore/qsystemsemaphore.pro +++ b/tests/auto/corelib/kernel/qsystemsemaphore/qsystemsemaphore.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = systemsemaphorehelper test +SUBDIRS = acquirerelease test.pro diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/systemsemaphorehelper/systemsemaphorehelper.pro b/tests/auto/corelib/kernel/qsystemsemaphore/systemsemaphorehelper/systemsemaphorehelper.pro deleted file mode 100644 index 389015d504..0000000000 --- a/tests/auto/corelib/kernel/qsystemsemaphore/systemsemaphorehelper/systemsemaphorehelper.pro +++ /dev/null @@ -1,17 +0,0 @@ -QT = core testlib - -win32: CONFIG += console -mac:CONFIG -= app_bundle - -SOURCES += main.cpp -TARGET = helperbinary - -CONFIG(debug_and_release) { - CONFIG(debug, debug|release) { - DESTDIR = ../debug - } else { - DESTDIR = ../release - } -} else { - DESTDIR = .. -} diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/test.pro b/tests/auto/corelib/kernel/qsystemsemaphore/test.pro new file mode 100644 index 0000000000..13bd1fa270 --- /dev/null +++ b/tests/auto/corelib/kernel/qsystemsemaphore/test.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +QT = core testlib + +SOURCES += tst_qsystemsemaphore.cpp +TARGET = tst_qsystemsemaphore + +win32: CONFIG += console diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/test/test.pro b/tests/auto/corelib/kernel/qsystemsemaphore/test/test.pro deleted file mode 100644 index f60207eb01..0000000000 --- a/tests/auto/corelib/kernel/qsystemsemaphore/test/test.pro +++ /dev/null @@ -1,17 +0,0 @@ -CONFIG += testcase -QT = core testlib - -win32: CONFIG += console - -SOURCES += tst_qsystemsemaphore.cpp -TARGET = tst_qsystemsemaphore - -CONFIG(debug_and_release) { - CONFIG(debug, debug|release) { - DESTDIR = ../debug - } else { - DESTDIR = ../release - } -} else { - DESTDIR = .. -} diff --git a/tests/auto/corelib/kernel/qsystemsemaphore/test/tst_qsystemsemaphore.cpp b/tests/auto/corelib/kernel/qsystemsemaphore/tst_qsystemsemaphore.cpp index 6ff1e14976..5f010ae3d1 100644 --- a/tests/auto/corelib/kernel/qsystemsemaphore/test/tst_qsystemsemaphore.cpp +++ b/tests/auto/corelib/kernel/qsystemsemaphore/tst_qsystemsemaphore.cpp @@ -42,7 +42,6 @@ public: tst_QSystemSemaphore(); public Q_SLOTS: - void initTestCase(); void init(); void cleanup(); @@ -65,22 +64,16 @@ private slots: void initialValue(); private: - static QString helperBinary(); QSystemSemaphore *existingLock; const QString m_helperBinary; }; tst_QSystemSemaphore::tst_QSystemSemaphore() - : m_helperBinary(helperBinary()) + : m_helperBinary("acquirerelease_helper") { } -void tst_QSystemSemaphore::initTestCase() -{ - QVERIFY2(!m_helperBinary.isEmpty(), "Could not find helper binary"); -} - void tst_QSystemSemaphore::init() { existingLock = new QSystemSemaphore(EXISTING_SHARE, 1, QSystemSemaphore::Create); @@ -294,14 +287,6 @@ void tst_QSystemSemaphore::initialValue() #endif } -QString tst_QSystemSemaphore::helperBinary() -{ - QString binary = QStringLiteral("helperbinary"); -#ifdef Q_OS_WIN - binary += QStringLiteral(".exe"); -#endif - return QFINDTESTDATA(binary); -} QTEST_MAIN(tst_QSystemSemaphore) #include "tst_qsystemsemaphore.moc" diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index 5b2d77a02c..8d194dafc1 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 callOnTimeout(); }; void tst_QTimer::zeroTimer() @@ -455,8 +456,7 @@ void tst_QTimer::moveToThread() #if defined(Q_OS_WIN32) QSKIP("Does not work reliably on Windows :("); #elif defined(Q_OS_MACOS) - if (__builtin_available(macOS 10.12, *)) - QSKIP("Does not work reliably on macOS 10.12 (QTBUG-59679)"); + QSKIP("Does not work reliably on macOS 10.12+ (QTBUG-59679)"); #endif QTimer ti1; QTimer ti2; @@ -979,5 +979,30 @@ void tst_QTimer::crossThreadSingleShotToFunctor() delete o; } +void tst_QTimer::callOnTimeout() +{ + QTimer timer; + QSignalSpy timeoutSpy(&timer, &QTimer::timeout); + timer.setInterval(0); + timer.start(); + + auto context = new QObject(); + + int count = 0; + timer.callOnTimeout([&count] { count++; }); + QMetaObject::Connection connection = timer.callOnTimeout(context, [&count] { count++; }); + timer.callOnTimeout(&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/qtranslator/android_testdata.qrc b/tests/auto/corelib/kernel/qtranslator/android_testdata.qrc index b33995ef21..39b85db664 100644 --- a/tests/auto/corelib/kernel/qtranslator/android_testdata.qrc +++ b/tests/auto/corelib/kernel/qtranslator/android_testdata.qrc @@ -1,6 +1,7 @@ <RCC> <qresource prefix="/android_testdata"> <file>hellotr_la.qm</file> + <file>hellotr_empty.qm</file> <file>msgfmt_from_po.qm</file> <file>dependencies_la.qm</file> </qresource> diff --git a/tests/auto/corelib/kernel/qtranslator/hellotr_empty.qm b/tests/auto/corelib/kernel/qtranslator/hellotr_empty.qm new file mode 100644 index 0000000000..be651eede2 --- /dev/null +++ b/tests/auto/corelib/kernel/qtranslator/hellotr_empty.qm @@ -0,0 +1 @@ +<d!`
\ No newline at end of file diff --git a/tests/auto/corelib/kernel/qtranslator/hellotr_empty.ts b/tests/auto/corelib/kernel/qtranslator/hellotr_empty.ts new file mode 100644 index 0000000000..85e0d3992b --- /dev/null +++ b/tests/auto/corelib/kernel/qtranslator/hellotr_empty.ts @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"/> diff --git a/tests/auto/corelib/kernel/qtranslator/qtranslator.pro b/tests/auto/corelib/kernel/qtranslator/qtranslator.pro index d8924c2d5f..a985f35a14 100644 --- a/tests/auto/corelib/kernel/qtranslator/qtranslator.pro +++ b/tests/auto/corelib/kernel/qtranslator/qtranslator.pro @@ -5,5 +5,5 @@ SOURCES = tst_qtranslator.cpp RESOURCES += qtranslator.qrc android:!android-embedded: RESOURCES += android_testdata.qrc -else: TESTDATA += dependencies_la.qm hellotr_la.qm msgfmt_from_po.qm +else: TESTDATA += dependencies_la.qm hellotr_empty.qm hellotr_la.qm msgfmt_from_po.qm diff --git a/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc b/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc index 333dcfaa21..cb82c6cc95 100644 --- a/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc +++ b/tests/auto/corelib/kernel/qtranslator/qtranslator.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="/tst_qtranslator"> <file>hellotr_la.qm</file> + <file>hellotr_empty.qm</file> </qresource> </RCC> diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp index 451f96339e..40a29c723c 100644 --- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp +++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp @@ -41,13 +41,12 @@ protected: private slots: void initTestCase(); + void load_data(); void load(); - void load2(); void threadLoad(); void testLanguageChange(); void plural(); void translate_qm_file_generated_with_msgfmt(); - void loadFromResource(); void loadDirectory(); void dependencies(); void translationInThreadWhileInstallingTranslator(); @@ -106,24 +105,45 @@ bool tst_QTranslator::eventFilter(QObject *, QEvent *event) return false; } -void tst_QTranslator::load() +void tst_QTranslator::load_data() { + QTest::addColumn<QString>("filepath"); + QTest::addColumn<bool>("isEmpty"); + QTest::addColumn<QString>("translation"); - QTranslator tor( 0 ); - tor.load("hellotr_la"); - QVERIFY(!tor.isEmpty()); - QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!")); + QTest::newRow("hellotr_la") << "hellotr_la.qm" << false << "Hallo Welt!"; + QTest::newRow("hellotr_empty") << "hellotr_empty.qm" << true << ""; } -void tst_QTranslator::load2() +void tst_QTranslator::load() { - QTranslator tor( 0 ); - QFile file("hellotr_la.qm"); - file.open(QFile::ReadOnly); - QByteArray data = file.readAll(); - tor.load((const uchar *)data.constData(), data.length()); - QVERIFY(!tor.isEmpty()); - QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!")); + QFETCH(QString, filepath); + QFETCH(bool, isEmpty); + QFETCH(QString, translation); + + { + QTranslator tor; + QVERIFY(tor.load(QFileInfo(filepath).baseName())); + QCOMPARE(tor.isEmpty(), isEmpty); + QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation); + } + + { + QFile file(filepath); + file.open(QFile::ReadOnly); + QByteArray data = file.readAll(); + QTranslator tor; + QVERIFY(tor.load((const uchar *)data.constData(), data.length())); + QCOMPARE(tor.isEmpty(), isEmpty); + QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation); + } + + { + QTranslator tor; + QVERIFY(tor.load(QString(":/tst_qtranslator/%1").arg(filepath))); + QCOMPARE(tor.isEmpty(), isEmpty); + QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation); + } } class TranslatorThread : public QThread @@ -240,14 +260,6 @@ void tst_QTranslator::translate_qm_file_generated_with_msgfmt() qApp->removeTranslator(&translator); } -void tst_QTranslator::loadFromResource() -{ - QTranslator tor; - tor.load(":/tst_qtranslator/hellotr_la.qm"); - QVERIFY(!tor.isEmpty()); - QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!")); -} - void tst_QTranslator::loadDirectory() { QString current_base = QDir::current().dirName(); diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 0780fb9172..4da34c407e 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -525,6 +525,12 @@ void tst_QVariant::canConvert_data() var = QVariant::fromValue<signed char>(-1); QTest::newRow("SChar") << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; + var = QVariant((short)-3); + QTest::newRow("Short") + << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; + var = QVariant((ushort)7); + QTest::newRow("UShort") + << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; var = QVariant::fromValue<QJsonValue>(QJsonValue(QStringLiteral("hello"))); QTest::newRow("JsonValue") << var << N << N << Y << N << N << N << N << N << N << Y << N << N << Y << N << N << Y << Y << Y << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; @@ -563,6 +569,8 @@ void tst_QVariant::toInt_data() QTest::newRow( "char" ) << QVariant::fromValue('a') << int('a') << true; signed char signedChar = -13; QTest::newRow( "signed char" ) << QVariant::fromValue(signedChar) << -13 << true; + QTest::newRow( "short" ) << QVariant::fromValue(short(-7)) << int(-7) << true; + QTest::newRow( "ushort" ) << QVariant::fromValue(ushort(30000)) << 30000 << true; QTest::newRow( "double" ) << QVariant( 3.1415927 ) << 3 << true; QTest::newRow( "float" ) << QVariant( 3.1415927f ) << 3 << true; QTest::newRow( "uint" ) << QVariant( 123u ) << 123 << true; 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/mimetypes/qmimedatabase/magic-and-hierarchy.foo b/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy.foo new file mode 100644 index 0000000000..ed5e761417 --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy.foo @@ -0,0 +1,3 @@ +<?foo> +<blah> +</blah> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy.xml b/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy.xml new file mode 100644 index 0000000000..27b5bd7e1f --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <!-- The real life example would be that this mime type is a sub class of application/xml + which has a magic that matches <?xml . + XML files with an early appearing qnx tag are detected as + application/vnd.qnx.bar-descriptor. We want that XML files without the qnx tag to be + identified as application/xml, independent of the order in which the two are registered. --> + <mime-type type="application/vnd.qnx.bar-descriptor"> + <sub-class-of type="application/foo"/> + <glob pattern="*.foo"/> + <magic><!-- higher priority than the parent magic --> + <match value="<qnx>" type="string" offset="0:200"/> + </magic> + </mime-type> + <mime-type type="application/foo"> + <glob pattern="*.foo"/> + <magic priority="40"> + <match value="<?foo" type="string" offset="0"/> + </magic> + </mime-type> +</mime-info> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy2.foo b/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy2.foo new file mode 100644 index 0000000000..fd90ed04b3 --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/magic-and-hierarchy2.foo @@ -0,0 +1,3 @@ +<?foo> +<qnx> +</qnx> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc b/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc index 29666627a1..1002d0195d 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc +++ b/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc @@ -7,5 +7,8 @@ <file>invalid-magic1.xml</file> <file>invalid-magic2.xml</file> <file>invalid-magic3.xml</file> + <file>magic-and-hierarchy.xml</file> + <file>magic-and-hierarchy.foo</file> + <file>magic-and-hierarchy2.foo</file> </qresource> </RCC> diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 3144c3071c..9df52887f7 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -52,6 +52,7 @@ static const char *const additionalMimeFiles[] = { "invalid-magic1.xml", "invalid-magic2.xml", "invalid-magic3.xml", + "magic-and-hierarchy.xml", 0 }; @@ -985,6 +986,26 @@ void tst_QMimeDatabase::installNewGlobalMimeType() qDebug() << objcsrc.globPatterns(); } + const QString fooTestFile = QLatin1String(RESOURCE_PREFIX "magic-and-hierarchy.foo"); + QCOMPARE(db.mimeTypeForFile(fooTestFile).name(), QString::fromLatin1("application/foo")); + + const QString fooTestFile2 = QLatin1String(RESOURCE_PREFIX "magic-and-hierarchy2.foo"); + QCOMPARE(db.mimeTypeForFile(fooTestFile2).name(), QString::fromLatin1("application/vnd.qnx.bar-descriptor")); + + // Test if we can use the default comment + { + struct RestoreLocale + { + ~RestoreLocale() { QLocale::setDefault(QLocale::c()); } + } restoreLocale; + + QLocale::setDefault(QLocale("zh_CN")); + QMimeType suseymp = db.mimeTypeForName("text/x-suse-ymp"); + QVERIFY(suseymp.isValid()); + QCOMPARE(suseymp.comment(), + QString::fromLatin1("YaST Meta Package")); + } + // Now test removing the mimetype definitions again for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) QFile::remove(destDir + m_additionalMimeFileNames.at(i)); diff --git a/tests/auto/corelib/plugin/plugin.pro b/tests/auto/corelib/plugin/plugin.pro index b094c24e55..240608fddf 100644 --- a/tests/auto/corelib/plugin/plugin.pro +++ b/tests/auto/corelib/plugin/plugin.pro @@ -11,6 +11,5 @@ qtConfig(library): SUBDIRS += \ contains(CONFIG, static) { message(Disabling tests requiring shared build of Qt) SUBDIRS -= qfactoryloader \ - qplugin \ - qpluginloader + qplugin } diff --git a/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp b/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp index d285ed79c0..a290c012df 100644 --- a/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp +++ b/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -30,8 +31,11 @@ #include <QCoreApplication> #include <QDebug> #include <QDir> +#include <qplugin.h> #include <QPluginLoader> +#include <private/qplugin_p.h> + class tst_QPlugin : public QObject { Q_OBJECT @@ -124,7 +128,10 @@ void tst_QPlugin::scanInvalidPlugin_data() { QTest::addColumn<QByteArray>("metadata"); QTest::addColumn<bool>("loads"); + QTest::addColumn<QString>("errMsg"); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + // Binary JSON metadata QByteArray prefix = "QTMETADATA "; { @@ -138,27 +145,65 @@ void tst_QPlugin::scanInvalidPlugin_data() obj.insert("debug", true); #endif obj.insert("MetaData", QJsonObject()); - QTest::newRow("control") << (prefix + QJsonDocument(obj).toBinaryData()) << true; + QTest::newRow("json-control") << (prefix + QJsonDocument(obj).toBinaryData()) << true << ""; } - QTest::newRow("zeroes") << prefix << false; + QTest::newRow("json-zeroes") << prefix << false << " "; prefix += "qbjs"; - QTest::newRow("bad-json-version0") << prefix << false; - QTest::newRow("bad-json-version2") << (prefix + QByteArray("\2\0\0\0", 4)) << false; + QTest::newRow("bad-json-version0") << prefix << false << " "; + QTest::newRow("bad-json-version2") << (prefix + QByteArray("\2\0\0\0", 4)) << false << " "; // valid qbjs version 1 prefix += QByteArray("\1\0\0\0"); // too large for the file (100 MB) - QTest::newRow("bad-json-size-large1") << (prefix + QByteArray("\0\0\x40\x06")) << false; + QTest::newRow("bad-json-size-large1") << (prefix + QByteArray("\0\0\x40\x06")) << false << " "; // too large for binary JSON (512 MB) - QTest::newRow("bad-json-size-large2") << (prefix + QByteArray("\0\0\0\x20")) << false; + QTest::newRow("bad-json-size-large2") << (prefix + QByteArray("\0\0\0\x20")) << false << " "; // could overflow - QTest::newRow("bad-json-size-large3") << (prefix + "\xff\xff\xff\x7f") << false; + QTest::newRow("bad-json-size-large3") << (prefix + "\xff\xff\xff\x7f") << false << " "; +#endif + // CBOR metadata + QByteArray cprefix = "QTMETADATA !1234"; + cprefix[12] = 0; // current version + cprefix[13] = QT_VERSION_MAJOR; + cprefix[14] = QT_VERSION_MINOR; + cprefix[15] = qPluginArchRequirements(); + + QByteArray cborValid = [] { + QCborMap m; + m.insert(int(QtPluginMetaDataKeys::IID), QLatin1String("org.qt-project.tst_qplugin")); + m.insert(int(QtPluginMetaDataKeys::ClassName), QLatin1String("tst")); + m.insert(int(QtPluginMetaDataKeys::MetaData), QCborMap()); + return QCborValue(m).toCbor(); + }(); + QTest::newRow("cbor-control") << (cprefix + cborValid) << true << ""; + + cprefix[12] = 1; + QTest::newRow("cbor-major-too-new") << (cprefix + cborValid) << false + << " Invalid metadata version"; + + cprefix[12] = 0; + cprefix[13] = QT_VERSION_MAJOR + 1; + QTest::newRow("cbor-major-too-new") << (cprefix + cborValid) << false << ""; + + cprefix[13] = QT_VERSION_MAJOR - 1; + QTest::newRow("cbor-major-too-old") << (cprefix + cborValid) << false << ""; + + cprefix[13] = QT_VERSION_MAJOR; + cprefix[14] = QT_VERSION_MINOR + 1; + QTest::newRow("cbor-minor-too-new") << (cprefix + cborValid) << false << ""; + + QTest::newRow("cbor-invalid") << (cprefix + "\xff") << false + << " Metadata parsing error: Invalid CBOR stream: unexpected 'break' byte"; + QTest::newRow("cbor-not-map1") << (cprefix + "\x01") << false + << " Unexpected metadata contents"; + QTest::newRow("cbor-not-map2") << (cprefix + "\x81\x01") << false + << " Unexpected metadata contents"; } static const char invalidPluginSignature[] = "qplugin testfile"; @@ -214,6 +259,11 @@ void tst_QPlugin::scanInvalidPlugin() // now try to load this QFETCH(bool, loads); + QFETCH(QString, errMsg); + if (!errMsg.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, + "Found invalid metadata in lib " + QFile::encodeName(newName) + + ":" + errMsg.toUtf8()); QPluginLoader loader(newName); QCOMPARE(loader.load(), loads); if (loads) diff --git a/tests/auto/corelib/plugin/qplugin/tst_qplugin.pro b/tests/auto/corelib/plugin/qplugin/tst_qplugin.pro index 8c6540fe87..4432ee20c1 100644 --- a/tests/auto/corelib/plugin/qplugin/tst_qplugin.pro +++ b/tests/auto/corelib/plugin/qplugin/tst_qplugin.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qplugin -QT = core testlib +QT = core-private testlib SOURCES = tst_qplugin.cpp TESTDATA += plugins/* diff --git a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h index 04ce042e24..ac349c2f75 100644 --- a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h +++ b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h @@ -35,7 +35,7 @@ class ThePlugin : public QObject, public PluginInterface { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.autotests.plugininterface" FILE "../empty.json") + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.autotests.plugininterface" FILE "../utf8_data.json") Q_INTERFACES(PluginInterface) public: diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index f34741281c..c517c0809a 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -140,6 +140,10 @@ void tst_QPluginLoader::cleanup() void tst_QPluginLoader::errorString() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif + const QString unknown(QLatin1String("Unknown error")); { @@ -206,6 +210,14 @@ void tst_QPluginLoader::errorString() { QPluginLoader loader( sys_qualifiedLibraryName("theplugin")); //a plugin + + // Check metadata + const QJsonObject metaData = loader.metaData(); + QCOMPARE(metaData.value("IID").toString(), QStringLiteral("org.qt-project.Qt.autotests.plugininterface")); + const QJsonObject kpluginObject = metaData.value("MetaData").toObject().value("KPlugin").toObject(); + QCOMPARE(kpluginObject.value("Name[mr]").toString(), QString::fromUtf8("चौकट भूमिती")); + + // Load QCOMPARE(loader.load(), true); QCOMPARE(loader.errorString(), unknown); @@ -224,6 +236,9 @@ void tst_QPluginLoader::errorString() void tst_QPluginLoader::loadHints() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif QPluginLoader loader; QCOMPARE(loader.loadHints(), (QLibrary::LoadHints)0); //Do not crash loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); @@ -233,6 +248,9 @@ void tst_QPluginLoader::loadHints() void tst_QPluginLoader::deleteinstanceOnUnload() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif for (int pass = 0; pass < 2; ++pass) { QPluginLoader loader1; loader1.setFileName( sys_qualifiedLibraryName("theplugin")); //a plugin @@ -268,6 +286,9 @@ void tst_QPluginLoader::deleteinstanceOnUnload() void tst_QPluginLoader::loadDebugObj() { +#if !defined(QT_SHARED) + QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds"); +#endif #if defined (__ELF__) QVERIFY(QFile::exists(QFINDTESTDATA("elftest/debugobj.so"))); QPluginLoader lib1(QFINDTESTDATA("elftest/debugobj.so")); @@ -277,6 +298,9 @@ void tst_QPluginLoader::loadDebugObj() void tst_QPluginLoader::loadCorruptElf() { +#if !defined(QT_SHARED) + QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds"); +#endif #if defined (__ELF__) if (sizeof(void*) == 8) { QVERIFY(QFile::exists(QFINDTESTDATA("elftest/corrupt1.elf64.so"))); @@ -377,6 +401,9 @@ void tst_QPluginLoader::loadMachO() #if defined (Q_OS_UNIX) void tst_QPluginLoader::loadGarbage() { +#if !defined(QT_SHARED) + QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds"); +#endif for (int i=0; i<5; i++) { const QString name = QLatin1String("elftest/garbage") + QString::number(i + 1) + QLatin1String(".so"); QPluginLoader lib(QFINDTESTDATA(name)); @@ -388,6 +415,9 @@ void tst_QPluginLoader::loadGarbage() void tst_QPluginLoader::relativePath() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif // Windows binaries run from release and debug subdirs, so we can't rely on the current dir. const QString binDir = QFINDTESTDATA("bin"); QVERIFY(!binDir.isEmpty()); @@ -402,6 +432,9 @@ void tst_QPluginLoader::relativePath() void tst_QPluginLoader::absolutePath() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif // Windows binaries run from release and debug subdirs, so we can't rely on the current dir. const QString binDir = QFINDTESTDATA("bin"); QVERIFY(!binDir.isEmpty()); @@ -416,6 +449,9 @@ void tst_QPluginLoader::absolutePath() void tst_QPluginLoader::reloadPlugin() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif QPluginLoader loader; loader.setFileName( sys_qualifiedLibraryName("theplugin")); //a plugin loader.load(); // not recommended, instance() should do the job. @@ -451,6 +487,9 @@ void tst_QPluginLoader::preloadedPlugin_data() void tst_QPluginLoader::preloadedPlugin() { +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif // check that using QPluginLoader does not interfere with QLibrary QFETCH(QString, libname); QLibrary lib(libname); diff --git a/tests/auto/corelib/plugin/qpluginloader/utf8_data.json b/tests/auto/corelib/plugin/qpluginloader/utf8_data.json new file mode 100644 index 0000000000..7763b65178 --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/utf8_data.json @@ -0,0 +1,17 @@ +{ + "KPlugin": { + "Name": "WindowGeometry", + "Name[mr]": "चौकट भूमिती", + "Name[pa]": "ਵਿੰਡੋਜੁਮੈਟਰੀ", + "Name[th]": "มิติขนาดของหน้าต่าง", + "Name[uk]": "Розміри вікна", + "Name[zh_CN]": "窗口形状", + "Name[zh_TW]": "視窗位置", + "ServiceTypes": [ + "KCModule" + ] + }, + "X-KDE-ParentComponents": [ + "windowgeometry" + ] +} 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..3dd4b5114c --- /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(useDevice ? QByteArray() : 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(useDevice ? QByteArray() : 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..4b753eab6b --- /dev/null +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -0,0 +1,1695 @@ +/**************************************************************************** +** +** 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(); + void toDiagnosticNotation_data(); + void toDiagnosticNotation(); +}; + +// 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()); + QVERIFY(extended == tagged); + QVERIFY(tagged == extended); + + QCOMPARE(extended.tag(), tagged.tag()); + QCOMPARE(extended.taggedValue(), tagged.taggedValue()); +} + +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 0 && 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()); +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) + QVERIFY(m.value("Hello").isUndefined()); +#endif + + 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)); + QCOMPARE(a.size(), 2); +} + +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()); + + // reinsert the element so we can take it + a.append(v); + QCOMPARE(a.takeAt(0), v); + QVERIFY(a.isEmpty()); +} + +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")); + + QCOMPARE(a.takeAt(1).toString(), "World"); + QCOMPARE(a.takeAt(0).toString(), "Hello"); + QVERIFY(a.isEmpty()); +} + +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")); + + QCOMPARE(m.extract(m.begin() + 1).toString(), "World"); + QCOMPARE(m.take(0).toString(), "Hello"); + QVERIFY(m.isEmpty()); +} + +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()); + + QCOMPARE(m.extract(it), v); + QVERIFY(!m.contains(42)); + + m[2] = v; + QCOMPARE(m.take(2), v); + QVERIFY(m.take(2).isUndefined()); + QVERIFY(m.isEmpty()); +} + +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); + QCOMPARE(a.takeAt(0).taggedValue(), v); + QCOMPARE(a.takeAt(0).taggedValue(), v); + QVERIFY(a.isEmpty()); +} + +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); + QCOMPARE(m.extract(m.end() - 1).taggedValue(), v); + QVERIFY(!m.contains(-21)); + QCOMPARE(m.take(11).taggedValue(), v); + QVERIFY(m.isEmpty()); +} + +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); + QCOMPARE(m.take(tagged).toInteger(), 47); + QVERIFY(!m.contains(tagged)); +} + +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{}); + } +} + +void tst_QCborValue::toDiagnosticNotation_data() +{ + QTest::addColumn<QCborValue>("v"); + QTest::addColumn<int>("opts"); + QTest::addColumn<QString>("expected"); + QDateTime dt = QDateTime::currentDateTimeUtc(); + QUuid uuid = QUuid::createUuid(); + + QMetaEnum me = QMetaEnum::fromType<QCborValue::Type>(); + auto add = [me](const QCborValue &v, const QString &exp) { + auto addRow = [=](const char *prefix) -> QTestData & { + QCborValue::Type t = v.type(); + if (t == QCborValue::Integer) + return QTest::addRow("%sInteger:%lld", prefix, v.toInteger()); + if (t == QCborValue::Double) + return QTest::addRow("%sDouble:%g", prefix, v.toDouble()); + if (t == QCborValue::ByteArray) + return QTest::addRow("%sByteArray:%d", prefix, v.toByteArray().size()); + if (t == QCborValue::String) + return QTest::addRow("%sString:%d", prefix, v.toString().size()); + + QByteArray typeString = me.valueToKey(t); + Q_ASSERT(!typeString.isEmpty()); + return QTest::newRow(prefix + typeString); + }; + addRow("") << v << int(QCborValue::DiagnosticNotationOptions{}) << exp; + addRow("LW:") << v << int(QCborValue::LineWrapped) << exp; + addRow("Array:") << QCborValue(QCborArray{v}) << int(QCborValue::DiagnosticNotationOptions{}) << '[' + exp + ']'; + addRow("Mapped:") << QCborValue(QCborMap{{2, v}}) << int(QCborValue::DiagnosticNotationOptions{}) << "{2: " + exp + '}'; + addRow("Mapping:") << QCborValue(QCborMap{{v, 2}}) << int(QCborValue::DiagnosticNotationOptions{}) << '{' + exp + ": 2}"; + }; + + // empty arrays and maps + QTest::newRow("EmptyArray") + << QCborValue(QCborArray()) << int(QCborValue::DiagnosticNotationOptions{}) + << "[]"; + QTest::newRow("EmptyMap") + << QCborValue(QCborMap()) << int(QCborValue::DiagnosticNotationOptions{}) + << "{}"; + + add(QCborValue(), "undefined"); + add(QCborValue::Null, "null"); + add(false, "false"); + add(true, "true"); + add(QCborSimpleType(0), "simple(0)"); + QTest::newRow("SimpleType-255") + << QCborValue(QCborSimpleType(255)) << int(QCborValue::DiagnosticNotationOptions{}) + << "simple(255)"; + add(0, "0"); + add(1, "1"); + add(-1, "-1"); + add(std::numeric_limits<qint64>::min(), QString::number(std::numeric_limits<qint64>::min())); + add(std::numeric_limits<qint64>::max(), QString::number(std::numeric_limits<qint64>::max())); + add(0., "0.0"); + add(1.25, "1.25"); + add(-1.25, "-1.25"); + add(qInf(), "inf"); + add(-qInf(), "-inf"); + add(qQNaN(), "nan"); + add(QByteArray(), "h''"); + add(QByteArray("Hello"), "h'48656c6c6f'"); + add(QLatin1String(), QLatin1String("\"\"")); + add("Hello", "\"Hello\""); + add("\"Hello\\World\"", "\"\\\"Hello\\\\World\\\"\""); + add(QCborValue(dt), "0(\"" + dt.toString(Qt::ISODateWithMs) + "\")"); + add(QCborValue(QUrl("http://example.com")), "32(\"http://example.com\")"); + add(QCborValue(QRegularExpression("^.*$")), "35(\"^.*$\")"); + add(QCborValue(uuid), "37(h'" + uuid.toString(QUuid::Id128) + "')"); + + // arrays and maps with more than one element + QTest::newRow("2Array") + << QCborValue(QCborArray{0, 1}) << int(QCborValue::DiagnosticNotationOptions{}) + << "[0, 1]"; + QTest::newRow("2Map") + << QCborValue(QCborMap{{0, 1}, {"foo", "bar"}}) << int(QCborValue::DiagnosticNotationOptions{}) + << "{0: 1, \"foo\": \"bar\"}"; + + // line wrapping in arrays and maps + QTest::newRow("LW:EmptyArray") + << QCborValue(QCborArray()) << int(QCborValue::LineWrapped) + << "[\n]"; + QTest::newRow("LW:EmptyMap") + << QCborValue(QCborMap()) << int(QCborValue::LineWrapped) + << "{\n}"; + QTest::newRow("LW:Array:Integer:0") + << QCborValue(QCborArray{0}) << int(QCborValue::LineWrapped) + << "[\n 0\n]"; + QTest::newRow("LW:Array:String:5") + << QCborValue(QCborArray{"Hello"}) << int(QCborValue::LineWrapped) + << "[\n \"Hello\"\n]"; + QTest::newRow("LW:Map:0-0") + << QCborValue(QCborMap{{0, 0}}) << int(QCborValue::LineWrapped) + << "{\n 0: 0\n}"; + QTest::newRow("LW:Map:String:5") + << QCborValue(QCborMap{{0, "Hello"}}) << int(QCborValue::LineWrapped) + << "{\n 0: \"Hello\"\n}"; + QTest::newRow("LW:2Array") + << QCborValue(QCborArray{0, 1}) << int(QCborValue::LineWrapped) + << "[\n 0,\n 1\n]"; + QTest::newRow("LW:2Map") + << QCborValue(QCborMap{{0, 0}, {"foo", "bar"}}) << int(QCborValue::LineWrapped) + << "{\n 0: 0,\n \"foo\": \"bar\"\n}"; + + // nested arrays and maps + QTest::newRow("Array:EmptyArray") + << QCborValue(QCborArray() << QCborArray()) << int(QCborValue::DiagnosticNotationOptions{}) + << "[[]]"; + QTest::newRow("Array:EmptyMap") + << QCborValue(QCborArray() << QCborMap()) << int(QCborValue::DiagnosticNotationOptions{}) + << "[{}]"; + QTest::newRow("LW:Array:EmptyArray") + << QCborValue(QCborArray() << QCborArray()) << int(QCborValue::LineWrapped) + << "[\n [\n ]\n]"; + QTest::newRow("LW:Array:EmptyMap") + << QCborValue(QCborArray() << QCborMap()) << int(QCborValue::LineWrapped) + << "[\n {\n }\n]"; + QTest::newRow("LW:Array:2Array") + << QCborValue(QCborArray() << QCborArray{0, 1}) << int(QCborValue::LineWrapped) + << "[\n [\n 0,\n 1\n ]\n]"; + QTest::newRow("LW:Map:2Array") + << QCborValue(QCborMap{{0, QCborArray{0, 1}}}) << int(QCborValue::LineWrapped) + << "{\n 0: [\n 0,\n 1\n ]\n}"; + QTest::newRow("LW:Map:2Map") + << QCborValue(QCborMap{{-1, QCborMap{{0, 0}, {"foo", "bar"}}}}) << int(QCborValue::LineWrapped) + << "{\n -1: {\n 0: 0,\n \"foo\": \"bar\"\n }\n}"; + + // string escaping + QTest::newRow("String:escaping") + << QCborValue("\1\a\b\t\f\r\n\v\x1f\x7f \"\xc2\xa0\xe2\x82\xac\xf0\x90\x80\x80\\\"") + << int(QCborValue::DiagnosticNotationOptions{}) + << "\"\\u0001\\a\\b\\t\\f\\r\\n\\v\\u001F\\u007F \\\"\\u00A0\\u20AC\\U00010000\\\\\\\"\""; + + // extended formatting for byte arrays + QTest::newRow("Extended:ByteArray:0") + << QCborValue(QByteArray()) << int(QCborValue::ExtendedFormat) + << "h''"; + QTest::newRow("Extended:ByteArray:5") + << QCborValue(QByteArray("Hello")) << int(QCborValue::ExtendedFormat) + << "h'48 65 6c 6c 6f'"; + QTest::newRow("Extended:ByteArray:Base64url") + << QCborValue(QCborKnownTags::ExpectedBase64url, QByteArray("\xff\xef")) + << int(QCborValue::ExtendedFormat) << "21(b64'_-8')"; + QTest::newRow("Extended:ByteArray:Base64") + << QCborValue(QCborKnownTags::ExpectedBase64, QByteArray("\xff\xef")) + << int(QCborValue::ExtendedFormat) << "22(b64'/+8=')"; + + // formatting applies through arrays too + QTest::newRow("Extended:Array:ByteArray:Base64url") + << QCborValue(QCborKnownTags::ExpectedBase64url, QCborArray{QByteArray("\xff\xef")}) + << int(QCborValue::ExtendedFormat) << "21([b64'_-8'])"; + // and only the innermost applies + QTest::newRow("ByteArray:multiple-tags") + << QCborValue(QCborKnownTags::ExpectedBase64url, + QCborArray{QCborValue(QCborKnownTags::ExpectedBase16, QByteArray("Hello")), + QByteArray("\xff\xef")}) + << int(QCborValue::ExtendedFormat) << "21([23(h'48 65 6c 6c 6f'), b64'_-8'])"; +} + +void tst_QCborValue::toDiagnosticNotation() +{ + QFETCH(QCborValue, v); + QFETCH(QString, expected); + QFETCH(int, opts); + + QString result = v.toDiagnosticNotation(QCborValue::DiagnosticNotationOptions(opts)); + QCOMPARE(result, expected); +} + +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..56245a7173 --- /dev/null +++ b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** 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); + if (variant.isValid()) { + QVariant variant2 = QVariant::fromValue(v); + QVERIFY(variant2.canConvert(variant.userType())); + QVERIFY(variant2.convert(variant.userType())); + QCOMPARE(variant2, 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); + QCOMPARE(QVariant::fromValue(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); + QCOMPARE(variant.value<QCborValue>(), 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(QVariant(json).value<QCborValue>(), 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..011a0e1a85 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; @@ -2211,25 +2211,22 @@ void tst_QDataStream::setVersion() } } -class SequentialBuffer : public QBuffer +class SequentialBuffer : public QIODevice { public: - SequentialBuffer(QByteArray *data) : QBuffer(data) { offset = 0; } + SequentialBuffer(QByteArray *data) : QIODevice() { buf.setBuffer(data); } - bool isSequential() const { return true; } - bool seek(qint64 pos) { offset = pos; return QBuffer::seek(pos); } - qint64 pos() const { return qint64(offset); } + bool isSequential() const override { return true; } + bool open(OpenMode mode) override { return buf.open(mode) && QIODevice::open(mode | QIODevice::Unbuffered); } + void close() override { buf.close(); QIODevice::close(); } + qint64 bytesAvailable() const override { return QIODevice::bytesAvailable() + buf.bytesAvailable(); } protected: - qint64 readData(char *data, qint64 maxSize) - { - qint64 ret = QBuffer::readData(data, maxSize); - offset += ret; - return ret; - } + qint64 readData(char *data, qint64 maxSize) override { return buf.read(data, maxSize); } + qint64 writeData(const char *data, qint64 maxSize) override { return buf.write(data, maxSize); } private: - int offset; + QBuffer buf; }; void tst_QDataStream::skipRawData_data() @@ -3329,15 +3326,21 @@ void tst_QDataStream::transaction_data() QTest::addColumn<bool>("bData"); QTest::addColumn<float>("fData"); QTest::addColumn<double>("dData"); + QTest::addColumn<QImage>("imgData"); QTest::addColumn<QByteArray>("strData"); QTest::addColumn<QByteArray>("rawData"); + QImage img1(open_xpm); + QImage img2; + QImage img3(50, 50, QImage::Format_ARGB32); + img3.fill(qRgba(12, 34, 56, 78)); + QTest::newRow("1") << qint8(1) << qint16(2) << qint32(3) << qint64(4) << true << 5.0f - << double(6.0) << QByteArray("Hello world!") << QByteArray("Qt rocks!"); + << double(6.0) << img1 << QByteArray("Hello world!") << QByteArray("Qt rocks!"); QTest::newRow("2") << qint8(1 << 6) << qint16(1 << 14) << qint32(1 << 30) << qint64Data(3) << false << 123.0f - << double(234.0) << stringData(5).toUtf8() << stringData(6).toUtf8(); + << double(234.0) << img2 << stringData(5).toUtf8() << stringData(6).toUtf8(); QTest::newRow("3") << qint8(-1) << qint16(-2) << qint32(-3) << qint64(-4) << true << -123.0f - << double(-234.0) << stringData(3).toUtf8() << stringData(4).toUtf8(); + << double(-234.0) << img3 << stringData(3).toUtf8() << stringData(4).toUtf8(); } void tst_QDataStream::transaction() @@ -3351,6 +3354,7 @@ void tst_QDataStream::transaction() QFETCH(bool, bData); QFETCH(float, fData); QFETCH(double, dData); + QFETCH(QImage, imgData); QFETCH(QByteArray, strData); QFETCH(QByteArray, rawData); @@ -3358,12 +3362,13 @@ void tst_QDataStream::transaction() QDataStream stream(&testBuffer, QIODevice::WriteOnly); stream << i8Data << i16Data << i32Data << i64Data - << bData << fData << dData << strData.constData(); + << bData << fData << dData << imgData << strData.constData(); stream.writeRawData(rawData.constData(), rawData.size()); } for (int splitPos = 0; splitPos <= testBuffer.size(); ++splitPos) { QByteArray readBuffer(testBuffer.left(splitPos)); + SequentialBuffer dev(&readBuffer); dev.open(QIODevice::ReadOnly); QDataStream stream(&dev); @@ -3375,12 +3380,13 @@ void tst_QDataStream::transaction() bool b; float f; double d; + QImage img; char *str; QByteArray raw(rawData.size(), 0); forever { stream.startTransaction(); - stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> str; + stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> img >> str; stream.readRawData(raw.data(), raw.size()); if (stream.commitTransaction()) @@ -3402,6 +3408,7 @@ void tst_QDataStream::transaction() QCOMPARE(b, bData); QCOMPARE(f, fData); QCOMPARE(d, dData); + QCOMPARE(img, imgData); QVERIFY(strData == str); delete [] str; QCOMPARE(raw, rawData); diff --git a/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp b/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp index df8746e518..edea4713a1 100644 --- a/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp +++ b/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp @@ -161,6 +161,7 @@ private slots: void string_write_operator_ToDevice(); void latin1String_write_operator_ToDevice(); void stringref_write_operator_ToDevice(); + void stringview_write_operator_ToDevice(); // other void skipWhiteSpace_data(); @@ -2573,6 +2574,17 @@ void tst_QTextStream::stringref_write_operator_ToDevice() QCOMPARE(buf.buffer().constData(), "No explicit lengthExplicit length"); } +void tst_QTextStream::stringview_write_operator_ToDevice() +{ + QBuffer buf; + buf.open(QBuffer::WriteOnly); + QTextStream stream(&buf); + const QStringView expected = QStringViewLiteral("expectedStringView"); + stream << expected; + stream.flush(); + QCOMPARE(buf.buffer().constData(), "expectedStringView"); +} + // ------------------------------------------------------------------------------ void tst_QTextStream::useCase1() { diff --git a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp index 16a4200b5d..8fdf91b090 100644 --- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp @@ -577,6 +577,8 @@ private slots: void invalidStringCharacters() const; void hasError() const; void readBack() const; + void roundTrip() const; + void roundTrip_data() const; private: static QByteArray readFile(const QString &filename); @@ -1741,5 +1743,35 @@ void tst_QXmlStream::readBack() const } } +void tst_QXmlStream::roundTrip_data() const +{ + QTest::addColumn<QString>("in"); + + QTest::newRow("QTBUG-63434") << + "<?xml version=\"1.0\"?>" + "<root>" + "<father>" + "<child xmlns:unknown=\"http://mydomain\">Text</child>" + "</father>" + "</root>\n"; +} + +void tst_QXmlStream::roundTrip() const +{ + QFETCH(QString, in); + QString out; + + QXmlStreamReader reader(in); + QXmlStreamWriter writer(&out); + + while (!reader.atEnd()) { + reader.readNext(); + QVERIFY(!reader.hasError()); + writer.writeCurrentToken(reader); + QVERIFY(!writer.hasError()); + } + QCOMPARE(out, in); +} + #include "tst_qxmlstream.moc" // vim: et:ts=4:sw=4:sts=4 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/qthreadonce/qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp index 9179750218..d27884197a 100644 --- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp @@ -30,7 +30,6 @@ #include "qplatformdefs.h" #include "qthreadonce.h" -#ifndef QT_NO_THREAD #include "qmutex.h" Q_GLOBAL_STATIC_WITH_ARGS(QMutex, onceInitializationMutex, (QMutex::Recursive)) @@ -104,5 +103,3 @@ void QOnceControl::done() { extra &= ~MustRunCode; } - -#endif // QT_NO_THREAD diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h index 71e830ca16..e5918b8fa5 100644 --- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h @@ -34,8 +34,6 @@ #include <QtCore/qatomic.h> -#ifndef QT_NO_THREAD - class QOnceControl { public: @@ -91,6 +89,4 @@ public: inline operator T*() { return value(); } }; -#endif // QT_NO_THREAD - #endif 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/thread/thread.pro b/tests/auto/corelib/thread/thread.pro index d3c669859b..90b8d6806e 100644 --- a/tests/auto/corelib/thread/thread.pro +++ b/tests/auto/corelib/thread/thread.pro @@ -1,22 +1,25 @@ TEMPLATE=subdirs -SUBDIRS=\ - qatomicint \ - qatomicinteger \ - qatomicpointer \ - qresultstore \ - qfuture \ - qfuturesynchronizer \ - qmutex \ - qmutexlocker \ - qreadlocker \ - qreadwritelock \ - qsemaphore \ - qthread \ - qthreadonce \ - qthreadpool \ - qthreadstorage \ - qwaitcondition \ - qwritelocker + +qtConfig(thread) { + SUBDIRS=\ + qatomicint \ + qatomicinteger \ + qatomicpointer \ + qresultstore \ + qfuture \ + qfuturesynchronizer \ + qmutex \ + qmutexlocker \ + qreadlocker \ + qreadwritelock \ + qsemaphore \ + qthread \ + qthreadonce \ + qthreadpool \ + qthreadstorage \ + qwaitcondition \ + qwritelocker +} qtHaveModule(concurrent) { SUBDIRS += \ 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..1ed41793dc 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -106,6 +106,8 @@ private slots: void number(); void toInt_data(); void toInt(); + void toDouble_data(); + void toDouble(); void blockSizeCalculations(); void resizeAfterFromRawData(); @@ -141,6 +143,8 @@ private slots: #endif void toUpperLower_data(); void toUpperLower(); + void isUpper(); + void isLower(); void macTypes(); @@ -856,15 +860,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 +901,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() @@ -1289,6 +1319,11 @@ void tst_QByteArray::toInt_data() QTest::newRow("base 0-3") << QByteArray("010") << 0 << int(8) << true; QTest::newRow("empty") << QByteArray() << 0 << int(0) << false; + QTest::newRow("leading space") << QByteArray(" 100") << 10 << int(100) << true; + QTest::newRow("trailing space") << QByteArray("100 ") << 10 << int(100) << true; + QTest::newRow("leading junk") << QByteArray("x100") << 10 << int(0) << false; + QTest::newRow("trailing junk") << QByteArray("100x") << 10 << int(0) << false; + // using fromRawData QTest::newRow("raw1") << QByteArray::fromRawData("1", 1) << 10 << 1 << true; QTest::newRow("raw2") << QByteArray::fromRawData("1foo", 1) << 10 << 1 << true; @@ -1313,6 +1348,34 @@ void tst_QByteArray::toInt() QCOMPARE( number, expectednumber ); } +void tst_QByteArray::toDouble_data() +{ + QTest::addColumn<QByteArray>("string"); + QTest::addColumn<double>("expectedNumber"); + QTest::addColumn<bool>("expectedOk"); + + QTest::newRow("decimal") << QByteArray("1.2345") << 1.2345 << true; + QTest::newRow("exponent lowercase") << QByteArray("1.2345e+01") << 12.345 << true; + QTest::newRow("exponent uppercase") << QByteArray("1.2345E+02") << 123.45 << true; + QTest::newRow("leading spaces") << QByteArray(" \n\r\t1.2345") << 1.2345 << true; + QTest::newRow("trailing spaces") << QByteArray("1.2345 \n\r\t") << 1.2345 << true; + QTest::newRow("leading junk") << QByteArray("x1.2345") << 0.0 << false; + QTest::newRow("trailing junk") << QByteArray("1.2345x") << 0.0 << false; +} + +void tst_QByteArray::toDouble() +{ + QFETCH(QByteArray, string); + QFETCH(double, expectedNumber); + QFETCH(bool, expectedOk); + + bool ok; + const double number = string.toDouble(&ok); + + QCOMPARE(ok, expectedOk); + QCOMPARE(number, expectedNumber); +} + void tst_QByteArray::toULong_data() { QTest::addColumn<QByteArray>("str"); @@ -1326,6 +1389,11 @@ void tst_QByteArray::toULong_data() QTest::newRow("empty") << QByteArray("") << 10 << 0UL << false; QTest::newRow("ulong1") << QByteArray("3234567890") << 10 << 3234567890UL << true; QTest::newRow("ulong2") << QByteArray("fFFfFfFf") << 16 << 0xFFFFFFFFUL << true; + + QTest::newRow("leading spaces") << QByteArray(" \n\r\t100") << 10 << 100UL << true; + QTest::newRow("trailing spaces") << QByteArray("100 \n\r\t") << 10 << 100UL << true; + QTest::newRow("leading junk") << QByteArray("x100") << 10 << 0UL << false; + QTest::newRow("trailing junk") << QByteArray("100x") << 10 << 0UL << false; } void tst_QByteArray::toULong() @@ -1351,6 +1419,10 @@ void tst_QByteArray::toULongLong_data() QTest::newRow("default") << QByteArray() << 10 << (qulonglong)0 << false; QTest::newRow("out of base bound") << QByteArray("c") << 10 << (qulonglong)0 << false; + QTest::newRow("leading spaces") << QByteArray(" \n\r\t100") << 10 << qulonglong(100) << true; + QTest::newRow("trailing spaces") << QByteArray("100 \n\r\t") << 10 << qulonglong(100) << true; + QTest::newRow("leading junk") << QByteArray("x100") << 10 << qulonglong(0) << false; + QTest::newRow("trailing junk") << QByteArray("100x") << 10 << qulonglong(0) << false; } void tst_QByteArray::toULongLong() @@ -1757,6 +1829,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 +2266,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/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp index 4b362a9825..62c29229e1 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp +++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp @@ -100,7 +100,7 @@ void tst_QCommandLineParser::testInvalidOptions() QCoreApplication app(empty_argc, empty_argv); QCommandLineParser parser; QTest::ignoreMessage(QtWarningMsg, "QCommandLineOption: Option names cannot start with a '-'"); - parser.addOption(QCommandLineOption(QStringLiteral("-v"), QStringLiteral("Displays version information."))); + QVERIFY(!parser.addOption(QCommandLineOption(QStringLiteral("-v"), QStringLiteral("Displays version information.")))); } void tst_QCommandLineParser::testPositionalArguments() @@ -336,7 +336,7 @@ void tst_QCommandLineParser::testDoubleDash() QCoreApplication app(empty_argc, empty_argv); QCommandLineParser parser; - parser.addOption(QCommandLineOption(QStringList() << "o" << "output", QStringLiteral("Output file"), QStringLiteral("filename"))); + QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "o" << "output", QStringLiteral("Output file"), QStringLiteral("filename")))); parser.setSingleDashWordOptionMode(parsingMode); QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--output" << "foo")); QCOMPARE(parser.value("output"), QString("foo")); @@ -382,7 +382,7 @@ void tst_QCommandLineParser::testMissingOptionValue() { QCoreApplication app(empty_argc, empty_argv); QCommandLineParser parser; - parser.addOption(QCommandLineOption(QStringLiteral("option"), QStringLiteral("An option"), "value")); + QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("option"), QStringLiteral("An option"), "value"))); QVERIFY(!parser.parse(QStringList() << "argv0" << "--option")); // the user forgot to pass a value for --option QCOMPARE(parser.value("option"), QString()); QCOMPARE(parser.errorText(), QString("Missing value after '--option'.")); @@ -400,8 +400,8 @@ void tst_QCommandLineParser::testStdinArgument() QCoreApplication app(empty_argc, empty_argv); QCommandLineParser parser; parser.setSingleDashWordOptionMode(parsingMode); - parser.addOption(QCommandLineOption(QStringList() << "i" << "input", QStringLiteral("Input file."), QStringLiteral("filename"))); - parser.addOption(QCommandLineOption("b", QStringLiteral("Boolean option."))); + QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "i" << "input", QStringLiteral("Input file."), QStringLiteral("filename")))); + QVERIFY(parser.addOption(QCommandLineOption("b", QStringLiteral("Boolean option.")))); QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-")); QCOMPARE(parser.value("input"), QString("-")); QCOMPARE(parser.positionalArguments(), QStringList()); @@ -471,14 +471,14 @@ void tst_QCommandLineParser::testSingleDashWordOptionModes() QCoreApplication app(empty_argc, empty_argv); QCommandLineParser parser; parser.setSingleDashWordOptionMode(parsingMode); - parser.addOption(QCommandLineOption("a", QStringLiteral("a option."))); - parser.addOption(QCommandLineOption("b", QStringLiteral("b option."))); - parser.addOption(QCommandLineOption(QStringList() << "c" << "abc", QStringLiteral("c option."), QStringLiteral("value"))); - parser.addOption(QCommandLineOption("nn", QStringLiteral("nn option."))); + QVERIFY(parser.addOption(QCommandLineOption("a", QStringLiteral("a option.")))); + QVERIFY(parser.addOption(QCommandLineOption("b", QStringLiteral("b option.")))); + QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "c" << "abc", QStringLiteral("c option."), QStringLiteral("value")))); + QVERIFY(parser.addOption(QCommandLineOption("nn", QStringLiteral("nn option.")))); QCommandLineOption forceShort(QStringLiteral("I"), QStringLiteral("always short option"), QStringLiteral("path"), QStringLiteral("default")); forceShort.setFlags(QCommandLineOption::ShortOptionStyle); - parser.addOption(forceShort); + QVERIFY(parser.addOption(forceShort)); QVERIFY(parser.parse(commandLine)); QCOMPARE(parser.optionNames(), expectedOptionNames); for (int i = 0; i < expectedOptionValues.count(); ++i) @@ -493,11 +493,11 @@ void tst_QCommandLineParser::testCpp11StyleInitialization() QCommandLineParser parser; // primarily check that this compiles: - parser.addOptions({ + QVERIFY(parser.addOptions({ { "a", "The A option." }, { { "v", "verbose" }, "The verbose option." }, { { "i", "infile" }, "The input file.", "value" }, - }); + })); // but do a very basic functionality test, too: QVERIFY(parser.parse({"tst_QCommandLineParser", "-a", "-vvv", "--infile=in.txt"})); QCOMPARE(parser.optionNames(), (QStringList{"a", "v", "v", "v", "infile"})); 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/qeasingcurve/tst_qeasingcurve.cpp b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp index 79309f960d..0196dd2d23 100644 --- a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp +++ b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp @@ -423,7 +423,12 @@ void tst_QEasingCurve::setCustomType() QCOMPARE(curve.valueForProgress(0.15), 0.1); QCOMPARE(curve.valueForProgress(0.20), 0.2); QCOMPARE(curve.valueForProgress(0.25), 0.2); + // QTBUG-69947, MinGW 7.3 returns 0.2 +#if defined(Q_CC_MINGW) +#if !defined(__GNUC__) || __GNUC__ != 7 || __GNUC_MINOR__ < 3 QCOMPARE(curve.valueForProgress(0.30), 0.3); +#endif +#endif QCOMPARE(curve.valueForProgress(0.35), 0.3); QCOMPARE(curve.valueForProgress(0.999999), 0.9); 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/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index b7cb8a1bdc..261689d401 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -78,6 +78,7 @@ private slots: #endif void ctor(); + void emptyCtor_data(); void emptyCtor(); void consistentC(); void matchingLocales(); @@ -118,8 +119,10 @@ private slots: void monthName(); void standaloneMonthName(); - void defaultNumeringSystem(); + void defaultNumberingSystem_data(); + void defaultNumberingSystem(); + void ampm_data(); void ampm(); void currency(); void quoteString(); @@ -127,6 +130,7 @@ private slots: void weekendDays(); void listPatterns(); + void measurementSystems_data(); void measurementSystems(); void QTBUG_26035_positivesign(); @@ -135,6 +139,7 @@ private slots: void formattedDataSize_data(); void formattedDataSize(); + void bcp47Name_data(); void bcp47Name(); void systemLocale_data(); @@ -145,6 +150,7 @@ private slots: // QLocale::setDefault() *must* appear *after* all other tests ! void defaulted_ctor(); // This one must be the first of these. void legacyNames(); + void unixLocaleName_data(); void unixLocaleName(); void testNames_data(); void testNames(); @@ -152,6 +158,7 @@ private slots: private: QString m_decimal, m_thousand, m_sdate, m_ldate, m_time; QString m_sysapp; + QStringList cleanEnv; bool europeanTimeZone; }; @@ -184,6 +191,14 @@ void tst_QLocale::initTestCase() QVERIFY2(fi.exists() && fi.isExecutable(), qPrintable(QDir::toNativeSeparators(m_sysapp) + QStringLiteral(" does not exist or is not executable."))); + + // Get an environment free of any locale-related variables + cleanEnv.clear(); + foreach (QString const& entry, QProcess::systemEnvironment()) { + if (entry.startsWith("LANG=") || entry.startsWith("LC_") || entry.startsWith("LANGUAGE=")) + continue; + cleanEnv << entry; + } #endif // QT_CONFIG(process) } @@ -214,32 +229,48 @@ void tst_QLocale::ctor() } // Exact matches - TEST_CTOR(Chinese, SimplifiedHanScript, China, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); - TEST_CTOR(Chinese, TraditionalHanScript, Taiwan, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); - TEST_CTOR(Chinese, TraditionalHanScript, HongKong, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::HongKong); + TEST_CTOR(Chinese, SimplifiedHanScript, China, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, TraditionalHanScript, Taiwan, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); + TEST_CTOR(Chinese, TraditionalHanScript, HongKong, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::HongKong); // Best match for AnyCountry - TEST_CTOR(Chinese, SimplifiedHanScript, AnyCountry, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); - TEST_CTOR(Chinese, TraditionalHanScript, AnyCountry, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); + TEST_CTOR(Chinese, SimplifiedHanScript, AnyCountry, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, TraditionalHanScript, AnyCountry, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); // Best match for AnyScript (and change country to supported one, if necessary) - TEST_CTOR(Chinese, AnyScript, China, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); - TEST_CTOR(Chinese, AnyScript, Taiwan, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); - TEST_CTOR(Chinese, AnyScript, HongKong, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::HongKong); - TEST_CTOR(Chinese, AnyScript, UnitedStates, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, AnyScript, China, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, AnyScript, Taiwan, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); + TEST_CTOR(Chinese, AnyScript, HongKong, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::HongKong); + TEST_CTOR(Chinese, AnyScript, UnitedStates, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); // Fully-specified not found; find best alternate country - TEST_CTOR(Chinese, SimplifiedHanScript, Taiwan, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); - TEST_CTOR(Chinese, SimplifiedHanScript, UnitedStates, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); - TEST_CTOR(Chinese, TraditionalHanScript, China, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); - TEST_CTOR(Chinese, TraditionalHanScript, UnitedStates, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); + TEST_CTOR(Chinese, SimplifiedHanScript, Taiwan, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, SimplifiedHanScript, UnitedStates, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, TraditionalHanScript, China, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); + TEST_CTOR(Chinese, TraditionalHanScript, UnitedStates, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); // Fully-specified not found; find best alternate script - TEST_CTOR(Chinese, LatinScript, China, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); - TEST_CTOR(Chinese, LatinScript, Taiwan, QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); + TEST_CTOR(Chinese, LatinScript, China, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, LatinScript, Taiwan, + QLocale::Chinese, QLocale::TraditionalHanScript, QLocale::Taiwan); // Fully-specified not found; find best alternate country and script - TEST_CTOR(Chinese, LatinScript, UnitedStates, QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); + TEST_CTOR(Chinese, LatinScript, UnitedStates, + QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China); #undef TEST_CTOR } @@ -278,7 +309,8 @@ void tst_QLocale::defaulted_ctor() TEST_CTOR(French, France, QLocale::French, QLocale::France) TEST_CTOR(C, France, QLocale::C, QLocale::AnyCountry) - TEST_CTOR(Spanish, LatinAmericaAndTheCaribbean, QLocale::Spanish, QLocale::LatinAmericaAndTheCaribbean) + TEST_CTOR(Spanish, LatinAmerica, QLocale::Spanish, + QLocale::LatinAmerica) QLocale::setDefault(QLocale(QLocale::English, QLocale::France)); @@ -370,8 +402,9 @@ void tst_QLocale::defaulted_ctor() QVERIFY2(l.language() == QLocale::exp_lang \ && l.country() == QLocale::exp_country, \ QString("requested: \"" + QString(req_lc) + "\", got: " \ - + QLocale::languageToString(l.language()) \ - + QLatin1Char('/') + QLocale::countryToString(l.country())).toLatin1().constData()); \ + + QLocale::languageToString(l.language()) \ + + QLatin1Char('/') \ + + QLocale::countryToString(l.country())).toLatin1().constData()); \ QCOMPARE(l, QLocale(QLocale::exp_lang, QLocale::exp_country)); \ QCOMPARE(qHash(l), qHash(QLocale(QLocale::exp_lang, QLocale::exp_country))); \ } @@ -405,6 +438,8 @@ void tst_QLocale::defaulted_ctor() TEST_CTOR("en_GB@bla", English, UnitedKingdom) TEST_CTOR("en-GB", English, UnitedKingdom) TEST_CTOR("en-GB@bla", English, UnitedKingdom) + TEST_CTOR("eo", Esperanto, World) + TEST_CTOR("yi", Yiddish, World) QVERIFY(QLocale::Norwegian == QLocale::NorwegianBokmal); TEST_CTOR("no", Norwegian, Norway) @@ -414,8 +449,8 @@ void tst_QLocale::defaulted_ctor() TEST_CTOR("nb_NO", Norwegian, Norway) TEST_CTOR("nn_NO", NorwegianNynorsk, Norway) TEST_CTOR("es_ES", Spanish, Spain) - TEST_CTOR("es_419", Spanish, LatinAmericaAndTheCaribbean) - TEST_CTOR("es-419", Spanish, LatinAmericaAndTheCaribbean) + TEST_CTOR("es_419", Spanish, LatinAmerica) + TEST_CTOR("es-419", Spanish, LatinAmerica) TEST_CTOR("fr_MA", French, Morocco) // test default countries for languages @@ -464,7 +499,8 @@ static inline bool runSysApp(const QString &binary, process.start(binary); process.closeWriteChannel(); if (!process.waitForStarted()) { - *errorMessage = QString::fromLatin1("Cannot start '%1': %2").arg(binary, process.errorString()); + *errorMessage = QLatin1String("Cannot start '") + binary + + QLatin1String("': ") + process.errorString(); return false; } if (!process.waitForFinished()) { @@ -488,94 +524,121 @@ static inline bool runSysAppTest(const QString &binary, return false; if (output.isEmpty()) { - *errorMessage = QString::fromLatin1("Empty output received for requested '%1' (expected '%2')"). - arg(requestedLocale, expectedOutput); + *errorMessage = QLatin1String("Empty output received for requested '") + requestedLocale + + QLatin1String("' (expected '") + expectedOutput + QLatin1String("')"); return false; } if (output != expectedOutput) { - *errorMessage = QString::fromLatin1("Output mismatch for requested '%1': Expected '%2', got '%3'"). - arg(requestedLocale, expectedOutput, output); + *errorMessage = QLatin1String("Output mismatch for requested '") + requestedLocale + + QLatin1String("': Expected '") + expectedOutput + QLatin1String("', got '") + + output + QLatin1String("'"); return false; } return true; } #endif -void tst_QLocale::emptyCtor() +void tst_QLocale::emptyCtor_data() { #if !QT_CONFIG(process) QSKIP("No qprocess support", SkipAll); -#else +#endif #ifdef Q_OS_ANDROID QSKIP("This test crashes on Android"); #endif -#define TEST_CTOR(req_lc, exp_str) \ - { \ - /* Test constructor without arguments. Needs separate process */ \ - /* because of caching of the system locale. */ \ - QString errorMessage; \ - QVERIFY2(runSysAppTest(m_sysapp, env, QLatin1String(req_lc), QLatin1String(exp_str), &errorMessage), \ - qPrintable(errorMessage)); \ - } - // Get an environment free of any locale-related variables - QStringList env; - foreach (QString const& entry, QProcess::systemEnvironment()) { - if (entry.startsWith("LANG=") || entry.startsWith("LC_") || entry.startsWith("LANGUAGE=")) - continue; - env << entry; - } + QTest::addColumn<QString>("expected"); + +#define ADD_CTOR_TEST(give, expect) QTest::newRow(give) << QStringLiteral(expect); + + // For format and meaning, see: + // http://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html + // Note that the accepted values for fields are implementation-dependent; + // the template is language[_territory][.codeset][@modifier] + + // Vanilla: + ADD_CTOR_TEST("C", "C"); + + // Standard forms: + ADD_CTOR_TEST("en", "en_US"); + ADD_CTOR_TEST("en_GB", "en_GB"); + ADD_CTOR_TEST("de", "de_DE"); + // Norsk has some quirks: + ADD_CTOR_TEST("no", "nb_NO"); + ADD_CTOR_TEST("nb", "nb_NO"); + ADD_CTOR_TEST("nn", "nn_NO"); + ADD_CTOR_TEST("no_NO", "nb_NO"); + ADD_CTOR_TEST("nb_NO", "nb_NO"); + ADD_CTOR_TEST("nn_NO", "nn_NO"); + + // Not too fussy about case: + ADD_CTOR_TEST("DE", "de_DE"); + ADD_CTOR_TEST("EN", "en_US"); + + // Invalid fields + ADD_CTOR_TEST("bla", "C"); + ADD_CTOR_TEST("zz", "C"); + ADD_CTOR_TEST("zz_zz", "C"); + ADD_CTOR_TEST("zz...", "C"); + ADD_CTOR_TEST("en.bla", "en_US"); +#if !(defined(Q_OS_DARWIN) && QT_HAS_FEATURE(address_sanitizer)) + // See QTBUG-69875 + ADD_CTOR_TEST("en@bla", "en_US"); +#endif + ADD_CTOR_TEST("en_blaaa", "en_US"); + ADD_CTOR_TEST("en_zz", "en_US"); + ADD_CTOR_TEST("en_GB.bla", "en_GB"); + ADD_CTOR_TEST("en_GB@.bla", "en_GB"); + ADD_CTOR_TEST("en_GB@bla", "en_GB"); + + // Empty optional fields, but with punctuators supplied + ADD_CTOR_TEST("en.", "en_US"); +#if !(defined(Q_OS_DARWIN) && QT_HAS_FEATURE(address_sanitizer)) + // See QTBUG-69875 + ADD_CTOR_TEST("en@", "en_US"); +#endif + ADD_CTOR_TEST("en.@", "en_US"); + ADD_CTOR_TEST("en_", "en_US"); + ADD_CTOR_TEST("en_.", "en_US"); + ADD_CTOR_TEST("en_.@", "en_US"); +#undef ADD_CTOR_TEST +#if QT_CONFIG(process) // for runSysApp // Get default locale. QString defaultLoc; QString errorMessage; - QVERIFY2(runSysApp(m_sysapp, env, &defaultLoc, &errorMessage), - qPrintable(errorMessage)); - - TEST_CTOR("C", "C") - TEST_CTOR("bla", "C") - TEST_CTOR("zz", "C") - TEST_CTOR("zz_zz", "C") - TEST_CTOR("zz...", "C") - TEST_CTOR("en", "en_US") - TEST_CTOR("en", "en_US") - TEST_CTOR("en.", "en_US") - TEST_CTOR("en@", "en_US") - TEST_CTOR("en.@", "en_US") - TEST_CTOR("en_", "en_US") - TEST_CTOR("en_.", "en_US") - TEST_CTOR("en_.@", "en_US") - TEST_CTOR("en.bla", "en_US") - TEST_CTOR("en@bla", "en_US") - TEST_CTOR("en_blaaa", "en_US") - TEST_CTOR("en_zz", "en_US") - TEST_CTOR("en_GB", "en_GB") - TEST_CTOR("en_GB.bla", "en_GB") - TEST_CTOR("en_GB@.bla", "en_GB") - TEST_CTOR("en_GB@bla", "en_GB") - TEST_CTOR("de", "de_DE") - - QVERIFY(QLocale::Norwegian == QLocale::NorwegianBokmal); - TEST_CTOR("no", "nb_NO") - TEST_CTOR("nb", "nb_NO") - TEST_CTOR("nn", "nn_NO") - TEST_CTOR("no_NO", "nb_NO") - TEST_CTOR("nb_NO", "nb_NO") - TEST_CTOR("nn_NO", "nn_NO") + if (runSysApp(m_sysapp, cleanEnv, &defaultLoc, &errorMessage)) { +#define ADD_CTOR_TEST(give) QTest::newRow(give) << defaultLoc; + ADD_CTOR_TEST("en/"); + ADD_CTOR_TEST("asdfghj"); + ADD_CTOR_TEST("123456"); +#undef ADD_CTOR_TEST + } else { + qDebug() << "Skipping tests based on default locale" << qPrintable(errorMessage); + } +#endif // process +} - TEST_CTOR("DE", "de_DE"); - TEST_CTOR("EN", "en_US"); +void tst_QLocale::emptyCtor() +{ +#if QT_CONFIG(process) // for runSysAppTest + QLatin1String request(QTest::currentDataTag()); + QFETCH(QString, expected); - TEST_CTOR("en/", defaultLoc.toLatin1()) - TEST_CTOR("asdfghj", defaultLoc.toLatin1()); - TEST_CTOR("123456", defaultLoc.toLatin1()); + // Test constructor without arguments (see syslocaleapp/syslocaleapp.cpp) + // Needs separate process because of caching of the system locale. + QString errorMessage; + QVERIFY2(runSysAppTest(m_sysapp, cleanEnv, request, expected, &errorMessage), + qPrintable(errorMessage)); -#undef TEST_CTOR -#endif +#else + // This won't be called, as _data() skipped out early. +#endif // process } void tst_QLocale::legacyNames() { + QVERIFY(QLocale::Norwegian == QLocale::NorwegianBokmal); QLocale::setDefault(QLocale(QLocale::C)); #define TEST_CTOR(req_lang, req_country, exp_lang, exp_country) \ @@ -598,8 +661,9 @@ void tst_QLocale::legacyNames() QVERIFY2(l.language() == QLocale::exp_lang \ && l.country() == QLocale::exp_country, \ QString("requested: \"" + QString(req_lc) + "\", got: " \ - + QLocale::languageToString(l.language()) \ - + QLatin1Char('/') + QLocale::countryToString(l.country())).toLatin1().constData()); \ + + QLocale::languageToString(l.language()) \ + + QLatin1Char('/') \ + + QLocale::countryToString(l.country())).toLatin1().constData()); \ } TEST_CTOR("mo_MD", Romanian, Moldova) @@ -649,22 +713,32 @@ void tst_QLocale::matchingLocales() QVERIFY(locales.contains(ru_RU)); } +void tst_QLocale::unixLocaleName_data() +{ + QTest::addColumn<QLocale::Language>("lang"); + QTest::addColumn<QLocale::Country>("land"); + QTest::addColumn<QString>("expect"); + +#define ADDROW(nom, lang, land, name) \ + QTest::newRow(nom) << QLocale::lang << QLocale::land << QStringLiteral(name) + + ADDROW("C_any", C, AnyCountry, "C"); + ADDROW("en_any", English, AnyCountry, "en_US"); + ADDROW("en_GB", English, UnitedKingdom, "en_GB"); + ADDROW("ay_GB", Aymara, UnitedKingdom, "C"); +#undef ADDROW +} + void tst_QLocale::unixLocaleName() { -#define TEST_NAME(req_lang, req_country, exp_name) \ - { \ - QLocale l(QLocale::req_lang, QLocale::req_country); \ - QCOMPARE(l.name(), QString(exp_name)); \ - } + QFETCH(QLocale::Language, lang); + QFETCH(QLocale::Country, land); + QFETCH(QString, expect); QLocale::setDefault(QLocale(QLocale::C)); - TEST_NAME(C, AnyCountry, "C") - TEST_NAME(English, AnyCountry, "en_US") - TEST_NAME(English, UnitedKingdom, "en_GB") - TEST_NAME(Aymara, UnitedKingdom, "C") - -#undef TEST_NAME + QLocale locale(lang, land); + QCOMPARE(locale.name(), expect); } void tst_QLocale::stringToDouble_data() @@ -1282,7 +1356,8 @@ void tst_QLocale::formatDate_data() QTest::newRow("26") << QDate(1974, 12, 1) << "\"yy\"" << "\"74\""; QTest::newRow("27") << QDate(1974, 12, 1) << "'\"yy\"'" << "\"yy\""; QTest::newRow("28") << QDate() << "'\"yy\"'" << ""; - QTest::newRow("29") << QDate(1974, 12, 1) << "hh:mm:ss.zzz ap d'd'dd/M/yy" << "hh:mm:ss.zzz ap 1d01/12/74"; + QTest::newRow("29") + << QDate(1974, 12, 1) << "hh:mm:ss.zzz ap d'd'dd/M/yy" << "hh:mm:ss.zzz ap 1d01/12/74"; QTest::newRow("dd MMMM yyyy") << QDate(1, 1, 1) << "dd MMMM yyyy" << "01 January 0001"; } @@ -1298,7 +1373,6 @@ void tst_QLocale::formatDate() QCOMPARE(l.toString(date, QStringView(format)), result); } - void tst_QLocale::formatTime_data() { QTest::addColumn<QTime>("time"); @@ -1577,7 +1651,8 @@ void tst_QLocale::formatTimeZone() QDateTime::currentDateTime().timeZoneAbbreviation()); // Time on its own will always be current local time zone - QCOMPARE(enUS.toString(QTime(1, 2, 3), "t"), QDateTime::currentDateTime().timeZoneAbbreviation()); + QCOMPARE(enUS.toString(QTime(1, 2, 3), "t"), + QDateTime::currentDateTime().timeZoneAbbreviation()); } void tst_QLocale::toDateTime_data() @@ -1614,7 +1689,8 @@ void tst_QLocale::toDateTime_data() QTest::newRow("bad-hour-C") << "C" << QDateTime() << "d-MMM-yy hh:m" << "4-Jun-11 1:2"; QTest::newRow("bad-min-C") << "C" << QDateTime() << "d-MMM-yy h:mm" << "4-Jun-11 1:2"; QTest::newRow("bad-sec-C") << "C" << QDateTime() << "d-MMM-yy h:m:ss" << "4-Jun-11 1:2:3"; - QTest::newRow("bad-milli-C") << "C" << QDateTime() << "d-MMM-yy h:m:s.zzz" << "4-Jun-11 1:2:3.4"; + QTest::newRow("bad-milli-C") + << "C" << QDateTime() << "d-MMM-yy h:m:s.zzz" << "4-Jun-11 1:2:3.4"; QTest::newRow("ok-C") << "C" << QDateTime(QDate(1911, 6, 4), QTime(1, 2, 3, 400)) << "d-MMM-yy h:m:s.z" << "4-Jun-11 1:2:3.4"; @@ -1637,11 +1713,13 @@ void tst_QLocale::toDateTime_data() QTest::newRow("12no_NO") << "no_NO" << QDateTime(QDate(1974, 12, 1), QTime(15, 0, 0)) << "d'd'dd/M/yyh" << "1d01/12/7415"; - QTest::newRow("RFC-1123") << "C" << QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30)) - << "ddd, dd MMM yyyy hh:mm:ss 'GMT'" << "Thu, 01 Nov 2007 18:08:30 GMT"; + QTest::newRow("RFC-1123") + << "C" << QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30)) + << "ddd, dd MMM yyyy hh:mm:ss 'GMT'" << "Thu, 01 Nov 2007 18:08:30 GMT"; - QTest::newRow("longFormat") << "en_US" << QDateTime(QDate(2009, 1, 5), QTime(11, 48, 32)) - << "dddd, MMMM d, yyyy h:mm:ss AP " << "Monday, January 5, 2009 11:48:32 AM "; + QTest::newRow("longFormat") + << "en_US" << QDateTime(QDate(2009, 1, 5), QTime(11, 48, 32)) + << "dddd, MMMM d, yyyy h:mm:ss AP " << "Monday, January 5, 2009 11:48:32 AM "; } void tst_QLocale::toDateTime() @@ -1716,36 +1794,40 @@ void tst_QLocale::macDefaultLocale() // To run this test make sure "Curreny" is US Dollar in System Preferences->Language & Region->Advanced. if (locale.currencySymbol() == QString("$")) { - QCOMPARE(locale.toCurrencyString(qulonglong(1234)), systemLocaleFormatNumber(QString("$1,234.00"))); - QCOMPARE(locale.toCurrencyString(double(1234.56)), systemLocaleFormatNumber(QString("$1,234.56"))); + QCOMPARE(locale.toCurrencyString(qulonglong(1234)), + systemLocaleFormatNumber(QString("$1,234.00"))); + QCOMPARE(locale.toCurrencyString(double(1234.56)), + systemLocaleFormatNumber(QString("$1,234.56"))); } // Depending on the configured time zone, the time string might not // contain a GMT specifier. (Sometimes it just names the zone, like "CEST") - if (timeString.contains(QString("GMT"))) { - QString expectedGMTSpecifierBase("GMT"); - if (diff >= 0) - expectedGMTSpecifierBase.append(QLatin1Char('+')); - else - expectedGMTSpecifierBase.append(QLatin1Char('-')); - - QString expectedGMTSpecifier = expectedGMTSpecifierBase + QString("%1").arg(qAbs(diff)); - QString expectedGMTSpecifierZeroExtended = expectedGMTSpecifierBase + QString("0%1").arg(qAbs(diff)); - - QVERIFY2(timeString.contains(expectedGMTSpecifier) - || timeString.contains(expectedGMTSpecifierZeroExtended), - qPrintable(QString("timeString `%1', expectedGMTSpecifier `%2' or `%3'") - .arg(timeString) - .arg(expectedGMTSpecifier) - .arg(expectedGMTSpecifierZeroExtended) - )); + QLatin1String gmt("GMT"); + if (timeString.contains(gmt) && diff) { + QLatin1Char sign(diff < 0 ? '-' : '+'); + QString number(QString::number(qAbs(diff))); + const QString expect = gmt + sign + number; + + if (diff < 10) { + const QString zeroed = gmt + sign + QLatin1Char('0') + number; + + QVERIFY2(timeString.contains(expect) || timeString.contains(zeroed), + qPrintable(QString("timeString `%1', expected GMT specifier `%2' or `%3'") + .arg(timeString).arg(expect).arg(zeroed))); + } else { + QVERIFY2(timeString.contains(expect), + qPrintable(QString("timeString `%1', expected GMT specifier `%2'") + .arg(timeString).arg(expect))); + } } QCOMPARE(locale.dayName(1), QString("Monday")); QCOMPARE(locale.dayName(7), QString("Sunday")); QCOMPARE(locale.monthName(1), QString("January")); QCOMPARE(locale.monthName(12), QString("December")); - QCOMPARE(locale.quoteString("string"), QString::fromUtf8("\xe2\x80\x9c" "string" "\xe2\x80\x9d")); - QCOMPARE(locale.quoteString("string", QLocale::AlternateQuotation), QString::fromUtf8("\xe2\x80\x98" "string" "\xe2\x80\x99")); + QCOMPARE(locale.quoteString("string"), + QString::fromUtf8("\xe2\x80\x9c" "string" "\xe2\x80\x9d")); + QCOMPARE(locale.quoteString("string", QLocale::AlternateQuotation), + QString::fromUtf8("\xe2\x80\x98" "string" "\xe2\x80\x99")); QList<Qt::DayOfWeek> days; days << Qt::Monday << Qt::Tuesday << Qt::Wednesday << Qt::Thursday << Qt::Friday; @@ -1787,12 +1869,6 @@ static void setWinLocaleInfo(LCTYPE type, const QString &value) # define LOCALE_SSHORTTIME 0x00000079 #endif -static inline LCTYPE shortTimeType() -{ - return (QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) && QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7 ? - LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT; -} - class RestoreLocaleHelper { public: RestoreLocaleHelper() { @@ -1800,7 +1876,7 @@ public: m_thousand = getWinLocaleInfo(LOCALE_STHOUSAND); m_sdate = getWinLocaleInfo(LOCALE_SSHORTDATE); m_ldate = getWinLocaleInfo(LOCALE_SLONGDATE); - m_time = getWinLocaleInfo(shortTimeType()); + m_time = getWinLocaleInfo(LOCALE_SSHORTTIME); } ~RestoreLocaleHelper() { @@ -1809,7 +1885,7 @@ public: setWinLocaleInfo(LOCALE_STHOUSAND, m_thousand); setWinLocaleInfo(LOCALE_SSHORTDATE, m_sdate); setWinLocaleInfo(LOCALE_SLONGDATE, m_ldate); - setWinLocaleInfo(shortTimeType(), m_time); + setWinLocaleInfo(LOCALE_SSHORTTIME, m_time); // make sure QLocale::system() gets updated QLocalePrivate::updateSystemPrivate(); @@ -1819,14 +1895,9 @@ public: }; -#endif // Q_OS_WIN - -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - void tst_QLocale::windowsDefaultLocale() { RestoreLocaleHelper systemLocale; - const bool win7OrLater = (QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) && QSysInfo::windowsVersion(); // set weird system defaults and make sure we're using them setWinLocaleInfo(LOCALE_SDECIMAL, QLatin1String("@")); setWinLocaleInfo(LOCALE_STHOUSAND, QLatin1String("?")); @@ -1835,7 +1906,7 @@ void tst_QLocale::windowsDefaultLocale() const QString longDateFormat = QStringLiteral("d@M@yyyy"); setWinLocaleInfo(LOCALE_SLONGDATE, longDateFormat); const QString shortTimeFormat = QStringLiteral("h^m^s"); - setWinLocaleInfo(shortTimeType(), shortTimeFormat); + setWinLocaleInfo(LOCALE_SSHORTTIME, shortTimeFormat); // make sure QLocale::system() gets updated QLocalePrivate::updateSystemPrivate(); @@ -1847,21 +1918,24 @@ void tst_QLocale::windowsDefaultLocale() QCOMPARE(locale.dateFormat(QLocale::ShortFormat), shortDateFormat); QCOMPARE(locale.dateFormat(QLocale::LongFormat), longDateFormat); QCOMPARE(locale.timeFormat(QLocale::ShortFormat), shortTimeFormat); - QCOMPARE(locale.dateTimeFormat(QLocale::ShortFormat), shortDateFormat + QLatin1Char(' ') + shortTimeFormat); - const QString expectedLongDateTimeFormat = longDateFormat + QLatin1Char(' ') - + (win7OrLater ? QStringLiteral("h:mm:ss AP") : shortTimeFormat); + QCOMPARE(locale.dateTimeFormat(QLocale::ShortFormat), + shortDateFormat + QLatin1Char(' ') + shortTimeFormat); + const QString expectedLongDateTimeFormat + = longDateFormat + QLatin1Char(' ') + QStringLiteral("h:mm:ss AP"); QCOMPARE(locale.dateTimeFormat(QLocale::LongFormat), expectedLongDateTimeFormat); // make sure we are using the system to parse them QCOMPARE(locale.toString(1234.56), QString("1?234@56")); QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::ShortFormat), QString("1*12*1974")); - QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::NarrowFormat), locale.toString(QDate(1974, 12, 1), QLocale::ShortFormat)); + QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::NarrowFormat), + locale.toString(QDate(1974, 12, 1), QLocale::ShortFormat)); QCOMPARE(locale.toString(QDate(1974, 12, 1), QLocale::LongFormat), QString("1@12@1974")); const QString expectedFormattedShortTimeSeconds = QStringLiteral("1^2^3"); - const QString expectedFormattedShortTime = win7OrLater ? QStringLiteral("1^2") : expectedFormattedShortTimeSeconds; + const QString expectedFormattedShortTime = QStringLiteral("1^2"); QCOMPARE(locale.toString(QTime(1,2,3), QLocale::ShortFormat), expectedFormattedShortTime); - QCOMPARE(locale.toString(QTime(1,2,3), QLocale::NarrowFormat), locale.toString(QTime(1,2,3), QLocale::ShortFormat)); - const QString expectedFormattedLongTime = win7OrLater ? QStringLiteral("1:02:03 AM") : expectedFormattedShortTimeSeconds; + QCOMPARE(locale.toString(QTime(1,2,3), QLocale::NarrowFormat), + locale.toString(QTime(1,2,3), QLocale::ShortFormat)); + const QString expectedFormattedLongTime = QStringLiteral("1:02:03 AM"); QCOMPARE(locale.toString(QTime(1,2,3), QLocale::LongFormat), expectedFormattedLongTime); QCOMPARE(locale.toString(QDateTime(QDate(1974, 12, 1), QTime(1,2,3)), QLocale::ShortFormat), QStringLiteral("1*12*1974 ") + expectedFormattedShortTime); @@ -1871,7 +1945,7 @@ void tst_QLocale::windowsDefaultLocale() QStringLiteral("1@12@1974 ") + expectedFormattedLongTime); QCOMPARE(locale.toString(QTime(1,2,3), QLocale::LongFormat), expectedFormattedLongTime); } -#endif // #ifdef Q_OS_WIN +#endif // Q_OS_WIN but !Q_OS_WINRT void tst_QLocale::numberOptions() { @@ -2056,17 +2130,23 @@ void tst_QLocale::dayName_data() QTest::addColumn<int>("day"); QTest::addColumn<QLocale::FormatType>("format"); - QTest::newRow("no_NO") << QString("no_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; - QTest::newRow("nb_NO") << QString("nb_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; - QTest::newRow("nn_NO") << QString("nn_NO") << QString("tysdag") << 2 << QLocale::LongFormat; + QTest::newRow("no_NO") << QString("no_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; + QTest::newRow("nb_NO") << QString("nb_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; + QTest::newRow("nn_NO") << QString("nn_NO") << QString("tysdag") << 2 << QLocale::LongFormat; - QTest::newRow("C long") << QString("C") << QString("Sunday") << 7 << QLocale::LongFormat; - QTest::newRow("C short") << QString("C") << QString("Sun") << 7 << QLocale::ShortFormat; - QTest::newRow("C narrow") << QString("C") << QString("7") << 7 << QLocale::NarrowFormat; + QTest::newRow("C long") << QString("C") << QString("Sunday") << 7 << QLocale::LongFormat; + QTest::newRow("C short") << QString("C") << QString("Sun") << 7 << QLocale::ShortFormat; + QTest::newRow("C narrow") << QString("C") << QString("7") << 7 << QLocale::NarrowFormat; - QTest::newRow("ru_RU long") << QString("ru_RU") << QString::fromUtf8("\320\262\320\276\321\201\320\272\321\200\320\265\321\201\320\265\320\275\321\214\320\265") << 7 << QLocale::LongFormat; - QTest::newRow("ru_RU short") << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::ShortFormat; - QTest::newRow("ru_RU narrow") << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::NarrowFormat; + QTest::newRow("ru_RU long") + << QString("ru_RU") + << QString::fromUtf8("\320\262\320\276\321\201\320\272\321\200\320" + "\265\321\201\320\265\320\275\321\214\320\265") + << 7 << QLocale::LongFormat; + QTest::newRow("ru_RU short") + << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::ShortFormat; + QTest::newRow("ru_RU narrow") + << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::NarrowFormat; } void tst_QLocale::dayName() @@ -2096,24 +2176,30 @@ void tst_QLocale::standaloneDayName_data() QTest::addColumn<int>("day"); QTest::addColumn<QLocale::FormatType>("format"); - QTest::newRow("no_NO") << QString("no_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; - QTest::newRow("nb_NO") << QString("nb_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; - QTest::newRow("nn_NO") << QString("nn_NO") << QString("tysdag") << 2 << QLocale::LongFormat; + QTest::newRow("no_NO") << QString("no_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; + QTest::newRow("nb_NO") << QString("nb_NO") << QString("tirsdag") << 2 << QLocale::LongFormat; + QTest::newRow("nn_NO") << QString("nn_NO") << QString("tysdag") << 2 << QLocale::LongFormat; - QTest::newRow("C invalid: 0 long") << QString("C") << QString() << 0 << QLocale::LongFormat; - QTest::newRow("C invalid: 0 short") << QString("C") << QString() << 0 << QLocale::ShortFormat; - QTest::newRow("C invalid: 0 narrow") << QString("C") << QString() << 0 << QLocale::NarrowFormat; - QTest::newRow("C invalid: 8 long") << QString("C") << QString() << 8 << QLocale::LongFormat; - QTest::newRow("C invalid: 8 short") << QString("C") << QString() << 8 << QLocale::ShortFormat; - QTest::newRow("C invalid: 8 narrow") << QString("C") << QString() << 8 << QLocale::NarrowFormat; + QTest::newRow("C invalid: 0 long") << QString("C") << QString() << 0 << QLocale::LongFormat; + QTest::newRow("C invalid: 0 short") << QString("C") << QString() << 0 << QLocale::ShortFormat; + QTest::newRow("C invalid: 0 narrow") << QString("C") << QString() << 0 << QLocale::NarrowFormat; + QTest::newRow("C invalid: 8 long") << QString("C") << QString() << 8 << QLocale::LongFormat; + QTest::newRow("C invalid: 8 short") << QString("C") << QString() << 8 << QLocale::ShortFormat; + QTest::newRow("C invalid: 8 narrow") << QString("C") << QString() << 8 << QLocale::NarrowFormat; - QTest::newRow("C long") << QString("C") << QString("Sunday") << 7 << QLocale::LongFormat; - QTest::newRow("C short") << QString("C") << QString("Sun") << 7 << QLocale::ShortFormat; - QTest::newRow("C narrow") << QString("C") << QString("S") << 7 << QLocale::NarrowFormat; + QTest::newRow("C long") << QString("C") << QString("Sunday") << 7 << QLocale::LongFormat; + QTest::newRow("C short") << QString("C") << QString("Sun") << 7 << QLocale::ShortFormat; + QTest::newRow("C narrow") << QString("C") << QString("S") << 7 << QLocale::NarrowFormat; - QTest::newRow("ru_RU long") << QString("ru_RU") << QString::fromUtf8("\320\262\320\276\321\201\320\272\321\200\320\265\321\201\320\265\320\275\321\214\320\265") << 7 << QLocale::LongFormat; - QTest::newRow("ru_RU short") << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::ShortFormat; - QTest::newRow("ru_RU narrow") << QString("ru_RU") << QString::fromUtf8("\320\222") << 7 << QLocale::NarrowFormat; + QTest::newRow("ru_RU long") + << QString("ru_RU") + << QString::fromUtf8("\320\262\320\276\321\201\320\272\321\200\320" + "\265\321\201\320\265\320\275\321\214\320\265") + << 7 << QLocale::LongFormat; + QTest::newRow("ru_RU short") + << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::ShortFormat; + QTest::newRow("ru_RU narrow") + << QString("ru_RU") << QString::fromUtf8("\320\222") << 7 << QLocale::NarrowFormat; } void tst_QLocale::standaloneDayName() @@ -2129,9 +2215,7 @@ void tst_QLocale::standaloneDayName() void tst_QLocale::underflowOverflow() { - QString -a(QLatin1String("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e10")); - + QString a(QLatin1String("0.") + QString(546, QLatin1Char('0')) + QLatin1String("1e10")); bool ok = false; double d = a.toDouble(&ok); QVERIFY(!ok); @@ -2160,75 +2244,56 @@ a(QLatin1String("0.0000000000000000000000000000000000000000000000000000000000000 QVERIFY(!ok); } -void tst_QLocale::defaultNumeringSystem() +void tst_QLocale::defaultNumberingSystem_data() { - QLocale sk("sk_SK"); - QCOMPARE(sk.toString(123), QLatin1String("123")); + QTest::addColumn<QString>("expect"); - QLocale ta("ta_IN"); - QCOMPARE(ta.toString(123), QLatin1String("123")); - - QLocale te("te_IN"); - QCOMPARE(te.toString(123), QLatin1String("123")); - - QLocale hi("hi_IN"); - QCOMPARE(hi.toString(123), QLatin1String("123")); - - QLocale gu("gu_IN"); - QCOMPARE(gu.toString(123), QLatin1String("123")); - - QLocale kn("kn_IN"); - QCOMPARE(kn.toString(123), QLatin1String("123")); - - QLocale pa("pa_IN"); - QCOMPARE(pa.toString(123), QLatin1String("123")); - - QLocale ne("ne_IN"); - QCOMPARE(ne.toString(123), QString::fromUtf8("१२३")); + QTest::newRow("sk_SK") << QStringLiteral("123"); + QTest::newRow("ta_IN") << QStringLiteral("123"); + QTest::newRow("te_IN") << QStringLiteral("123"); + QTest::newRow("hi_IN") << QStringLiteral("123"); + QTest::newRow("gu_IN") << QStringLiteral("123"); + QTest::newRow("kn_IN") << QStringLiteral("123"); + QTest::newRow("pa_IN") << QStringLiteral("123"); + QTest::newRow("ne_IN") << QString::fromUtf8("१२३"); + QTest::newRow("mr_IN") << QString::fromUtf8("१२३"); + QTest::newRow("ml_IN") << QStringLiteral("123"); + QTest::newRow("kok_IN") << QStringLiteral("123"); +} - QLocale mr("mr_IN"); - QCOMPARE(mr.toString(123), QString::fromUtf8("१२३")); +void tst_QLocale::defaultNumberingSystem() +{ + QFETCH(QString, expect); + QLatin1String name(QTest::currentDataTag()); + QLocale locale(name); + QCOMPARE(locale.toString(123), expect); +} - QLocale ml("ml_IN"); - QCOMPARE(ml.toString(123), QLatin1String("123")); +void tst_QLocale::ampm_data() +{ + QTest::addColumn<QString>("morn"); + QTest::addColumn<QString>("even"); - QLocale kok("kok_IN"); - QCOMPARE(kok.toString(123), QLatin1String("123")); + QTest::newRow("C") << QStringLiteral("AM") << QStringLiteral("PM"); + QTest::newRow("de_DE") << QStringLiteral("AM") << QStringLiteral("PM"); + QTest::newRow("sv_SE") << QStringLiteral("fm") << QStringLiteral("em"); + QTest::newRow("nl_NL") << QStringLiteral("a.m.") << QStringLiteral("p.m."); + QTest::newRow("uk_UA") << QString::fromUtf8("\320\264\320\277") + << QString::fromUtf8("\320\277\320\277"); + QTest::newRow("tr_TR") << QString::fromUtf8("\303\226\303\226") + << QString::fromUtf8("\303\226\123"); + QTest::newRow("id_ID") << QStringLiteral("AM") << QStringLiteral("PM"); + QTest::newRow("ta_LK") << QString::fromUtf8("முற்பகல்") << QString::fromUtf8("பிற்பகல்"); } void tst_QLocale::ampm() { - QLocale c(QLocale::C); - QCOMPARE(c.amText(), QLatin1String("AM")); - QCOMPARE(c.pmText(), QLatin1String("PM")); - - QLocale de("de_DE"); - QCOMPARE(de.amText(), QLatin1String("vorm.")); - QCOMPARE(de.pmText(), QLatin1String("nachm.")); - - QLocale sv("sv_SE"); - QCOMPARE(sv.amText(), QLatin1String("fm")); - QCOMPARE(sv.pmText(), QLatin1String("em")); - - QLocale nn("nl_NL"); - QCOMPARE(nn.amText(), QLatin1String("a.m.")); - QCOMPARE(nn.pmText(), QLatin1String("p.m.")); - - QLocale ua("uk_UA"); - QCOMPARE(ua.amText(), QString::fromUtf8("\320\264\320\277")); - QCOMPARE(ua.pmText(), QString::fromUtf8("\320\277\320\277")); - - QLocale tr("tr_TR"); - QCOMPARE(tr.amText(), QString::fromUtf8("\303\226\303\226")); - QCOMPARE(tr.pmText(), QString::fromUtf8("\303\226\123")); - - QLocale id("id_ID"); - QCOMPARE(id.amText(), QLatin1String("AM")); - QCOMPARE(id.pmText(), QLatin1String("PM")); - - QLocale ta("ta_LK"); - QCOMPARE(ta.amText(), QString::fromUtf8("முற்பகல்")); - QCOMPARE(ta.pmText(), QString::fromUtf8("பிற்பகல்")); + QFETCH(QString, morn); + QFETCH(QString, even); + QLatin1String name(QTest::currentDataTag()); + QLocale locale(name == QLatin1String("C") ? QLocale(QLocale::C) : QLocale(name)); + QCOMPARE(locale.amText(), morn); + QCOMPARE(locale.pmText(), even); } void tst_QLocale::dateFormat() @@ -2310,8 +2375,10 @@ void tst_QLocale::monthName() QCOMPARE(de.monthName(12, QLocale::NarrowFormat), QLatin1String("D")); QLocale ru("ru_RU"); - QCOMPARE(ru.monthName(1, QLocale::LongFormat), QString::fromUtf8("\321\217\320\275\320\262\320\260\321\200\321\217")); - QCOMPARE(ru.monthName(1, QLocale::ShortFormat), QString::fromUtf8("\321\217\320\275\320\262\56")); + QCOMPARE(ru.monthName(1, QLocale::LongFormat), + QString::fromUtf8("\321\217\320\275\320\262\320\260\321\200\321\217")); + QCOMPARE(ru.monthName(1, QLocale::ShortFormat), + QString::fromUtf8("\321\217\320\275\320\262\56")); QCOMPARE(ru.monthName(1, QLocale::NarrowFormat), QString::fromUtf8("\320\257")); QLocale ir("ga_IE"); @@ -2340,13 +2407,16 @@ void tst_QLocale::standaloneMonthName() // For de_DE locale Unicode CLDR database doesn't contain standalone long months // so just checking if the return value is the same as in monthName(). QCOMPARE(de.standaloneMonthName(12, QLocale::LongFormat), QLatin1String("Dezember")); - QCOMPARE(de.standaloneMonthName(12, QLocale::LongFormat), de.monthName(12, QLocale::LongFormat)); + QCOMPARE(de.standaloneMonthName(12, QLocale::LongFormat), + de.monthName(12, QLocale::LongFormat)); QCOMPARE(de.standaloneMonthName(12, QLocale::ShortFormat), QLatin1String("Dez")); QCOMPARE(de.standaloneMonthName(12, QLocale::NarrowFormat), QLatin1String("D")); QLocale ru("ru_RU"); - QCOMPARE(ru.standaloneMonthName(1, QLocale::LongFormat), QString::fromUtf8("\xd1\x8f\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\xd1\x8c")); - QCOMPARE(ru.standaloneMonthName(1, QLocale::ShortFormat), QString::fromUtf8("\xd1\x8f\xd0\xbd\xd0\xb2.")); + QCOMPARE(ru.standaloneMonthName(1, QLocale::LongFormat), + QString::fromUtf8("\xd1\x8f\xd0\xbd\xd0\xb2\xd0\xb0\xd1\x80\xd1\x8c")); + QCOMPARE(ru.standaloneMonthName(1, QLocale::ShortFormat), + QString::fromUtf8("\xd1\x8f\xd0\xbd\xd0\xb2.")); QCOMPARE(ru.standaloneMonthName(1, QLocale::NarrowFormat), QString::fromUtf8("\xd0\xaf")); } @@ -2371,19 +2441,30 @@ void tst_QLocale::currency() QCOMPARE(en_US.toCurrencyString(double(-1234.56), NULL, 4), QString("$-1,234.5600")); const QLocale ru_RU("ru_RU"); - QCOMPARE(ru_RU.toCurrencyString(qulonglong(1234)), QString::fromUtf8("1" "\xc2\xa0" "234\xc2\xa0\xe2\x82\xbd")); - QCOMPARE(ru_RU.toCurrencyString(qlonglong(-1234)), QString::fromUtf8("-1" "\xc2\xa0" "234\xc2\xa0\xe2\x82\xbd")); - QCOMPARE(ru_RU.toCurrencyString(double(1234.56)), QString::fromUtf8("1" "\xc2\xa0" "234,56\xc2\xa0\xe2\x82\xbd")); - QCOMPARE(ru_RU.toCurrencyString(double(-1234.56)), QString::fromUtf8("-1" "\xc2\xa0" "234,56\xc2\xa0\xe2\x82\xbd")); + QCOMPARE(ru_RU.toCurrencyString(qulonglong(1234)), + QString::fromUtf8("1" "\xc2\xa0" "234\xc2\xa0\xe2\x82\xbd")); + QCOMPARE(ru_RU.toCurrencyString(qlonglong(-1234)), + QString::fromUtf8("-1" "\xc2\xa0" "234\xc2\xa0\xe2\x82\xbd")); + QCOMPARE(ru_RU.toCurrencyString(double(1234.56)), + QString::fromUtf8("1" "\xc2\xa0" "234,56\xc2\xa0\xe2\x82\xbd")); + QCOMPARE(ru_RU.toCurrencyString(double(-1234.56)), + QString::fromUtf8("-1" "\xc2\xa0" "234,56\xc2\xa0\xe2\x82\xbd")); const QLocale de_DE("de_DE"); - QCOMPARE(de_DE.toCurrencyString(qulonglong(1234)), QString::fromUtf8("1.234\xc2\xa0\xe2\x82\xac")); - QCOMPARE(de_DE.toCurrencyString(qulonglong(1234), QLatin1String("BAZ")), QString::fromUtf8("1.234\xc2\xa0" "BAZ")); - QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234)), QString::fromUtf8("-1.234\xc2\xa0\xe2\x82\xac")); - QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234), QLatin1String("BAZ")), QString::fromUtf8("-1.234\xc2\xa0" "BAZ")); - QCOMPARE(de_DE.toCurrencyString(double(1234.56)), QString::fromUtf8("1.234,56\xc2\xa0\xe2\x82\xac")); - QCOMPARE(de_DE.toCurrencyString(double(-1234.56)), QString::fromUtf8("-1.234,56\xc2\xa0\xe2\x82\xac")); - QCOMPARE(de_DE.toCurrencyString(double(-1234.56), QLatin1String("BAZ")), QString::fromUtf8("-1.234,56\xc2\xa0" "BAZ")); + QCOMPARE(de_DE.toCurrencyString(qulonglong(1234)), + QString::fromUtf8("1.234\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(qulonglong(1234), QLatin1String("BAZ")), + QString::fromUtf8("1.234\xc2\xa0" "BAZ")); + QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234)), + QString::fromUtf8("-1.234\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234), QLatin1String("BAZ")), + QString::fromUtf8("-1.234\xc2\xa0" "BAZ")); + QCOMPARE(de_DE.toCurrencyString(double(1234.56)), + QString::fromUtf8("1.234,56\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(double(-1234.56)), + QString::fromUtf8("-1.234,56\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(double(-1234.56), QLatin1String("BAZ")), + QString::fromUtf8("-1.234,56\xc2\xa0" "BAZ")); const QLocale es_CR(QLocale::Spanish, QLocale::CostaRica); QCOMPARE(es_CR.toCurrencyString(double(1565.25)), @@ -2398,11 +2479,13 @@ void tst_QLocale::quoteString() const QString someText("text"); const QLocale c(QLocale::C); QCOMPARE(c.quoteString(someText), QString::fromUtf8("\x22" "text" "\x22")); - QCOMPARE(c.quoteString(someText, QLocale::AlternateQuotation), QString::fromUtf8("\x27" "text" "\x27")); + QCOMPARE(c.quoteString(someText, QLocale::AlternateQuotation), + QString::fromUtf8("\x27" "text" "\x27")); const QLocale de_CH("de_CH"); QCOMPARE(de_CH.quoteString(someText), QString::fromUtf8("\xe2\x80\x9e" "text" "\xe2\x80\x9c")); - QCOMPARE(de_CH.quoteString(someText, QLocale::AlternateQuotation), QString::fromUtf8("\xe2\x80\x9a" "text" "\xe2\x80\x98")); + QCOMPARE(de_CH.quoteString(someText, QLocale::AlternateQuotation), + QString::fromUtf8("\xe2\x80\x9a" "text" "\xe2\x80\x98")); } @@ -2484,23 +2567,28 @@ void tst_QLocale::listPatterns() QCOMPARE(zh_CN.createSeparatedList(sl1), QString("")); QCOMPARE(zh_CN.createSeparatedList(sl2), QString("aaa")); QCOMPARE(zh_CN.createSeparatedList(sl3), QString::fromUtf8("aaa" "\xe5\x92\x8c" "bbb")); - QCOMPARE(zh_CN.createSeparatedList(sl4), QString::fromUtf8("aaa" "\xe3\x80\x81" "bbb" "\xe5\x92\x8c" "ccc")); - QCOMPARE(zh_CN.createSeparatedList(sl5), QString::fromUtf8("aaa" "\xe3\x80\x81" "bbb" "\xe3\x80\x81" "ccc" "\xe5\x92\x8c" "ddd")); + QCOMPARE(zh_CN.createSeparatedList(sl4), + QString::fromUtf8("aaa" "\xe3\x80\x81" "bbb" "\xe5\x92\x8c" "ccc")); + QCOMPARE(zh_CN.createSeparatedList(sl5), + QString::fromUtf8("aaa" "\xe3\x80\x81" "bbb" "\xe3\x80\x81" + "ccc" "\xe5\x92\x8c" "ddd")); } -void tst_QLocale::measurementSystems() +void tst_QLocale::measurementSystems_data() { - QLocale locale(QLocale::English, QLocale::UnitedStates); - QCOMPARE(locale.measurementSystem(), QLocale::ImperialUSSystem); - - locale = QLocale(QLocale::English, QLocale::UnitedKingdom); - QCOMPARE(locale.measurementSystem(), QLocale::ImperialUKSystem); - - locale = QLocale(QLocale::English, QLocale::Australia); - QCOMPARE(locale.measurementSystem(), QLocale::MetricSystem); + QTest::addColumn<QLocale>("locale"); + QTest::addColumn<QLocale::MeasurementSystem>("system"); + QTest::newRow("en_US") << QLocale(QLocale::English, QLocale::UnitedStates) << QLocale::ImperialUSSystem; + QTest::newRow("en_GB") << QLocale(QLocale::English, QLocale::UnitedKingdom) << QLocale::ImperialUKSystem; + QTest::newRow("en_AU") << QLocale(QLocale::English, QLocale::Australia) << QLocale::MetricSystem; + QTest::newRow("de") << QLocale(QLocale::German) << QLocale::MetricSystem; +} - locale = QLocale(QLocale::German); - QCOMPARE(locale.measurementSystem(), QLocale::MetricSystem); +void tst_QLocale::measurementSystems() +{ + QFETCH(QLocale, locale); + QFETCH(QLocale::MeasurementSystem, system); + QCOMPARE(locale.measurementSystem(), system); } void tst_QLocale::QTBUG_26035_positivesign() @@ -2566,7 +2654,9 @@ void tst_QLocale::textDirection_data() case QLocale::Uighur: case QLocale::Urdu: case QLocale::Yiddish: - rightToLeft = QLocale(QLocale::Language(language)).language() == QLocale::Language(language); // false if there is no locale data for language + // false if there is no locale data for language: + rightToLeft = (QLocale(QLocale::Language(language)).language() + == QLocale::Language(language)); break; default: break; @@ -2651,6 +2741,37 @@ void tst_QLocale::formattedDataSize_data() #undef ROWQ #undef ROWB } + + // Languages which don't use a Latin alphabet + + const QLocale::DataSizeFormats iecFormat = QLocale::DataSizeIecFormat; + const QLocale::DataSizeFormats traditionalFormat = QLocale::DataSizeTraditionalFormat; + const QLocale::DataSizeFormats siFormat = QLocale::DataSizeSIFormat; + const QLocale::Language lang = QLocale::Russian; + + QTest::newRow("Russian-IEC-0") << lang << 2 << iecFormat << 0 << QString("0 \u0431\u0430\u0439\u0442\u044B"); + QTest::newRow("Russian-IEC-10") << lang << 2 << iecFormat << 10 << QString("10 \u0431\u0430\u0439\u0442\u044B"); + // CLDR doesn't provide IEC prefixes (yet?) so they aren't getting translated + QTest::newRow("Russian-IEC-12Ki") << lang << 2 << iecFormat << 12345 << QString("12,06 KiB"); + QTest::newRow("Russian-IEC-16Ki") << lang << 2 << iecFormat << 16384 << QString("16,00 KiB"); + QTest::newRow("Russian-IEC-1235k") << lang << 2 << iecFormat << 1234567 << QString("1,18 MiB"); + QTest::newRow("Russian-IEC-1374k") << lang << 2 << iecFormat << 1374744 << QString("1,31 MiB"); + QTest::newRow("Russian-IEC-1234M") << lang << 2 << iecFormat << 1234567890 << QString("1,15 GiB"); + + QTest::newRow("Russian-Trad-0") << lang << 2 << traditionalFormat << 0 << QString("0 \u0431\u0430\u0439\u0442\u044B"); + QTest::newRow("Russian-Trad-10") << lang << 2 << traditionalFormat << 10 << QString("10 \u0431\u0430\u0439\u0442\u044B"); + QTest::newRow("Russian-Trad-12Ki") << lang << 2 << traditionalFormat << 12345 << QString("12,06 \u043A\u0411"); + QTest::newRow("Russian-Trad-16Ki") << lang << 2 << traditionalFormat << 16384 << QString("16,00 \u043A\u0411"); + QTest::newRow("Russian-Trad-1235k") << lang << 2 << traditionalFormat << 1234567 << QString("1,18 \u041C\u0411"); + QTest::newRow("Russian-Trad-1374k") << lang << 2 << traditionalFormat << 1374744 << QString("1,31 \u041C\u0411"); + QTest::newRow("Russian-Trad-1234M") << lang << 2 << traditionalFormat << 1234567890 << QString("1,15 \u0413\u0411"); + + QTest::newRow("Russian-Decimal-0") << lang << 2 << siFormat << 0 << QString("0 \u0431\u0430\u0439\u0442\u044B"); + QTest::newRow("Russian-Decimal-10") << lang << 2 << siFormat << 10 << QString("10 \u0431\u0430\u0439\u0442\u044B"); + QTest::newRow("Russian-Decimal-16Ki") << lang << 2 << siFormat << 16384 << QString("16,38 \u043A\u0411"); + QTest::newRow("Russian-Decimal-1234k") << lang << 2 << siFormat << 1234567 << QString("1,23 \u041C\u0411"); + QTest::newRow("Russian-Decimal-1374k") << lang << 2 << siFormat << 1374744 << QString("1,37 \u041C\u0411"); + QTest::newRow("Russian-Decimal-1234M") << lang << 2 << siFormat << 1234567890 << QString("1,23 \u0413\u0411"); } void tst_QLocale::formattedDataSize() @@ -2663,25 +2784,33 @@ void tst_QLocale::formattedDataSize() QCOMPARE(QLocale(language).formattedDataSize(bytes, decimalPlaces, units), output); } -void tst_QLocale::bcp47Name() +void tst_QLocale::bcp47Name_data() { - QCOMPARE(QLocale("C").bcp47Name(), QStringLiteral("en")); - QCOMPARE(QLocale("en").bcp47Name(), QStringLiteral("en")); - QCOMPARE(QLocale("en_US").bcp47Name(), QStringLiteral("en")); - QCOMPARE(QLocale("en_GB").bcp47Name(), QStringLiteral("en-GB")); - QCOMPARE(QLocale("en_DE").bcp47Name(), QStringLiteral("en-DE")); - QCOMPARE(QLocale("de_DE").bcp47Name(), QStringLiteral("de")); - QCOMPARE(QLocale("sr_RS").bcp47Name(), QStringLiteral("sr")); - QCOMPARE(QLocale("sr_Cyrl_RS").bcp47Name(), QStringLiteral("sr")); - QCOMPARE(QLocale("sr_Latn_RS").bcp47Name(), QStringLiteral("sr-Latn")); - QCOMPARE(QLocale("sr_ME").bcp47Name(), QStringLiteral("sr-ME")); - QCOMPARE(QLocale("sr_Cyrl_ME").bcp47Name(), QStringLiteral("sr-Cyrl-ME")); - QCOMPARE(QLocale("sr_Latn_ME").bcp47Name(), QStringLiteral("sr-ME")); + QTest::addColumn<QString>("expect"); + + QTest::newRow("C") << QStringLiteral("en"); + QTest::newRow("en") << QStringLiteral("en"); + QTest::newRow("en_US") << QStringLiteral("en"); + QTest::newRow("en_GB") << QStringLiteral("en-GB"); + QTest::newRow("en_DE") << QStringLiteral("en-DE"); + QTest::newRow("de_DE") << QStringLiteral("de"); + QTest::newRow("sr_RS") << QStringLiteral("sr"); + QTest::newRow("sr_Cyrl_RS") << QStringLiteral("sr"); + QTest::newRow("sr_Latn_RS") << QStringLiteral("sr-Latn"); + QTest::newRow("sr_ME") << QStringLiteral("sr-ME"); + QTest::newRow("sr_Cyrl_ME") << QStringLiteral("sr-Cyrl-ME"); + QTest::newRow("sr_Latn_ME") << QStringLiteral("sr-ME"); // Fall back to defaults when country isn't in CLDR for this language: - QCOMPARE(QLocale("sr_HR").bcp47Name(), QStringLiteral("sr")); - QCOMPARE(QLocale("sr_Cyrl_HR").bcp47Name(), QStringLiteral("sr")); - QCOMPARE(QLocale("sr_Latn_HR").bcp47Name(), QStringLiteral("sr-Latn")); + QTest::newRow("sr_HR") << QStringLiteral("sr"); + QTest::newRow("sr_Cyrl_HR") << QStringLiteral("sr"); + QTest::newRow("sr_Latn_HR") << QStringLiteral("sr-Latn"); +} + +void tst_QLocale::bcp47Name() +{ + QFETCH(QString, expect); + QCOMPARE(QLocale(QLatin1String(QTest::currentDataTag())).bcp47Name(), expect); } class MySystemLocale : public QSystemLocale diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro b/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro index 26b3a47472..8599596344 100644 --- a/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro +++ b/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro @@ -2,3 +2,4 @@ CONFIG += testcase TARGET = tst_qmacautoreleasepool QT = core testlib SOURCES = tst_qmacautoreleasepool.mm +LIBS += -framework Foundation diff --git a/tests/auto/corelib/tools/qmakearray/qmakearray.pro b/tests/auto/corelib/tools/qmakearray/qmakearray.pro new file mode 100644 index 0000000000..abb3d9fdbc --- /dev/null +++ b/tests/auto/corelib/tools/qmakearray/qmakearray.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +TARGET = tst_qmakearray +QT = core testlib core-private +SOURCES = $$PWD/tst_qmakearray.cpp diff --git a/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp b/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp new file mode 100644 index 0000000000..e0d3f52719 --- /dev/null +++ b/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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 <private/qmakearray_p.h> + + +class tst_QMakeArray : public QObject +{ + Q_OBJECT + +private slots: + void quicksort(); +}; + +struct Pair { + unsigned int key; + unsigned int val; + + constexpr bool operator <=(const Pair &that) const noexcept + { + return key <= that.key; + } + + constexpr bool operator<(const Pair &that) const noexcept + { + return key < that.key; + } + + constexpr bool operator==(const Pair &that) const noexcept + { + return key == that.key; + } +}; + +template<std::size_t Key, std::size_t Val> +struct PairQuickSortElem +{ + using Type = Pair; + static constexpr Type data() noexcept { return Type{Key, Val}; } +}; + +void tst_QMakeArray::quicksort() +{ + constexpr const auto sorted_array = qMakeArray( + QSortedData< + PairQuickSortElem<10, 0>, + PairQuickSortElem<5, 0>, + PairQuickSortElem<7, 0>, + PairQuickSortElem<1, 0>, + PairQuickSortElem<8, 0>, + PairQuickSortElem<6, 0>, + PairQuickSortElem<4, 0>, + PairQuickSortElem<3, 0>, + PairQuickSortElem<1, 0>, + PairQuickSortElem<2, 0>, + PairQuickSortElem<10, 0>, + PairQuickSortElem<5, 0> + >::Data{}); + static_assert(sorted_array.size() == 12, "sorted_array.size() != 12"); + QCOMPARE(sorted_array[0].key, 1u); + QCOMPARE(sorted_array[1].key, 1u); + QCOMPARE(sorted_array[2].key, 2u); + QCOMPARE(sorted_array[3].key, 3u); + QCOMPARE(sorted_array[4].key, 4u); + QCOMPARE(sorted_array[5].key, 5u); + QCOMPARE(sorted_array[6].key, 5u); + QCOMPARE(sorted_array[7].key, 6u); + QCOMPARE(sorted_array[8].key, 7u); + QCOMPARE(sorted_array[9].key, 8u); + QCOMPARE(sorted_array[10].key, 10u); + QCOMPARE(sorted_array[11].key, 10u); +} + + +QTEST_APPLESS_MAIN(tst_QMakeArray) +#include "tst_qmakearray.moc" 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/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/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/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..c23ee3b0ba 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,190 @@ 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; + } + + // pcre2 does not support JIT for winrt. As this test row takes a long time without JIT we skip + // it for winrt as it might time out in COIN. +#ifndef Q_OS_WINRT + { + QString subject = "ab"; + subject.append(QString(512*1024, QLatin1Char('x'))); + subject.append("c"); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } +#endif // Q_OS_WINRT + + { + 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); + + QElapsedTimer time; + time.start(); + static const int THREAD_SAFETY_ITERATIONS = 50; + const int threadCount = qMax(QThread::idealThreadCount(), 4); + + for (int threadSafetyIteration = 0; threadSafetyIteration < THREAD_SAFETY_ITERATIONS && time.elapsed() < 2000; ++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("[*]", "test.html", -1); + addRow("[?]","test.html", -1); + addRow("[?]","test.h?ml", 6); + addRow("[[]","test.h[ml", 6); + addRow("[]]","test.h]ml", 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); + + addRow("foo/*/bar", "Qt/foo/baz/bar", 3); + addRow("foo/(*)/bar", "Qt/foo/baz/bar", -1); + addRow("foo/(*)/bar", "Qt/foo/(baz)/bar", 3); + addRow("foo/?/bar", "Qt/foo/Q/bar", 3); + addRow("foo/?/bar", "Qt/foo/Qt/bar", -1); + addRow("foo/(?)/bar", "Qt/foo/Q/bar", -1); + addRow("foo/(?)/bar", "Qt/foo/(Q)/bar", 3); + +#ifdef Q_OS_WIN + addRow("foo\\*\\bar", "Qt\\foo\\baz\\bar", 3); + addRow("foo\\(*)\\bar", "Qt\\foo\\baz\\bar", -1); + addRow("foo\\(*)\\bar", "Qt\\foo\\(baz)\\bar", 3); + addRow("foo\\?\\bar", "Qt\\foo\\Q\\bar", 3); + addRow("foo\\?\\bar", "Qt\\foo\\Qt\\bar", -1); + addRow("foo\\(?)\\bar", "Qt\\foo\\Q\\bar", -1); + addRow("foo\\(?)\\bar", "Qt\\foo\\(Q)\\bar", 3); +#endif +} + +void tst_QRegularExpression::wildcard() +{ + QFETCH(QString, pattern); + QFETCH(QString, string); + QFETCH(int, foundIndex); + + QRegularExpression re(QRegularExpression::wildcardToRegularExpression(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); + QFETCH(bool, isValid); + + QRegularExpression re(QRegularExpression::wildcardToRegularExpression(pattern)); + 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/qscopeguard/qscopeguard.pro b/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro new file mode 100644 index 0000000000..070d4b077c --- /dev/null +++ b/tests/auto/corelib/tools/qscopeguard/qscopeguard.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +TARGET = tst_qscopeguard +QT = core testlib +SOURCES = tst_qscopeguard.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp index 38a3a64fa3..f95d48f042 100644 --- a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp +++ b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins <sergio.martins@kdab.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -27,13 +27,59 @@ ****************************************************************************/ #include <QtTest/QtTest> -#include "../tst_qregularexpression.h" +#include <QtCore/QScopeGuard> -class tst_QRegularExpression_ForceOptimize : public tst_QRegularExpression +/*! + \class tst_QScopedGuard + \internal + \since 5.11 + \brief Tests class QScopedCleanup and function qScopeGuard + + */ +class tst_QScopedGuard : public QObject { Q_OBJECT + +private Q_SLOTS: + void leavingScope(); + void exceptions(); }; -QTEST_APPLESS_MAIN(tst_QRegularExpression_ForceOptimize) +static int s_globalState = 0; + +void tst_QScopedGuard::leavingScope() +{ + auto cleanup = qScopeGuard([] { s_globalState++; QCOMPARE(s_globalState, 3); }); + QCOMPARE(s_globalState, 0); + + { + auto cleanup = qScopeGuard([] { s_globalState++; }); + QCOMPARE(s_globalState, 0); + } + + QCOMPARE(s_globalState, 1); + s_globalState++; +} + +void tst_QScopedGuard::exceptions() +{ + s_globalState = 0; + bool caught = false; + QT_TRY + { + auto cleanup = qScopeGuard([&caught] { s_globalState++; }); + QT_THROW(std::bad_alloc()); //if Qt compiled without exceptions this is noop + s_globalState = 100; + } + QT_CATCH(...) + { + caught = true; + QCOMPARE(s_globalState, 1); + } + + QVERIFY((caught && s_globalState == 1) || (!caught && s_globalState == 101)); + +} -#include "tst_qregularexpression_forceoptimize.moc" +QTEST_MAIN(tst_QScopedGuard) +#include "tst_qscopeguard.moc" diff --git a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp index 3e1668522e..4dc620e6ab 100644 --- a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp @@ -53,6 +53,12 @@ # include <unistd.h> #endif +enum { + QMakeTimeout = 300000, // 5 minutes + CompileTimeout = 600000, // 10 minutes + RunTimeout = 300000 // 5 minutes +}; + static QString makespec() { static const char default_makespec[] = DEFAULT_MAKESPEC; @@ -143,7 +149,7 @@ namespace QTest { bool prepareSourceCode(const QByteArray &body); bool createProjectFile(); bool runQmake(); - bool runMake(Target target); + bool runMake(Target target, int timeout); bool commonSetup(const QByteArray &body); }; @@ -602,7 +608,7 @@ namespace QTest { std_err += "qmake: "; std_err += qmake.errorString().toLocal8Bit(); } else { - ok = qmake.waitForFinished(); + ok = qmake.waitForFinished(QMakeTimeout); exitCode = qmake.exitCode(); if (!ok) QTest::ensureStopped(qmake); @@ -617,7 +623,7 @@ namespace QTest { #endif // QT_CONFIG(process) } - bool QExternalTestPrivate::runMake(Target target) + bool QExternalTestPrivate::runMake(Target target, int timeout) { #if !QT_CONFIG(process) return false; @@ -670,7 +676,7 @@ namespace QTest { } make.closeWriteChannel(); - bool ok = make.waitForFinished(channelMode == QProcess::ForwardedChannels ? -1 : 60000); + bool ok = make.waitForFinished(channelMode == QProcess::ForwardedChannels ? -1 : timeout); if (!ok) QTest::ensureStopped(make); exitCode = make.exitCode(); @@ -705,7 +711,7 @@ namespace QTest { failedStage = QExternalTest::CompilationStage; std_out += "\n### --- stdout from make (compilation) --- ###\n"; std_err += "\n### --- stderr from make (compilation) --- ###\n"; - return runMake(Compile); + return runMake(Compile, CompileTimeout); } bool QExternalTestPrivate::tryLink(const QByteArray &body) @@ -717,7 +723,7 @@ namespace QTest { failedStage = QExternalTest::LinkStage; std_out += "\n### --- stdout from make (linking) --- ###\n"; std_err += "\n### --- stderr from make (linking) --- ###\n"; - return runMake(Link); + return runMake(Link, CompileTimeout); } bool QExternalTestPrivate::tryRun(const QByteArray &body) @@ -729,7 +735,7 @@ namespace QTest { failedStage = QExternalTest::RunStage; std_out += "\n### --- stdout from process --- ###\n"; std_err += "\n### --- stderr from process --- ###\n"; - return runMake(Run); + return runMake(Run, RunTimeout); } } QT_END_NAMESPACE diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 2074c9789a..f429bda804 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,46 @@ 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). The test cases boil down to code like this: + // console.log("\u1111\u1171\u11B6".localeCompare("\ud4db") + + // example from Unicode 5.0, section 3.7, definition D70 + QTest::newRow("normalize1") << QString() << QString::fromUtf8("o\xCC\x88") << QString::fromUtf8("\xC3\xB6") << 0; + // examples from Unicode 5.0, chapter 3.11 + QTest::newRow("normalize2") << QString() << QString::fromUtf8("\xC3\xA4\xCC\xA3") << QString::fromUtf8("a\xCC\xA3\xCC\x88") << 0; + QTest::newRow("normalize3") << QString() << QString::fromUtf8("a\xCC\x88\xCC\xA3") << QString::fromUtf8("a\xCC\xA3\xCC\x88") << 0; + QTest::newRow("normalize4") << QString() << QString::fromUtf8("\xE1\xBA\xA1\xCC\x88") << QString::fromUtf8("a\xCC\xA3\xCC\x88") << 0; + QTest::newRow("normalize5") << QString() << QString::fromUtf8("\xC3\xA4\xCC\x86") << QString::fromUtf8("a\xCC\x88\xCC\x86") << 0; + QTest::newRow("normalize6") << QString() << QString::fromUtf8("\xC4\x83\xCC\x88") << QString::fromUtf8("a\xCC\x86\xCC\x88") << 0; + // example from Unicode 5.0, chapter 3.12 + QTest::newRow("normalize7") << QString() << QString::fromUtf8("\xE1\x84\x91\xE1\x85\xB1\xE1\x86\xB6") << QString::fromUtf8("\xED\x93\x9B") << 0; + // examples from UTS 10, Unicode Collation Algorithm + QTest::newRow("normalize8") << QString() << QString::fromUtf8("\xE2\x84\xAB") << QString::fromUtf8("\xC3\x85") << 0; + QTest::newRow("normalize9") << QString() << QString::fromUtf8("\xE2\x84\xAB") << QString::fromUtf8("A\xCC\x8A") << 0; + QTest::newRow("normalize10") << QString() << QString::fromUtf8("x\xCC\x9B\xCC\xA3") << QString::fromUtf8("x\xCC\xA3\xCC\x9B") << 0; + QTest::newRow("normalize11") << QString() << QString::fromUtf8("\xE1\xBB\xB1") << QString::fromUtf8("\xE1\xBB\xA5\xCC\x9B") << 0; + QTest::newRow("normalize12") << QString() << QString::fromUtf8("\xE1\xBB\xB1") << QString::fromUtf8("u\xCC\x9B\xCC\xA3") << 0; + QTest::newRow("normalize13") << QString() << QString::fromUtf8("\xE1\xBB\xB1") << QString::fromUtf8("\xC6\xB0\xCC\xA3") << 0; + QTest::newRow("normalize14") << QString() << QString::fromUtf8("\xE1\xBB\xB1") << QString::fromUtf8("u\xCC\xA3\xCC\x9B") << 0; + // examples from UAX 15, Unicode Normalization Forms + QTest::newRow("normalize15") << QString() << QString::fromUtf8("\xC3\x87") << QString::fromUtf8("C\xCC\xA7") << 0; + QTest::newRow("normalize16") << QString() << QString::fromUtf8("q\xCC\x87\xCC\xA3") << QString::fromUtf8("q\xCC\xA3\xCC\x87") << 0; + QTest::newRow("normalize17") << QString() << QString::fromUtf8("\xEA\xB0\x80") << QString::fromUtf8("\xE1\x84\x80\xE1\x85\xA1") << 0; + QTest::newRow("normalize18") << QString() << QString::fromUtf8("\xE2\x84\xAB") << QString::fromUtf8("A\xCC\x8A") << 0; + QTest::newRow("normalize19") << QString() << QString::fromUtf8("\xE2\x84\xA6") << QString::fromUtf8("\xCE\xA9") << 0; + QTest::newRow("normalize20") << QString() << QString::fromUtf8("\xC3\x85") << QString::fromUtf8("A\xCC\x8A") << 0; + QTest::newRow("normalize21") << QString() << QString::fromUtf8("\xC3\xB4") << QString::fromUtf8("o\xCC\x82") << 0; + QTest::newRow("normalize22") << QString() << QString::fromUtf8("\xE1\xB9\xA9") << QString::fromUtf8("s\xCC\xA3\xCC\x87") << 0; + QTest::newRow("normalize23") << QString() << QString::fromUtf8("\xE1\xB8\x8B\xCC\xA3") << QString::fromUtf8("d\xCC\xA3\xCC\x87") << 0; + QTest::newRow("normalize24") << QString() << QString::fromUtf8("\xE1\xB8\x8B\xCC\xA3") << QString::fromUtf8("\xE1\xB8\x8D\xCC\x87") << 0; + QTest::newRow("normalize25") << QString() << QString::fromUtf8("q\xCC\x87\xCC\xA3") << QString::fromUtf8("q\xCC\xA3\xCC\x87") << 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 +5615,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 +5628,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 +5693,6 @@ void tst_QString::localeAwareCompare() if (!locale.isEmpty()) setlocale(LC_ALL, ""); } -#endif //!defined(Q_OS_WIN) void tst_QString::reverseIterators() { @@ -6160,11 +6238,14 @@ void tst_QString::compare() QStringRef r1(&s1, 0, s1.length()); QStringRef r2(&s2, 0, s2.length()); + const QStringView v2(s2); + QCOMPARE(sign(QString::compare(s1, s2)), csr); QCOMPARE(sign(QStringRef::compare(r1, r2)), csr); QCOMPARE(sign(s1.compare(s2)), csr); QCOMPARE(sign(s1.compare(r2)), csr); QCOMPARE(sign(r1.compare(r2)), csr); + QCOMPARE(sign(s1.compare(v2)), csr); QCOMPARE(sign(s1.compare(s2, Qt::CaseSensitive)), csr); QCOMPARE(sign(s1.compare(s2, Qt::CaseInsensitive)), cir); @@ -6172,6 +6253,8 @@ void tst_QString::compare() QCOMPARE(sign(s1.compare(r2, Qt::CaseInsensitive)), cir); QCOMPARE(sign(r1.compare(r2, Qt::CaseSensitive)), csr); QCOMPARE(sign(r1.compare(r2, Qt::CaseInsensitive)), cir); + QCOMPARE(sign(s1.compare(v2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(s1.compare(v2, Qt::CaseInsensitive)), cir); QCOMPARE(sign(QString::compare(s1, s2, Qt::CaseSensitive)), csr); QCOMPARE(sign(QString::compare(s1, s2, Qt::CaseInsensitive)), cir); diff --git a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp index 9f054190e5..a3aec4c299 100644 --- a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp +++ b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp @@ -273,6 +273,15 @@ void tst_QStringList::contains() QVERIFY(list.contains(QLatin1String("ARTHUR"), Qt::CaseInsensitive)); QVERIFY(list.contains(QLatin1String("dent"), Qt::CaseInsensitive)); QVERIFY(!list.contains(QLatin1String("hans"), Qt::CaseInsensitive)); + + QVERIFY(list.contains(QStringView(QString("arthur")))); + QVERIFY(!list.contains(QStringView(QString("ArthuR")))); + QVERIFY(!list.contains(QStringView(QString("Hans")))); + QVERIFY(list.contains(QStringView(QString("arthur")), Qt::CaseInsensitive)); + QVERIFY(list.contains(QStringView(QString("ArthuR")), Qt::CaseInsensitive)); + QVERIFY(list.contains(QStringView(QString("ARTHUR")), Qt::CaseInsensitive)); + QVERIFY(list.contains(QStringView(QString("dent")), Qt::CaseInsensitive)); + QVERIFY(!list.contains(QStringView(QString("hans")), Qt::CaseInsensitive)); } void tst_QStringList::removeDuplicates_data() diff --git a/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp index 4ae96005d0..e800a0d794 100644 --- a/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp +++ b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp @@ -217,6 +217,8 @@ private Q_SLOTS: #endif } + void comparison(); + private: template <typename String> void conversion_tests(String arg) const; @@ -615,5 +617,23 @@ void tst_QStringView::conversion_tests(String string) const } } +void tst_QStringView::comparison() +{ + const QStringView aa = QStringViewLiteral("aa"); + const QStringView upperAa = QStringViewLiteral("AA"); + const QStringView bb = QStringViewLiteral("bb"); + + QVERIFY(aa == aa); + QVERIFY(aa != bb); + QVERIFY(aa < bb); + QVERIFY(bb > aa); + + QCOMPARE(aa.compare(aa), 0); + QVERIFY(aa.compare(upperAa) != 0); + QCOMPARE(aa.compare(upperAa, Qt::CaseInsensitive), 0); + QVERIFY(aa.compare(bb) < 0); + QVERIFY(bb.compare(aa) > 0); +} + QTEST_APPLESS_MAIN(tst_QStringView) #include "tst_qstringview.moc" diff --git a/tests/auto/corelib/tools/qtimeline/BLACKLIST b/tests/auto/corelib/tools/qtimeline/BLACKLIST index 74f84a4a6d..5611969b4d 100644 --- a/tests/auto/corelib/tools/qtimeline/BLACKLIST +++ b/tests/auto/corelib/tools/qtimeline/BLACKLIST @@ -1,7 +1,9 @@ [interpolation] windows osx-10.12 +osx-10.13 [duration] windows [frameRate] osx-10.12 +osx-10.13 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); diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index f35ed026ac..f28cf21b8b 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -30,6 +30,7 @@ SUBDIRS=\ qlist \ qlist_strictiterators \ qlocale \ + qmakearray \ qmap \ qmap_strictiterators \ qmargins \ |