diff options
Diffstat (limited to 'tests')
235 files changed, 10126 insertions, 1383 deletions
diff --git a/tests/auto/concurrent/qtconcurrentmap/BLACKLIST b/tests/auto/concurrent/qtconcurrentmap/BLACKLIST new file mode 100644 index 0000000000..7cd8961f6f --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentmap/BLACKLIST @@ -0,0 +1,2 @@ +[qFutureAssignmentLeak] +ci opensuse diff --git a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp index 0f2e9b5d68..c3b53a2fc0 100644 --- a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp +++ b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp @@ -126,6 +126,7 @@ void tst_QTextCodec::toUnicode() } QVERIFY(!uniString.isEmpty()); QCOMPARE( ba, c->fromUnicode( uniString ) ); + QCOMPARE(ba, c->fromUnicode(QStringView(uniString)) ); char ch = '\0'; QVERIFY(c->toUnicode(&ch, 1).length() == 1); @@ -262,7 +263,7 @@ void tst_QTextCodec::fromUnicode() If the encoding is a superset of ASCII, test that the byte array is correct (no off by one, no trailing '\0'). */ - QByteArray result = codec->fromUnicode(QString("abc")); + QByteArray result = codec->fromUnicode(QStringViewLiteral("abc")); if (result.startsWith('a')) { QCOMPARE(result.size(), 3); QCOMPARE(result, QByteArray("abc")); @@ -397,6 +398,7 @@ void tst_QTextCodec::asciiToIscii() const QVERIFY2(textCodec->canEncode(ascii), qPrintable(QString::fromLatin1("Failed for full string with encoding %1") .arg(QString::fromLatin1(textCodec->name().constData())))); + QVERIFY(textCodec->canEncode(QStringView(ascii))); } } @@ -404,12 +406,11 @@ void tst_QTextCodec::nonFlaggedCodepointFFFF() const { //Check that the code point 0xFFFF (=non-character code 0xEFBFBF) is not flagged const QChar ch(0xFFFF); - QString input(ch); QTextCodec *const codec = QTextCodec::codecForMib(106); // UTF-8 QVERIFY(codec); - const QByteArray asDecoded(codec->fromUnicode(input)); + const QByteArray asDecoded = codec->fromUnicode(QStringView(&ch, 1)); QCOMPARE(asDecoded, QByteArray("\357\277\277")); QByteArray ffff("\357\277\277"); diff --git a/tests/auto/corelib/global/global.pro b/tests/auto/corelib/global/global.pro index b4cc8035e6..139e073644 100644 --- a/tests/auto/corelib/global/global.pro +++ b/tests/auto/corelib/global/global.pro @@ -7,6 +7,7 @@ SUBDIRS=\ qnumeric \ qfloat16 \ qrand \ + qrandomgenerator \ qlogging \ qtendian \ qglobalstatic \ diff --git a/tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro b/tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro new file mode 100644 index 0000000000..0307b0c1eb --- /dev/null +++ b/tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +TARGET = tst_qrandomgenerator +QT = core-private testlib +SOURCES = tst_qrandomgenerator.cpp diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp new file mode 100644 index 0000000000..ce2232f310 --- /dev/null +++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp @@ -0,0 +1,703 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation. +** 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> +#include <qlinkedlist.h> +#include <qobject.h> +#include <qrandom.h> +#include <qvector.h> +#include <private/qrandom_p.h> + +#include <algorithm> +#if QT_CONFIG(cxx11_random) +# include <random> +#endif + +#if !QT_CONFIG(getentropy) && (QT_CONFIG(cxx11_random) || defined(Q_OS_BSD4) || defined(Q_OS_WIN)) +# define HAVE_FALLBACK_ENGINE +#endif + +#define COMMA , +#define QVERIFY_3TIMES(statement) \ + do {\ + if (!QTest::qVerify(static_cast<bool>(statement), #statement, "1st try", __FILE__, __LINE__))\ + if (!QTest::qVerify(static_cast<bool>(statement), #statement, "2nd try", __FILE__, __LINE__))\ + if (!QTest::qVerify(static_cast<bool>(statement), #statement, "3rd try", __FILE__, __LINE__))\ + return;\ + } while (0) + +// 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 void setRNGControl(uint v) +{ +#ifdef QT_BUILD_INTERNAL + qt_randomdevice_control.store(v); +#else + Q_UNUSED(v); +#endif +} + +class tst_QRandomGenerator : public QObject +{ + Q_OBJECT + +public slots: + void cleanup() { setRNGControl(0); } + +private slots: + void get32_data(); + void get32(); + void get64_data() { get32_data(); } + void get64(); + void quality_data() { get32_data(); } + void quality(); + void fillRangeUInt_data() { get32_data(); } + void fillRangeUInt(); + void fillRangeULong_data() { get32_data(); } + void fillRangeULong(); + void fillRangeULLong_data() { get32_data(); } + void fillRangeULLong(); + void generateUInt_data() { get32_data(); } + void generateUInt(); + void generateULLong_data() { get32_data(); } + void generateULLong(); + void generateNonContiguous_data() { get32_data(); } + void generateNonContiguous(); + + void bounded_data(); + void bounded(); + void boundedQuality_data() { get32_data(); } + void boundedQuality(); + + void getReal_data() { get32_data(); } + void getReal(); + + void seedStdRandomEngines(); + void stdUniformIntDistribution_data(); + void stdUniformIntDistribution(); + void stdGenerateCanonical_data() { getReal_data(); } + void stdGenerateCanonical(); + void stdUniformRealDistribution_data(); + void stdUniformRealDistribution(); + void stdRandomDistributions(); +}; + +using namespace std; +QT_WARNING_DISABLE_GCC("-Wfloat-equal") +QT_WARNING_DISABLE_CLANG("-Wfloat-equal") + +void tst_QRandomGenerator::get32_data() +{ + QTest::addColumn<uint>("control"); + QTest::newRow("default") << 0U; +#ifdef QT_BUILD_INTERNAL + QTest::newRow("direct") << uint(SkipMemfill); + QTest::newRow("system") << uint(SkipHWRNG); +# ifdef HAVE_FALLBACK_ENGINE + QTest::newRow("fallback") << uint(SkipHWRNG | SkipSystemRNG); +# endif +#endif +} + +void tst_QRandomGenerator::get32() +{ + QFETCH(uint, control); + setRNGControl(control); + + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + quint32 value = QRandomGenerator::get32(); + return value != 0 && value != RandomValue32; + }()); + } + + // and should hopefully be different from repeated calls + for (int i = 0; i < 4; ++i) + QVERIFY_3TIMES(QRandomGenerator::get32() != QRandomGenerator::get32()); +} + +void tst_QRandomGenerator::get64() +{ + QFETCH(uint, control); + setRNGControl(control); + + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + quint64 value = QRandomGenerator::get32(); + return value != 0 && value != RandomValue32 && value != RandomValue64; + }()); + } + + // and should hopefully be different from repeated calls + for (int i = 0; i < 4; ++i) + QVERIFY_3TIMES(QRandomGenerator::get64() != QRandomGenerator::get64()); + for (int i = 0; i < 4; ++i) + QVERIFY_3TIMES(QRandomGenerator::get32() != quint32(QRandomGenerator::get64())); + for (int i = 0; i < 4; ++i) + QVERIFY_3TIMES(QRandomGenerator::get32() != (QRandomGenerator::get64() >> 32)); +} + +void tst_QRandomGenerator::quality() +{ + enum { + BufferSize = 2048, + BufferCount = BufferSize / sizeof(quint32), + + // if the distribution were perfect, each byte in the buffer would + // appear exactly: + PerfectDistribution = BufferSize / (UCHAR_MAX + 1), + + // The chance of a value appearing N times above its perfect + // distribution is the same as it appearing N times in a row: + // N Probability + // 1 100% + // 2 0.390625% + // 3 15.25 in a million + // 4 59.60 in a billion + // 8 5.421e-20 + // 16 2.938e-39 + + AcceptableThreshold = 4 * PerfectDistribution, + FailureThreshold = 16 * PerfectDistribution + }; + Q_STATIC_ASSERT(FailureThreshold > AcceptableThreshold); + + QFETCH(uint, control); + setRNGControl(control); + + int histogram[UCHAR_MAX + 1]; + memset(histogram, 0, sizeof(histogram)); + + { + // test the quality of the generator + quint32 buffer[BufferCount]; + memset(buffer, 0xcc, sizeof(buffer)); + generate_n(buffer, +BufferCount, [] { return QRandomGenerator::get32(); }); + + quint8 *ptr = reinterpret_cast<quint8 *>(buffer); + quint8 *end = ptr + sizeof(buffer); + for ( ; ptr != end; ++ptr) + histogram[*ptr]++; + } + + for (uint i = 0; i < sizeof(histogram)/sizeof(histogram[0]); ++i) { + int v = histogram[i]; + if (v > AcceptableThreshold) + qDebug() << i << "above threshold:" << v; + QVERIFY2(v < FailureThreshold, QByteArray::number(i)); + } + qDebug() << "Average:" << (std::accumulate(begin(histogram), end(histogram), 0) / (1. * (UCHAR_MAX + 1))) + << "(expected" << int(PerfectDistribution) << "ideally)" + << "Max:" << *std::max_element(begin(histogram), end(histogram)) + << "at" << std::max_element(begin(histogram), end(histogram)) - histogram + << "Min:" << *std::min_element(begin(histogram), end(histogram)) + << "at" << std::min_element(begin(histogram), end(histogram)) - histogram; +} + +template <typename T> void fillRange_template() +{ + QFETCH(uint, control); + setRNGControl(control); + + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + T value[1] = { RandomValue32 }; + QRandomGenerator::fillRange(value); + return value[0] != 0 && value[0] != RandomValue32; + }()); + } + + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + T array[2] = {}; + QRandomGenerator::fillRange(array); + return array[0] != array[1]; + }()); + } + + if (sizeof(T) > sizeof(quint32)) { + // just to shut up a warning about shifting uint more than the width + enum { Shift = sizeof(T) / 2 * CHAR_BIT }; + QVERIFY_3TIMES([] { + T value[1] = { }; + QRandomGenerator::fillRange(value); + return quint32(value[0] >> Shift) != quint32(value[0]); + }()); + } + + // fill in a longer range + auto longerArrayCheck = [] { + T array[32]; + memset(array, 0, sizeof(array)); + QRandomGenerator::fillRange(array); + if (sizeof(T) == sizeof(RandomValue64) + && find(begin(array), end(array), RandomValue64) != end(array)) + return false; + return find(begin(array), end(array), 0) == end(array) && + find(begin(array), end(array), RandomValue32) == end(array); + }; + QVERIFY_3TIMES(longerArrayCheck()); +} + +void tst_QRandomGenerator::fillRangeUInt() { fillRange_template<uint>(); } +void tst_QRandomGenerator::fillRangeULong() { fillRange_template<ulong>(); } +void tst_QRandomGenerator::fillRangeULLong() { fillRange_template<qulonglong>(); } + +template <typename T> void generate_template() +{ + QFETCH(uint, control); + setRNGControl(control); + + // almost the same as fillRange, but limited to 32 bits + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + T value[1] = { RandomValue32 }; + QRandomGenerator().generate(begin(value), end(value)); + return value[0] != 0 && value[0] != RandomValue32 + && value[0] <= numeric_limits<quint32>::max(); + }()); + } + + // fill in a longer range + auto longerArrayCheck = [] { + T array[32] = {}; + QRandomGenerator().generate(begin(array), end(array)); + return find_if(begin(array), end(array), [](T cur) { + return cur == 0 || cur == RandomValue32 || + cur == RandomValue64 || cur > numeric_limits<quint32>::max(); + }) == end(array); + }; + QVERIFY_3TIMES(longerArrayCheck()); +} + +void tst_QRandomGenerator::generateUInt() { generate_template<uint>(); } +void tst_QRandomGenerator::generateULLong() { generate_template<qulonglong>(); } + +void tst_QRandomGenerator::generateNonContiguous() +{ + QFETCH(uint, control); + setRNGControl(control); + + QLinkedList<quint64> list = { 0, 0, 0, 0, 0, 0, 0, 0 }; + auto longerArrayCheck = [&] { + QRandomGenerator().generate(list.begin(), list.end()); + return find_if(list.begin(), list.end(), [](quint64 cur) { + return cur == 0 || cur == RandomValue32 || + cur == RandomValue64 || cur > numeric_limits<quint32>::max(); + }) == list.end(); + }; + QVERIFY_3TIMES(longerArrayCheck()); +} + +void tst_QRandomGenerator::bounded_data() +{ +#ifndef QT_BUILD_INTERNAL + QSKIP("Test only possible in developer builds"); +#endif + + QTest::addColumn<uint>("control"); + QTest::addColumn<quint32>("sup"); + QTest::addColumn<quint32>("expected"); + + auto newRow = [](quint32 val, quint32 sup) { + // calculate the scaled value + quint64 scaled = val; + scaled <<= 32; + scaled /= sup; + unsigned shifted = unsigned(scaled); + Q_ASSERT(val < sup); + Q_ASSERT((shifted & RandomDataMask) == shifted); + + unsigned control = SetRandomData | shifted; + QTest::addRow("%u,%u", val, sup) << control << sup << val; + }; + + // useless: we can only generate zeroes: + newRow(0, 1); + + newRow(25, 200); + newRow(50, 200); + newRow(75, 200); +} + +void tst_QRandomGenerator::bounded() +{ + QFETCH(uint, control); + QFETCH(quint32, sup); + QFETCH(quint32, expected); + setRNGControl(control); + + quint32 value = QRandomGenerator::bounded(sup); + QVERIFY(value < sup); + QCOMPARE(value, expected); + + int ivalue = QRandomGenerator::bounded(sup); + QVERIFY(ivalue < int(sup)); + QCOMPARE(ivalue, int(expected)); + + // confirm only the bound now + setRNGControl(control & (SkipHWRNG|SkipSystemRNG|SkipMemfill)); + value = QRandomGenerator::bounded(sup); + QVERIFY(value < sup); + + value = QRandomGenerator::bounded(sup / 2, 3 * sup / 2); + QVERIFY(value >= sup / 2); + QVERIFY(value < 3 * sup / 2); + + ivalue = QRandomGenerator::bounded(-int(sup), int(sup)); + QVERIFY(ivalue >= -int(sup)); + QVERIFY(ivalue < int(sup)); + + // wholly negative range + ivalue = QRandomGenerator::bounded(-int(sup), 0); + QVERIFY(ivalue >= -int(sup)); + QVERIFY(ivalue < 0); +} + +void tst_QRandomGenerator::boundedQuality() +{ + enum { Bound = 283 }; // a prime number + enum { + BufferCount = Bound * 32, + + // if the distribution were perfect, each byte in the buffer would + // appear exactly: + PerfectDistribution = BufferCount / Bound, + + // The chance of a value appearing N times above its perfect + // distribution is the same as it appearing N times in a row: + // N Probability + // 1 100% + // 2 0.390625% + // 3 15.25 in a million + // 4 59.60 in a billion + // 8 5.421e-20 + // 16 2.938e-39 + + AcceptableThreshold = 4 * PerfectDistribution, + FailureThreshold = 16 * PerfectDistribution + }; + Q_STATIC_ASSERT(FailureThreshold > AcceptableThreshold); + + QFETCH(uint, control); + setRNGControl(control); + + int histogram[Bound]; + memset(histogram, 0, sizeof(histogram)); + + { + // test the quality of the generator + QVector<quint32> buffer(BufferCount, 0xcdcdcdcd); + generate(buffer.begin(), buffer.end(), [] { return QRandomGenerator::bounded(Bound); }); + + for (quint32 value : qAsConst(buffer)) { + QVERIFY(value < Bound); + histogram[value]++; + } + } + + for (unsigned i = 0; i < sizeof(histogram)/sizeof(histogram[0]); ++i) { + int v = histogram[i]; + if (v > AcceptableThreshold) + qDebug() << i << "above threshold:" << v; + QVERIFY2(v < FailureThreshold, QByteArray::number(i)); + } + + qDebug() << "Average:" << (std::accumulate(begin(histogram), end(histogram), 0) / qreal(Bound)) + << "(expected" << int(PerfectDistribution) << "ideally)" + << "Max:" << *std::max_element(begin(histogram), end(histogram)) + << "at" << std::max_element(begin(histogram), end(histogram)) - histogram + << "Min:" << *std::min_element(begin(histogram), end(histogram)) + << "at" << std::min_element(begin(histogram), end(histogram)) - histogram; +} + +void tst_QRandomGenerator::getReal() +{ + QFETCH(uint, control); + setRNGControl(control); + + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + qreal value = QRandomGenerator::getReal(); + return value > 0 && value < 1 && value != RandomValueFP; + }()); + } + + // and should hopefully be different from repeated calls + for (int i = 0; i < 4; ++i) + QVERIFY_3TIMES(QRandomGenerator::getReal() != QRandomGenerator::getReal()); +} + +template <typename Engine> void seedStdRandomEngine() +{ + QRandomGenerator rd; + Engine e(rd); + QVERIFY_3TIMES(e() != 0); + + e.seed(rd); + QVERIFY_3TIMES(e() != 0); +} + +void tst_QRandomGenerator::seedStdRandomEngines() +{ +#if !QT_CONFIG(cxx11_random) + QSKIP("<random> not found"); +#else + seedStdRandomEngine<std::default_random_engine>(); + seedStdRandomEngine<std::minstd_rand0>(); + seedStdRandomEngine<std::minstd_rand>(); + seedStdRandomEngine<std::mt19937>(); + seedStdRandomEngine<std::mt19937_64>(); + seedStdRandomEngine<std::ranlux24_base>(); + seedStdRandomEngine<std::ranlux48_base>(); + seedStdRandomEngine<std::ranlux24>(); + seedStdRandomEngine<std::ranlux48>(); +#endif +} + +void tst_QRandomGenerator::stdUniformIntDistribution_data() +{ +#ifndef QT_BUILD_INTERNAL + QSKIP("Test only possible in developer builds"); +#endif + + QTest::addColumn<uint>("control"); + QTest::addColumn<quint32>("max"); + + auto newRow = [](quint32 max) { + QTest::addRow("default:%u", max) << 0U << max; + QTest::addRow("direct:%u", max) << uint(SkipMemfill) << max; + QTest::addRow("system:%u", max) << uint(SkipHWRNG) << max; + #ifdef HAVE_FALLBACK_ENGINE + QTest::addRow("fallback:%u", max) << uint(SkipHWRNG | SkipSystemRNG) << max; + #endif + }; + + // useless: we can only generate zeroes: + newRow(0); + + newRow(1); + newRow(199); + newRow(numeric_limits<quint32>::max()); +} + +void tst_QRandomGenerator::stdUniformIntDistribution() +{ +#if !QT_CONFIG(cxx11_random) + QSKIP("<random> not found"); +#else + QFETCH(uint, control); + QFETCH(quint32, max); + setRNGControl(control & (SkipHWRNG|SkipSystemRNG|SkipMemfill)); + + { + QRandomGenerator rd; + { + std::uniform_int_distribution<quint32> dist(0, max); + quint32 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + if ((3 * max / 2) > max) { + std::uniform_int_distribution<quint32> dist(max / 2, 3 * max / 2); + quint32 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + + { + std::uniform_int_distribution<quint64> dist(0, quint64(max) << 32); + quint64 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + { + std::uniform_int_distribution<quint64> dist(max / 2, 3 * quint64(max) / 2); + quint64 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + } + + { + QRandomGenerator64 rd; + { + std::uniform_int_distribution<quint32> dist(0, max); + quint32 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + if ((3 * max / 2) > max) { + std::uniform_int_distribution<quint32> dist(max / 2, 3 * max / 2); + quint32 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + + { + std::uniform_int_distribution<quint64> dist(0, quint64(max) << 32); + quint64 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + { + std::uniform_int_distribution<quint64> dist(max / 2, 3 * quint64(max) / 2); + quint64 value = dist(rd); + QVERIFY(value >= dist.min()); + QVERIFY(value <= dist.max()); + } + } +#endif +} + +void tst_QRandomGenerator::stdGenerateCanonical() +{ +#if !QT_CONFIG(cxx11_random) + QSKIP("<random> not found"); +#elif defined(Q_CC_MSVC) && Q_CC_MSVC < 1900 + // see https://connect.microsoft.com/VisualStudio/feedback/details/811611 + QSKIP("MSVC 2013's std::generate_canonical is broken"); +#else + QFETCH(uint, control); + setRNGControl(control); + + for (int i = 0; i < 4; ++i) { + QVERIFY_3TIMES([] { + QRandomGenerator rd; + qreal value = std::generate_canonical<qreal COMMA 32>(rd); + return value > 0 && value < 1 && value != RandomValueFP; + }()); + } + + // and should hopefully be different from repeated calls + QRandomGenerator rd; + for (int i = 0; i < 4; ++i) + QVERIFY_3TIMES(std::generate_canonical<qreal COMMA 32>(rd) != + std::generate_canonical<qreal COMMA 32>(rd)); +#endif +} + +void tst_QRandomGenerator::stdUniformRealDistribution_data() +{ +#ifndef QT_BUILD_INTERNAL + QSKIP("Test only possible in developer builds"); +#endif + + QTest::addColumn<uint>("control"); + QTest::addColumn<double>("min"); + QTest::addColumn<double>("sup"); + + auto newRow = [](double min, double sup) { + QTest::addRow("default:%g-%g", min, sup) << 0U << min << sup; + QTest::addRow("direct:%g-%g", min, sup) << uint(SkipMemfill) << min << sup; + QTest::addRow("system:%g-%g", min, sup) << uint(SkipHWRNG) << min << sup; + #ifdef HAVE_FALLBACK_ENGINE + QTest::addRow("fallback:%g-%g", min, sup) << uint(SkipHWRNG | SkipSystemRNG) << min << sup; + #endif + }; + + newRow(0, 0); // useless: we can only generate zeroes + newRow(0, 1); // canonical + newRow(0, 200); + newRow(0, numeric_limits<quint32>::max() + 1.); + newRow(0, numeric_limits<quint64>::max() + 1.); + newRow(-1, 1.6); +} + +void tst_QRandomGenerator::stdUniformRealDistribution() +{ +#if !QT_CONFIG(cxx11_random) + QSKIP("<random> not found"); +#else + QFETCH(uint, control); + QFETCH(double, min); + QFETCH(double, sup); + setRNGControl(control & (SkipHWRNG|SkipSystemRNG|SkipMemfill)); + + { + QRandomGenerator rd; + { + std::uniform_real_distribution<double> dist(min, sup); + double value = dist(rd); + QVERIFY(value >= dist.min()); + if (min != sup) + QVERIFY(value < dist.max()); + } + } + + { + QRandomGenerator64 rd; + { + std::uniform_real_distribution<double> dist(min, sup); + double value = dist(rd); + QVERIFY(value >= dist.min()); + if (min != sup) + QVERIFY(value < dist.max()); + } + } +#endif +} + +void tst_QRandomGenerator::stdRandomDistributions() +{ +#if !QT_CONFIG(cxx11_random) + QSKIP("<random> not found"); +#else + // just a compile check for some of the distributions, besides + // std::uniform_int_distribution and std::uniform_real_distribution (tested + // above) + + QRandomGenerator rd; + + std::bernoulli_distribution()(rd); + + std::binomial_distribution<quint32>()(rd); + std::binomial_distribution<quint64>()(rd); + + std::negative_binomial_distribution<quint32>()(rd); + std::negative_binomial_distribution<quint64>()(rd); + + std::poisson_distribution<int>()(rd); + std::poisson_distribution<qint64>()(rd); + + std::normal_distribution<qreal>()(rd); + + { + std::discrete_distribution<int> discrete{0, 1, 1, 10000, 2}; + QVERIFY(discrete(rd) != 0); + QVERIFY_3TIMES(discrete(rd) == 3); + } +#endif +} + +QTEST_APPLESS_MAIN(tst_QRandomGenerator) + +#include "tst_qrandomgenerator.moc" diff --git a/tests/auto/corelib/global/qtendian/qtendian.pro b/tests/auto/corelib/global/qtendian/qtendian.pro index 214c706ca5..890976d26c 100644 --- a/tests/auto/corelib/global/qtendian/qtendian.pro +++ b/tests/auto/corelib/global/qtendian/qtendian.pro @@ -1,4 +1,4 @@ CONFIG += testcase TARGET = tst_qtendian -QT = core testlib +QT = core core-private testlib SOURCES = tst_qtendian.cpp diff --git a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp index 915e40954b..6934818dcf 100644 --- a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp +++ b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp @@ -29,6 +29,7 @@ #include <QtTest/QtTest> #include <QtCore/qendian.h> +#include <QtCore/private/qendian_p.h> class tst_QtEndian: public QObject @@ -41,6 +42,11 @@ private slots: void toBigEndian(); void toLittleEndian(); + + void endianIntegers_data(); + void endianIntegers(); + + void endianBitfields(); }; struct TestData @@ -129,5 +135,83 @@ void tst_QtEndian::toLittleEndian() #undef ENDIAN_TEST +void tst_QtEndian::endianIntegers_data() +{ + QTest::addColumn<int>("val"); + + QTest::newRow("-30000") << -30000; + QTest::newRow("-1") << -1; + QTest::newRow("0") << 0; + QTest::newRow("1020") << 1020; + QTest::newRow("16385") << 16385; +} + +void tst_QtEndian::endianIntegers() +{ + QFETCH(int, val); + + qint16 vi16 = val; + qint32 vi32 = val; + qint64 vi64 = val; + quint16 vu16 = val; + quint32 vu32 = val; + quint64 vu64 = val; + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), vi16); + QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), vi32); + QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), vi64); + QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), qbswap(vi16)); + QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), qbswap(vi32)); + QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), qbswap(vi64)); + QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), vu16); + QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), vu32); + QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), vu64); + QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), qbswap(vu16)); + QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), qbswap(vu32)); + QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), qbswap(vu64)); +#else + QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), qbswap(vi16)); + QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), qbswap(vi32)); + QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), qbswap(vi64)); + QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), vi16); + QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), vi32); + QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), vi64); + QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), qbswap(vu16)); + QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), qbswap(vu32)); + QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), qbswap(vu64)); + QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), vu16); + QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), vu32); + QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), vu64); +#endif +} + +void tst_QtEndian::endianBitfields() +{ + union { + quint32_be_bitfield<21, 11> upper; + quint32_be_bitfield<10, 11> lower; + qint32_be_bitfield<0, 10> bottom; + } u; + + u.upper = 200; + QCOMPARE(u.upper, 200U); + u.lower = 1000; + u.bottom = -8; + QCOMPARE(u.lower, 1000U); + QCOMPARE(u.upper, 200U); + + u.lower += u.upper; + QCOMPARE(u.upper, 200U); + QCOMPARE(u.lower, 1200U); + + u.upper = 65536 + 7; + u.lower = 65535; + QCOMPARE(u.lower, 65535U & ((1<<11) - 1)); + QCOMPARE(u.upper, 7U); + + QCOMPARE(u.bottom, -8); +} + QTEST_MAIN(tst_QtEndian) #include "tst_qtendian.moc" diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro index 01ed84fda9..bd2a9f4c8e 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -34,12 +34,6 @@ SUBDIRS=\ qurl \ qurlinternal \ qurlquery \ - qwinoverlappedionotifier \ - -!win32 { - SUBDIRS -=\ - qwinoverlappedionotifier -} !qtHaveModule(gui): SUBDIRS -= \ qdatastream \ @@ -73,5 +67,4 @@ win32:!qtConfig(private_tests): SUBDIRS -= \ qprocess-noapplication winrt: SUBDIRS -= \ - qstorageinfo \ - qwinoverlappedionotifier + qstorageinfo diff --git a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp index dba920d1f7..4e5059c1a2 100644 --- a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp +++ b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp @@ -353,6 +353,13 @@ public: return QDateTime(); } + bool setFileTime(const QDateTime &newDate, FileTime time) + { + Q_UNUSED(newDate); + Q_UNUSED(time); + return false; + } + void setFileName(const QString &file) { if (openForRead_ || openForWrite_) diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index 7147405f3b..b43ea7cfa5 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -51,6 +51,7 @@ private slots: void qDebugQChar() const; void qDebugQString() const; void qDebugQStringRef() const; + void qDebugQStringView() const; void qDebugQLatin1String() const; void qDebugQByteArray() const; void qDebugQFlags() const; @@ -492,6 +493,46 @@ void tst_QDebug::qDebugQStringRef() const } } +void tst_QDebug::qDebugQStringView() const +{ + /* Use a basic string. */ + { + QLatin1String file, function; + int line = 0; + const QStringView inView = QStringViewLiteral("input"); + + MessageHandlerSetter mhs(myMessageHandler); + { qDebug() << inView; } +#ifndef QT_NO_MESSAGELOGCONTEXT + file = QLatin1String(__FILE__); line = __LINE__ - 2; function = QLatin1String(Q_FUNC_INFO); +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, QLatin1String("\"input\"")); + QCOMPARE(QLatin1String(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QLatin1String(s_function), function); + } + + /* Use a null QStringView. */ + { + QString file, function; + int line = 0; + + const QStringView inView; + + MessageHandlerSetter mhs(myMessageHandler); + { qDebug() << inView; } +#ifndef QT_NO_MESSAGELOGCONTEXT + file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, QLatin1String("\"\"")); + QCOMPARE(QLatin1String(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QLatin1String(s_function), function); + } +} + void tst_QDebug::qDebugQLatin1String() const { QString file, function; diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index c7216dc169..17497e46b9 100644 --- a/tests/auto/corelib/io/qfile/tst_qfile.cpp +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -2087,6 +2087,7 @@ 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; } private: int number; diff --git a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp index b0acb1c58d..b78fa29fb6 100644 --- a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp +++ b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp @@ -64,7 +64,7 @@ void tst_QNoDebug::noDebugOutput() const void tst_QNoDebug::streaming() const { QDateTime dt(QDate(1,2,3),QTime(4,5,6)); - const QByteArray debugString = dt.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1(); + const QByteArray debugString = dt.toString(QStringViewLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1(); const QByteArray message = "QDateTime(" + debugString + " Qt::TimeSpec(LocalTime))"; QTest::ignoreMessage(QtWarningMsg, message.constData()); qWarning() << dt; diff --git a/tests/auto/corelib/io/qprocess/testDetached/main.cpp b/tests/auto/corelib/io/qprocess/testDetached/main.cpp index 702cabe873..c10e32d584 100644 --- a/tests/auto/corelib/io/qprocess/testDetached/main.cpp +++ b/tests/auto/corelib/io/qprocess/testDetached/main.cpp @@ -40,32 +40,89 @@ #include <windows.h> #endif +static void writeStuff(QFile &f) +{ + f.write(QDir::currentPath().toUtf8()); + f.putChar('\n'); +#if defined(Q_OS_UNIX) + f.write(QByteArray::number(quint64(getpid()))); +#elif defined(Q_OS_WIN) + f.write(QByteArray::number(quint64(GetCurrentProcessId()))); +#endif + f.putChar('\n'); + f.write(qgetenv("tst_QProcess")); + f.putChar('\n'); +} + +struct Args +{ + int exitCode = 0; + QByteArray errorMessage; + QString fileName; + FILE *channel = nullptr; + QByteArray channelName; +}; + +static Args parseArguments(const QStringList &args) +{ + Args result; + if (args.count() < 2) { + result.exitCode = 128; + result.errorMessage = "Usage: testDetached [--out-channel={stdout|stderr}] filename.txt\n"; + return result; + } + for (const QString &arg : args) { + if (arg.startsWith("--")) { + if (!arg.startsWith("--out-channel=")) { + result.exitCode = 2; + result.errorMessage = "Unknown argument " + arg.toLocal8Bit(); + return result; + } + result.channelName = arg.mid(14).toLocal8Bit(); + if (result.channelName == "stdout") { + result.channel = stdout; + } else if (result.channelName == "stderr") { + result.channel = stderr; + } else { + result.exitCode = 3; + result.errorMessage = "Unknown channel " + result.channelName; + return result; + } + } else { + result.fileName = arg; + } + } + return result; +} + int main(int argc, char **argv) { QCoreApplication app(argc, argv); - QStringList args = app.arguments(); - if (args.count() != 2) { - fprintf(stderr, "Usage: testDetached filename.txt\n"); - return 128; + const Args args = parseArguments(app.arguments()); + if (args.exitCode) { + fprintf(stderr, "testDetached: %s\n", args.errorMessage.constData()); + return args.exitCode; } - QFile f(args.at(1)); + if (args.channel) { + QFile channel; + if (!channel.open(args.channel, QIODevice::WriteOnly | QIODevice::Text)) { + fprintf(stderr, "Cannot open channel %s for writing: %s\n", + qPrintable(args.channelName), qPrintable(channel.errorString())); + return 4; + } + writeStuff(channel); + } + + QFile f(args.fileName); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { fprintf(stderr, "Cannot open %s for writing: %s\n", qPrintable(f.fileName()), qPrintable(f.errorString())); return 1; } - f.write(QDir::currentPath().toUtf8()); - f.putChar('\n'); -#if defined(Q_OS_UNIX) - f.write(QByteArray::number(quint64(getpid()))); -#elif defined(Q_OS_WIN) - f.write(QByteArray::number(quint64(GetCurrentProcessId()))); -#endif - f.putChar('\n'); - + writeStuff(f); f.close(); return 0; diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index f4d6d5cb40..de6eb28503 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -127,7 +127,8 @@ private slots: void systemEnvironment(); void lockupsInStartDetached(); void waitForReadyReadForNonexistantProcess(); - void detachedWorkingDirectoryAndPid(); + void detachedProcessParameters_data(); + void detachedProcessParameters(); void startFinishStartFinish(); void invalidProgramString_data(); void invalidProgramString(); @@ -2072,21 +2073,54 @@ void tst_QProcess::fileWriterProcess() } while (stopWatch.elapsed() < 3000); } -void tst_QProcess::detachedWorkingDirectoryAndPid() +void tst_QProcess::detachedProcessParameters_data() { + QTest::addColumn<QString>("outChannel"); + QTest::newRow("none") << QString(); + QTest::newRow("stdout") << QString("stdout"); + QTest::newRow("stderr") << QString("stderr"); +} + +void tst_QProcess::detachedProcessParameters() +{ + QFETCH(QString, outChannel); qint64 pid; QFile infoFile(m_temporaryDir.path() + QLatin1String("/detachedinfo.txt")); if (infoFile.exists()) QVERIFY(infoFile.remove()); + QFile channelFile(m_temporaryDir.path() + QLatin1String("detachedinfo2.txt")); + if (channelFile.exists()) + QVERIFY(channelFile.remove()); QString workingDir = QDir::currentPath() + "/testDetached"; QVERIFY(QFile::exists(workingDir)); - QStringList args; - args << infoFile.fileName(); - QVERIFY(QProcess::startDetached(QDir::currentPath() + QLatin1String("/testDetached/testDetached"), args, workingDir, &pid)); + QVERIFY(qgetenv("tst_QProcess").isEmpty()); + QByteArray envVarValue("foobarbaz"); + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + environment.insert(QStringLiteral("tst_QProcess"), QString::fromUtf8(envVarValue)); + + QProcess process; + process.setProgram(QDir::currentPath() + QLatin1String("/testDetached/testDetached")); +#ifdef Q_OS_WIN + int modifierCalls = 0; + process.setCreateProcessArgumentsModifier( + [&modifierCalls] (QProcess::CreateProcessArguments *) { modifierCalls++; }); +#endif + QStringList args(infoFile.fileName()); + if (!outChannel.isEmpty()) { + args << QStringLiteral("--out-channel=") + outChannel; + if (outChannel == "stdout") + process.setStandardOutputFile(channelFile.fileName()); + else if (outChannel == "stderr") + process.setStandardErrorFile(channelFile.fileName()); + } + process.setArguments(args); + process.setWorkingDirectory(workingDir); + process.setProcessEnvironment(environment); + QVERIFY(process.startDetached(&pid)); QFileInfo fi(infoFile); fi.setCaching(false); @@ -2097,19 +2131,35 @@ void tst_QProcess::detachedWorkingDirectoryAndPid() } QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text)); - QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()); - actualWorkingDir.chop(1); // strip off newline - QByteArray processIdString = infoFile.readLine(); - processIdString.chop(1); + QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()).trimmed(); + QByteArray processIdString = infoFile.readLine().trimmed(); + QByteArray actualEnvVarValue = infoFile.readLine().trimmed(); + QByteArray infoFileContent; + if (!outChannel.isEmpty()) { + infoFile.seek(0); + infoFileContent = infoFile.readAll(); + } infoFile.close(); infoFile.remove(); + if (!outChannel.isEmpty()) { + QVERIFY(channelFile.open(QIODevice::ReadOnly | QIODevice::Text)); + QByteArray channelContent = channelFile.readAll(); + channelFile.close(); + channelFile.remove(); + QCOMPARE(channelContent, infoFileContent); + } + bool ok = false; qint64 actualPid = processIdString.toLongLong(&ok); QVERIFY(ok); QCOMPARE(actualWorkingDir, workingDir); QCOMPARE(actualPid, pid); + QCOMPARE(actualEnvVarValue, envVarValue); +#ifdef Q_OS_WIN + QCOMPARE(modifierCalls, 1); +#endif } void tst_QProcess::switchReadChannels() diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp index 59cd3a8411..17bcde4992 100644 --- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp +++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp @@ -866,8 +866,6 @@ void tst_QTemporaryFile::guaranteeUnique() // First pass. See which filename QTemporaryFile will try first. { - // Fix the random seed. - qsrand(1135); QTemporaryFile tmpFile("testFile1.XXXXXX"); tmpFile.open(); takenFileName = tmpFile.fileName(); @@ -881,8 +879,6 @@ void tst_QTemporaryFile::guaranteeUnique() // Second pass, now we have blocked its first attempt with a directory. { - // Fix the random seed. - qsrand(1135); QTemporaryFile tmpFile("testFile1.XXXXXX"); QVERIFY(tmpFile.open()); QString uniqueFileName = tmpFile.fileName(); diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index c5647752fd..7eeaea564f 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -1384,18 +1384,6 @@ void tst_QUrl::compat_constructor_01_data() void tst_QUrl::compat_constructor_01() { - /* The following should work as expected: - * - * QUrlOperator op; - * op.copy( QString( "Makefile" ), - * QString("ftp://rms:grmpf12@nibbler/home/rms/tmp"), - * false ); - * - * as well as the following: - * - * QUrlOperator op; - * op.copy(QString("ftp://ftp.qt-project.org/qt/INSTALL"), "."); - */ QFETCH( QString, urlStr ); { @@ -1425,11 +1413,6 @@ void tst_QUrl::compat_constructor_02_data() void tst_QUrl::compat_constructor_02() { - /* The following should work as expected: - * - * QUrlOperator op( "ftp://ftp.qt-project.org/qt" ); - * op.copy(QString("INSTALL"), "."); - */ QFETCH( QString, urlStr ); QFETCH( QString, fileName ); diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro b/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro deleted file mode 100644 index 4f0e9da3c2..0000000000 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qwinoverlappedionotifier -QT = core-private testlib -SOURCES = tst_qwinoverlappedionotifier.cpp diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp b/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp deleted file mode 100644 index 7034c2c9fd..0000000000 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include <private/qwinoverlappedionotifier_p.h> -#include <qbytearray.h> -#include <qt_windows.h> - -#ifndef PIPE_REJECT_REMOTE_CLIENTS -#define PIPE_REJECT_REMOTE_CLIENTS 0x08 -#endif - -class tst_QWinOverlappedIoNotifier : public QObject -{ - Q_OBJECT - -private slots: - void initTestCase(); - void readFile_data(); - void readFile(); - void waitForNotified_data(); - void waitForNotified(); - void waitForAnyNotified(); - void brokenPipe(); - void multipleOperations(); - -private: - QFileInfo sourceFileInfo; - DWORD notifiedBytesRead; - DWORD notifiedErrorCode; -}; - -class NotifierSink : public QObject -{ - Q_OBJECT -public: - NotifierSink(QWinOverlappedIoNotifier *notifier) - : QObject(notifier), - threshold(1) - { - connect(notifier, &QWinOverlappedIoNotifier::notified, this, &NotifierSink::notified); - } - -protected slots: - void notified(DWORD bytesRead, DWORD errorCode, OVERLAPPED *overlapped) - { - IOResult ioResult; - ioResult.bytes = bytesRead; - ioResult.errorCode = errorCode; - ioResult.overlapped = overlapped; - notifications.append(ioResult); - if (notifications.count() >= threshold) - emit notificationReceived(); - } - -signals: - void notificationReceived(); - -public: - int threshold; - - struct IOResult - { - IOResult() - : bytes(0), errorCode(ERROR_SUCCESS), overlapped(0) - {} - DWORD bytes; - DWORD errorCode; - OVERLAPPED *overlapped; - }; - - QList<IOResult> notifications; -}; - -void tst_QWinOverlappedIoNotifier::initTestCase() -{ - sourceFileInfo.setFile(QFINDTESTDATA("tst_qwinoverlappedionotifier.cpp")); - QVERIFY2(sourceFileInfo.exists(), "File tst_qwinoverlappedionotifier.cpp not found."); -} - -void tst_QWinOverlappedIoNotifier::readFile_data() -{ - QTest::addColumn<QString>("fileName"); - QTest::addColumn<int>("readBufferSize"); - QTest::addColumn<DWORD>("expectedBytesRead"); - - QString sourceFileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath()); - int sourceFileSize = sourceFileInfo.size(); - - QTest::newRow("read file, less than available") - << sourceFileName << sourceFileSize / 2 << DWORD(sourceFileSize / 2); - QTest::newRow("read file, more than available") - << sourceFileName << sourceFileSize * 2 << DWORD(sourceFileSize); -} - -void tst_QWinOverlappedIoNotifier::readFile() -{ - QFETCH(QString, fileName); - QFETCH(int, readBufferSize); - QFETCH(DWORD, expectedBytesRead); - - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QTestEventLoop::instance().enterLoop(3); - CloseHandle(hFile); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, expectedBytesRead); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); -} - -void tst_QWinOverlappedIoNotifier::waitForNotified_data() -{ - readFile_data(); -} - -void tst_QWinOverlappedIoNotifier::waitForNotified() -{ - QFETCH(QString, fileName); - QFETCH(int, readBufferSize); - QFETCH(DWORD, expectedBytesRead); - - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - QCOMPARE(notifier.waitForNotified(100, 0), false); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QCOMPARE(notifier.waitForNotified(3000, &overlapped), true); - CloseHandle(hFile); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, expectedBytesRead); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); - QCOMPARE(notifier.waitForNotified(100, &overlapped), false); -} - -void tst_QWinOverlappedIoNotifier::waitForAnyNotified() -{ - const QString fileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath()); - const int readBufferSize = sourceFileInfo.size(); - - QWinOverlappedIoNotifier notifier; - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - QVERIFY(!notifier.waitForAnyNotified(100)); - - OVERLAPPED overlapped1; - ZeroMemory(&overlapped1, sizeof(OVERLAPPED)); - QByteArray buffer1(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer1.data(), buffer1.size(), NULL, &overlapped1); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - OVERLAPPED overlapped2; - ZeroMemory(&overlapped2, sizeof(OVERLAPPED)); - QByteArray buffer2(readBufferSize, 0); - readSuccess = ReadFile(hFile, buffer2.data(), buffer2.size(), NULL, &overlapped2); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QSet<OVERLAPPED *> overlappedObjects; - overlappedObjects << &overlapped1 << &overlapped2; - - for (int i = 1; i <= 2; ++i) { - OVERLAPPED *notifiedOverlapped = notifier.waitForAnyNotified(3000); - QVERIFY(overlappedObjects.contains(notifiedOverlapped)); - overlappedObjects.remove(notifiedOverlapped); - } - - CloseHandle(hFile); - QVERIFY(overlappedObjects.isEmpty()); - QVERIFY(!notifier.waitForAnyNotified(100)); -} - -void tst_QWinOverlappedIoNotifier::brokenPipe() -{ - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_brokenPipe"; - HANDLE hPipe = CreateNamedPipe(pipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 0, 0, NULL); - QVERIFY(hPipe != INVALID_HANDLE_VALUE); - HANDLE hReadEnd = CreateFile(pipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - QVERIFY(hReadEnd != INVALID_HANDLE_VALUE); - notifier.setHandle(hReadEnd); - notifier.setEnabled(true); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(1024, 0); - BOOL readSuccess = ReadFile(hReadEnd, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - // close the write end of the pipe - CloseHandle(hPipe); - - QTestEventLoop::instance().enterLoop(3); - CloseHandle(hReadEnd); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, DWORD(0)); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_BROKEN_PIPE)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); -} - -void tst_QWinOverlappedIoNotifier::multipleOperations() -{ - QWinOverlappedIoNotifier clientNotifier; - NotifierSink sink(&clientNotifier); - sink.threshold = 2; - connect(&sink, &NotifierSink::notificationReceived, - &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_multipleOperations"; - HANDLE hServer = CreateNamedPipe(pipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 0, 0, NULL); - QVERIFY(hServer != INVALID_HANDLE_VALUE); - HANDLE hClient = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - QVERIFY(hClient != INVALID_HANDLE_VALUE); - clientNotifier.setHandle(hClient); - clientNotifier.setEnabled(true); - - // start async read on client - QByteArray clientReadBuffer(377, Qt::Uninitialized); - OVERLAPPED clientReadOverlapped; - ZeroMemory(&clientReadOverlapped, sizeof(clientReadOverlapped)); - BOOL readSuccess = ReadFile(hClient, clientReadBuffer.data(), clientReadBuffer.size(), - NULL, &clientReadOverlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - // start async write client -> server - QByteArray clientDataToWrite(233, 'B'); - OVERLAPPED clientWriteOverlapped; - ZeroMemory(&clientWriteOverlapped, sizeof(clientWriteOverlapped)); - BOOL writeSuccess = WriteFile(hClient, clientDataToWrite.data(), clientDataToWrite.size(), - NULL, &clientWriteOverlapped); - QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING); - - // start async write server -> client - QByteArray serverDataToWrite(144, 'A'); - OVERLAPPED serverOverlapped; - ZeroMemory(&serverOverlapped, sizeof(serverOverlapped)); - writeSuccess = WriteFile(hServer, serverDataToWrite.data(), serverDataToWrite.size(), - NULL, &serverOverlapped); - QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING); - - // read synchronously on server to complete the client -> server write - QByteArray serverReadBuffer(610, Qt::Uninitialized); - DWORD dwBytesRead = 0; - readSuccess = ReadFile(hServer, serverReadBuffer.data(), serverReadBuffer.size(), - &dwBytesRead, NULL); - QVERIFY(readSuccess); - QCOMPARE(int(dwBytesRead), clientDataToWrite.size()); - serverReadBuffer.resize(dwBytesRead); - QCOMPARE(serverReadBuffer, clientDataToWrite); - - QTestEventLoop::instance().enterLoop(3); - QTRY_COMPARE(sink.notifications.count(), 2); - foreach (const NotifierSink::IOResult &r, sink.notifications) { - QCOMPARE(r.errorCode, DWORD(ERROR_SUCCESS)); - if (r.bytes == DWORD(serverDataToWrite.count())) - QCOMPARE(r.overlapped, &clientReadOverlapped); - else if (r.bytes == DWORD(clientDataToWrite.count())) - QCOMPARE(r.overlapped, &clientWriteOverlapped); - else - QVERIFY2(false, "Unexpected number of bytes received."); - } - - CloseHandle(hClient); - CloseHandle(hServer); -} - -QTEST_MAIN(tst_QWinOverlappedIoNotifier) - -#include "tst_qwinoverlappedionotifier.moc" diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro index c1d75cc2cb..a09f03a7b4 100644 --- a/tests/auto/corelib/itemmodels/itemmodels.pro +++ b/tests/auto/corelib/itemmodels/itemmodels.pro @@ -7,6 +7,7 @@ qtHaveModule(gui): SUBDIRS += \ qabstractproxymodel \ qidentityproxymodel \ qitemselectionmodel \ + qsortfilterproxymodel_recursive \ qtHaveModule(widgets) { SUBDIRS += \ diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp index bc8f0c1c51..383bbbd3f8 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp @@ -94,6 +94,7 @@ private slots: void changeSourceDataKeepsStableSorting_qtbug1548(); void changeSourceDataForwardsRoles_qtbug35440(); void resortingDoesNotBreakTreeModels(); + void dynamicFilterWithoutSort(); void sortFilterRole(); void selectionFilteredOut(); void match_data(); @@ -148,6 +149,9 @@ private slots: void sourceLayoutChangeLeavesValidPersistentIndexes(); void rowMoveLeavesValidPersistentIndexes(); + void emitLayoutChangedOnlyIfSortingChanged_data(); + void emitLayoutChangedOnlyIfSortingChanged(); + protected: void buildHierarchy(const QStringList &data, QAbstractItemModel *model); void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); @@ -2057,8 +2061,6 @@ static void checkSortedTableModel(const QAbstractItemModel *model, const QString void tst_QSortFilterProxyModel::changeSourceDataKeepsStableSorting_qtbug1548() { - QSKIP("This test will fail, see QTBUG-1548"); - // Check that emitting dataChanged from the source model // for a change of a role which is not the sorting role // doesn't alter the sorting. In this case, we sort on the DisplayRole, @@ -3568,6 +3570,13 @@ void tst_QSortFilterProxyModel::testParentLayoutChanged() parentItem = item; } } + // item 0 + // item 10 + // - item 1 + // - item 11 + // - item 2 + // - item 12 + // ... QSortFilterProxyModel proxy; proxy.sort(0, Qt::AscendingOrder); @@ -3609,11 +3618,12 @@ void tst_QSortFilterProxyModel::testParentLayoutChanged() QVERIFY(proxy2ParentsChangedSpy.isValid()); QStandardItem *item = model.invisibleRootItem()->child(1)->child(1); + QCOMPARE(item->text(), QStringLiteral("item 11")); // Ensure mapped: proxy.mapFromSource(model.indexFromItem(item)); - item->setData("Changed"); + item->setText("Changed"); QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); @@ -4026,7 +4036,7 @@ public: } - QModelIndex mapToSource(const QModelIndex &proxyIndex) const + QModelIndex mapToSource(const QModelIndex &proxyIndex) const override { Q_ASSERT(sourceModel()); return QSortFilterProxyModel::mapToSource(proxyIndex); @@ -4353,5 +4363,152 @@ void tst_QSortFilterProxyModel::rowMoveLeavesValidPersistentIndexes() QVERIFY(persistentIndex.parent().isValid()); } +void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged_data() +{ + QTest::addColumn<int>("changedRow"); + QTest::addColumn<Qt::ItemDataRole>("changedRole"); + QTest::addColumn<QString>("newData"); + QTest::addColumn<QString>("expectedSourceRowTexts"); + QTest::addColumn<QString>("expectedProxyRowTexts"); + QTest::addColumn<int>("expectedLayoutChanged"); + + // Starting point: + // a source model with 8,7,6,5,4,3,2,1 + // a proxy model keeping only even rows and sorting them, therefore showing 2,4,6,8 + + // When setData changes ordering, layoutChanged should be emitted + QTest::newRow("ordering_change") << 0 << Qt::DisplayRole << "0" << "07654321" << "0246" << 1; + + // When setData on visible row doesn't change ordering, layoutChanged should not be emitted + QTest::newRow("no_ordering_change") << 6 << Qt::DisplayRole << "0" << "87654301" << "0468" << 0; + + // When setData happens on a filtered out row, layoutChanged should not be emitted + QTest::newRow("filtered_out") << 1 << Qt::DisplayRole << "9" << "89654321" << "2468" << 0; + + // When setData makes a row visible, layoutChanged should not be emitted (rowsInserted is emitted instead) + QTest::newRow("make_row_visible") << 7 << Qt::DisplayRole << "0" << "87654320" << "02468" << 0; + + // When setData makes a row hidden, layoutChanged should not be emitted (rowsRemoved is emitted instead) + QTest::newRow("make_row_hidden") << 4 << Qt::DisplayRole << "1" << "87651321" << "268" << 0; + + // When setData happens on an unrelated role, layoutChanged should not be emitted + QTest::newRow("unrelated_role") << 0 << Qt::DecorationRole << "" << "87654321" << "2468" << 0; + + // When many changes happen together... and trigger removal, insertion, and layoutChanged + QTest::newRow("many_changes") << -1 << Qt::DisplayRole << "3,4,2,5,6,0,7,9" << "34256079" << "0246" << 1; + + // When many changes happen together... and trigger removal, insertion, but no change in ordering of visible rows => no layoutChanged + QTest::newRow("many_changes_no_layoutChanged") << -1 << Qt::DisplayRole << "7,5,4,3,2,1,0,8" << "75432108" << "0248" << 0; +} + +void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged() +{ + QFETCH(int, changedRow); + QFETCH(QString, newData); + QFETCH(Qt::ItemDataRole, changedRole); + QFETCH(QString, expectedSourceRowTexts); + 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) + strings.append(QString::number(i)); + model.replaceData(strings); + QCOMPARE(rowTexts(&model), QStringLiteral("87654321")); + + class FilterEvenRowsProxyModel : public QSortFilterProxyModel + { + public: + bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const override + { + return sourceModel()->index(srcRow, 0, srcParent).data().toInt() % 2 == 0; + } + }; + + FilterEvenRowsProxyModel proxy; + proxy.sort(0); + proxy.setSourceModel(&model); + QCOMPARE(rowTexts(&proxy), QStringLiteral("2468")); + + QSignalSpy modelDataChangedSpy(&model, &QAbstractItemModel::dataChanged); + QSignalSpy proxyLayoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged); + + if (changedRole == Qt::DecorationRole) + model.emitDecorationChangedSignal(); + else if (changedRow == -1) + model.replaceData(newData.split(QLatin1Char(','))); + else + model.setData(model.index(changedRow, 0), newData, changedRole); + + QCOMPARE(rowTexts(&model), expectedSourceRowTexts); + QCOMPARE(rowTexts(&proxy), expectedProxyRowTexts); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyLayoutChangedSpy.size(), expectedLayoutChanged); +} + +void tst_QSortFilterProxyModel::dynamicFilterWithoutSort() +{ + QStringListModel model; + const QStringList initial = QString("bravo charlie delta echo").split(QLatin1Char(' ')); + model.setStringList(initial); + QSortFilterProxyModel proxy; + proxy.setDynamicSortFilter(true); + proxy.setSourceModel(&model); + + QSignalSpy layoutChangeSpy(&proxy, &QAbstractItemModel::layoutChanged); + QSignalSpy resetSpy(&proxy, &QAbstractItemModel::modelReset); + + QVERIFY(layoutChangeSpy.isValid()); + QVERIFY(resetSpy.isValid()); + + model.setStringList(QStringList() << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday"); + + QVERIFY(layoutChangeSpy.isEmpty()); + + QCOMPARE(model.stringList(), QStringList() << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday"); + + QCOMPARE(resetSpy.count(), 1); +} + QTEST_MAIN(tst_QSortFilterProxyModel) #include "tst_qsortfilterproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore new file mode 100644 index 0000000000..2007aaabbd --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore @@ -0,0 +1 @@ +tst_qsortfilterproxymodel_recursive diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro new file mode 100644 index 0000000000..a8b793dbc6 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +CONFIG += parallel_test +TARGET = tst_qsortfilterproxymodel_recursive + +QT += testlib + +SOURCES += tst_qsortfilterproxymodel_recursive.cpp +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp new file mode 100644 index 0000000000..54c79e0893 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp @@ -0,0 +1,727 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, authors Filipe Azevedo <filipe.azevedo@kdab.com> and David Faure <david.faure@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QTest> +#include <QSignalSpy> + +#include <QtCore/QSortFilterProxyModel> +#include <QtGui/QStandardItem> + +Q_DECLARE_METATYPE(QModelIndex) + +class ModelSignalSpy : public QObject { + Q_OBJECT +public: + explicit ModelSignalSpy(QAbstractItemModel &model) { + connect(&model, &QAbstractItemModel::rowsInserted, this, &ModelSignalSpy::onRowsInserted); + connect(&model, &QAbstractItemModel::rowsRemoved, this, &ModelSignalSpy::onRowsRemoved); + connect(&model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelSignalSpy::onRowsAboutToBeInserted); + connect(&model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelSignalSpy::onRowsAboutToBeRemoved); + connect(&model, &QAbstractItemModel::rowsMoved, this, &ModelSignalSpy::onRowsMoved); + connect(&model, &QAbstractItemModel::dataChanged, this, &ModelSignalSpy::onDataChanged); + connect(&model, &QAbstractItemModel::layoutChanged, this, &ModelSignalSpy::onLayoutChanged); + connect(&model, &QAbstractItemModel::modelReset, this, &ModelSignalSpy::onModelReset); + } + + QStringList mSignals; + +private Q_SLOTS: + void onRowsInserted(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsInserted(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsRemoved(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsRemoved(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsAboutToBeInserted(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsAboutToBeInserted(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsAboutToBeRemoved(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsAboutToBeRemoved(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsMoved(QModelIndex,int,int,QModelIndex,int) { + mSignals << QStringLiteral("rowsMoved"); + } + void onDataChanged(const QModelIndex &from, const QModelIndex& ) { + mSignals << QStringLiteral("dataChanged(%1)").arg(from.data().toString()); + } + void onLayoutChanged() { + mSignals << QStringLiteral("layoutChanged"); + } + void onModelReset() { + mSignals << QStringLiteral("modelReset"); + } +private: + QString textForRowSpy(const QModelIndex &parent, int start, int end) + { + QString txt = parent.data().toString(); + if (!txt.isEmpty()) + txt += QLatin1Char('.'); + txt += QString::number(start+1); + if (start != end) + txt += QLatin1Char('-') + QString::number(end+1); + return txt; + } +}; + +class TestModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + TestModel(QAbstractItemModel *sourceModel) + : QSortFilterProxyModel() + { + setRecursiveFiltering(true); + setSourceModel(sourceModel); + } + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override + { + return sourceModel()->index(sourceRow, 0, sourceParent).data(Qt::UserRole +1).toBool() + && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); + } +}; + +// Represents this tree +// - A +// - - B +// - - - C +// - - - D +// - - E +// as a single string, englobing children in brackets, like this: +// [A[B[C D] E]] +// In addition, items that match the filtering (data(UserRole+1) == true) have a * after their value. +static QString treeAsString(const QAbstractItemModel &model, const QModelIndex &parent = QModelIndex()) +{ + QString ret; + const int rowCount = model.rowCount(parent); + if (rowCount > 0) { + ret += QLatin1Char('['); + for (int row = 0 ; row < rowCount; ++row) { + if (row > 0) { + ret += ' '; + } + const QModelIndex child = model.index(row, 0, parent); + ret += child.data().toString(); + if (child.data(Qt::UserRole+1).toBool()) + ret += QLatin1Char('*'); + ret += treeAsString(model, child); + } + ret += QLatin1Char(']'); + } + return ret; +} + +// Fill a tree model based on a string representation (see treeAsString) +static void fillModel(QStandardItemModel &model, const QString &str) +{ + QCOMPARE(str.count('['), str.count(']')); + QStandardItem *item = 0; + QString data; + for ( int i = 0 ; i < str.length() ; ++i ) { + const QChar ch = str.at(i); + if ((ch == '[' || ch == ']' || ch == ' ') && !data.isEmpty()) { + if (data.endsWith('*')) { + item->setData(true, Qt::UserRole + 1); + data.chop(1); + } + item->setText(data); + data.clear(); + } + if (ch == '[') { + // Create new child + QStandardItem *child = new QStandardItem; + if (item) + item->appendRow(child); + else + model.appendRow(child); + item = child; + } else if (ch == ']') { + // Go up to parent + item = item->parent(); + } else if (ch == ' ') { + // Create new sibling + QStandardItem *child = new QStandardItem; + QStandardItem *parent = item->parent(); + if (parent) + parent->appendRow(child); + else + model.appendRow(child); + item = child; + } else { + data += ch; + } + } +} + +class tst_QSortFilterProxyModel_Recursive : public QObject +{ + Q_OBJECT +private: +private Q_SLOTS: + void testInitialFiltering_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("proxyStr"); + + QTest::newRow("empty") << "[]" << ""; + QTest::newRow("no") << "[1]" << ""; + QTest::newRow("yes") << "[1*]" << "[1*]"; + QTest::newRow("second") << "[1 2*]" << "[2*]"; + QTest::newRow("child_yes") << "[1 2[2.1*]]" << "[2[2.1*]]"; + QTest::newRow("grandchild_yes") << "[1 2[2.1[2.1.1*]]]" << "[2[2.1[2.1.1*]]]"; + // 1, 3.1 and 4.2.1 match, so their parents are in the model + QTest::newRow("more") << "[1* 2[2.1] 3[3.1*] 4[4.1 4.2[4.2.1*]]]" << "[1* 3[3.1*] 4[4.2[4.2.1*]]]"; + } + + void testInitialFiltering() + { + QFETCH(QString, sourceStr); + QFETCH(QString, proxyStr); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), proxyStr); + } + + // Test changing a role that is unrelated to the filtering. + void testUnrelatedDataChange() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1*]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), sourceStr); + + ModelSignalSpy spy(proxy); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + + // When changing the text on the item + item_1_1_1->setText(QStringLiteral("ME")); + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[ME*]]]")); + + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("dataChanged(ME)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + } + + // Test changing a role that is unrelated to the filtering, in a hidden item. + void testHiddenDataChange() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + + // When changing the text on a hidden item + item_1_1_1->setText(QStringLiteral("ME")); + + QCOMPARE(treeAsString(proxy), QString()); + QCOMPARE(spy.mSignals, QStringList()); + } + + // Test that we properly react to a data-changed signal in a descendant and include all required rows + void testDataChangeIn_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("add"); // set the flag on this item + QTest::addColumn<QString>("expectedProxyStr"); + QTest::addColumn<QStringList>("expectedSignals"); + + QTest::newRow("toplevel") << "[1]" << "" << "1" << "[1*]" + << (QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") << QStringLiteral("rowsInserted(1)")); + QTest::newRow("show_parents") << "[1[1.1[1.1.1]]]" << "" << "1.1.1" << "[1[1.1[1.1.1*]]]" + << (QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") << QStringLiteral("rowsInserted(1)")); + + const QStringList insert_1_1_1 = QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1.1)") + << QStringLiteral("rowsInserted(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)") + ; + QTest::newRow("parent_visible") << "[1[1.1*[1.1.1]]]" << "[1[1.1*]]" << "1.1.1" << "[1[1.1*[1.1.1*]]]" + << insert_1_1_1; + + QTest::newRow("sibling_visible") << "[1[1.1[1.1.1 1.1.2*]]]" << "[1[1.1[1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.1* 1.1.2*]]]" + << insert_1_1_1; + + QTest::newRow("visible_cousin") << "[1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" + << insert_1_1_1; + + QTest::newRow("show_parent") << "[1[1.1[1.1.1 1.1.2] 1.2*]]" << "[1[1.2*]]" << "1.1.1" << "[1[1.1[1.1.1*] 1.2*]]" + << (QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1)") + << QStringLiteral("rowsInserted(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("with_children") << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "1.1.1" << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" + << (QStringList() + << QStringLiteral("dataChanged(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + + } + + void testDataChangeIn() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, add); + QFETCH(QString, expectedProxyStr); + QFETCH(QStringList, expectedSignals); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + // When changing the data on the designated item to show this row + QStandardItem *itemToChange = itemByText(model, add); + QVERIFY(!itemToChange->data().toBool()); + itemToChange->setData(true); + + // The proxy should update as expected + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, expectedSignals); + } + + void testDataChangeOut_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("remove"); // unset the flag on this item + QTest::addColumn<QString>("expectedProxyStr"); + QTest::addColumn<QStringList>("expectedSignals"); + + const QStringList remove1_1_1 = (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("toplevel") << "[1*]" << "[1*]" << "1" << "" + << (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1)") << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("hide_parent") << "[1[1.1[1.1.1*]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" << + (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("parent_visible") << "[1[1.1*[1.1.1*]]]" << "[1[1.1*[1.1.1*]]]" << "1.1.1" << "[1[1.1*]]" + << remove1_1_1; + + QTest::newRow("visible") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.2*]]]" + << remove1_1_1; + QTest::newRow("visible_cousin") << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.2[1.1.2.1*]]]]" + << remove1_1_1; + + // The following tests trigger the removal of an ascendant. + QTest::newRow("remove_parent") << "[1[1.1[1.1.1* 1.1.2] 1.2*]]" << "[1[1.1[1.1.1*] 1.2*]]" << "1.1.1" << "[1[1.2*]]" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("with_children") << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" << "1.1.1" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" + << (QStringList() + << QStringLiteral("dataChanged(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("last_visible") << "[1[1.1[1.1.1* 1.1.2]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + } + + void testDataChangeOut() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, remove); + QFETCH(QString, expectedProxyStr); + QFETCH(QStringList, expectedSignals); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + + // When changing the data on the designated item to exclude this row again + QStandardItem *itemToChange = itemByText(model, remove); + QVERIFY(itemToChange->data().toBool()); + itemToChange->setData(false); + + // The proxy should update as expected + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, expectedSignals); + } + + void testInsert() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1_1->setData(true); + item_1_1_1->appendRow(item_1_1_1_1); + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*]]]]")); + + QCOMPARE(spy.mSignals, QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") + << QStringLiteral("rowsInserted(1)")); + } + + // Start from [1[1.1[1.1.1 1.1.2[1.1.2.1*]]]] + // where 1.1.1 is hidden but 1.1 is shown, we want to insert a shown child in 1.1.1. + // The proxy ensures dataChanged is called on 1.1, + // so that 1.1.1 and 1.1.1.1 are included in the model. + void testInsertCousin() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.2[1.1.2.1*]]]]")); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1_1->setData(true); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + item_1_1_1->appendRow(item_1_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*] 1.1.2[1.1.2.1*]]]]")); + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1.1)") + << QStringLiteral("rowsInserted(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + } + + void testInsertWithChildren() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1 = new QStandardItem(QStringLiteral("1.1.1")); + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1_1->setData(true); + item_1_1_1->appendRow(item_1_1_1_1); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->appendRow(item_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*]]]]")); + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("rowsAboutToBeInserted(1)") + << QStringLiteral("rowsInserted(1)")); + } + + void testInsertIntoVisibleWithChildren() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1*]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), sourceStr); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_2 = new QStandardItem(QStringLiteral("1.1.2")); + QStandardItem *item_1_1_2_1 = new QStandardItem(QStringLiteral("1.1.2.1")); + item_1_1_2_1->setData(true); + item_1_1_2->appendRow(item_1_1_2_1); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->appendRow(item_1_1_2); + } + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]")); + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1.2)") + << QStringLiteral("rowsInserted(1.1.2)")); + } + + void testInsertBefore() + { + QStandardItemModel model; + const QString sourceStr = "[1[1.1[1.1.2*]]]"; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), sourceStr); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1 = new QStandardItem("1.1.1"); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->insertRow(0, item_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QString("[1[1.1[1.1.2*]]]")); + QCOMPARE(spy.mSignals, QStringList()); + } + + void testInsertHidden() // inserting filtered-out rows shouldn't emit anything + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1 = new QStandardItem(QStringLiteral("1.1.1")); + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1->appendRow(item_1_1_1_1); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->appendRow(item_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QString()); + QCOMPARE(spy.mSignals, QStringList()); + } + + void testConsecutiveInserts_data() + { + testInitialFiltering_data(); + } + + void testConsecutiveInserts() + { + QFETCH(QString, sourceStr); + QFETCH(QString, proxyStr); + + QStandardItemModel model; + TestModel proxy(&model); // this time the proxy listens to the model while we fill it + + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + QCOMPARE(treeAsString(proxy), proxyStr); + } + + void testRemove_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("remove"); // remove this item + QTest::addColumn<QString>("expectedProxyStr"); + QTest::addColumn<QStringList>("expectedSignals"); + + const QStringList remove1_1_1 = (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") << QStringLiteral("rowsRemoved(1.1.1)")); + + QTest::newRow("toplevel") << "[1* 2* 3*]" << "[1* 2* 3*]" << "1" << "[2* 3*]" + << (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1)") << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("remove_hidden") << "[1 2* 3*]" << "[2* 3*]" << "1" << "[2* 3*]" << QStringList(); + + QTest::newRow("parent_hidden") << "[1[1.1[1.1.1]]]" << "" << "1.1.1" << "" << QStringList(); + + QTest::newRow("child_hidden") << "[1[1.1*[1.1.1]]]" << "[1[1.1*]]" << "1.1.1" << "[1[1.1*]]" << QStringList(); + + QTest::newRow("parent_visible") << "[1[1.1*[1.1.1*]]]" << "[1[1.1*[1.1.1*]]]" << "1.1.1" << "[1[1.1*]]" + << remove1_1_1; + + QTest::newRow("visible") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.2*]]]" + << remove1_1_1; + QTest::newRow("visible_cousin") << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.2[1.1.2.1*]]]]" + << remove1_1_1; + + // The following tests trigger the removal of an ascendant. + // We could optimize the rows{AboutToBe,}Removed(1.1.1) away... + + QTest::newRow("remove_parent") << "[1[1.1[1.1.1* 1.1.2] 1.2*]]" << "[1[1.1[1.1.1*] 1.2*]]" << "1.1.1" << "[1[1.2*]]" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("with_children") << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "1.1.1" << "[2*]" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("last_visible") << "[1[1.1[1.1.1* 1.1.2]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + + } + + void testRemove() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, remove); + QFETCH(QString, expectedProxyStr); + QFETCH(QStringList, expectedSignals); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + QStandardItem *itemToRemove = itemByText(model, remove); + QVERIFY(itemToRemove); + if (itemToRemove->parent()) + itemToRemove->parent()->removeRow(itemToRemove->row()); + else + model.removeRow(itemToRemove->row()); + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, expectedSignals); + } + + void testStandardFiltering_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("filter"); + QTest::addColumn<QString>("expectedProxyStr"); + + QTest::newRow("select_child") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" + << "1.1.2" << "[1[1.1[1.1.2*]]]"; + + QTest::newRow("filter_all_out") << "[1[1.1[1.1.1*]]]" << "[1[1.1[1.1.1*]]]" + << "test" << ""; + + QTest::newRow("select_parent") << "[1[1.1[1.1.1*[child*] 1.1.2*]]]" << "[1[1.1[1.1.1*[child*] 1.1.2*]]]" + << "1.1.1" << "[1[1.1[1.1.1*]]]"; + + } + + void testStandardFiltering() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, filter); + QFETCH(QString, expectedProxyStr); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + + //qDebug() << "setFilterFixedString"; + proxy.setFilterFixedString(filter); + + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + } + +private: + QStandardItem *itemByText(const QStandardItemModel& model, const QString &text) const { + QModelIndexList list = model.match(model.index(0, 0), Qt::DisplayRole, text, 1, Qt::MatchRecursive); + return list.isEmpty() ? 0 : model.itemFromIndex(list.first()); + } +}; + +QTEST_GUILESS_MAIN(tst_QSortFilterProxyModel_Recursive) +#include "tst_qsortfilterproxymodel_recursive.moc" diff --git a/tests/auto/corelib/json/tst_qtjson.cpp b/tests/auto/corelib/json/tst_qtjson.cpp index b215364f0e..417b9a4173 100644 --- a/tests/auto/corelib/json/tst_qtjson.cpp +++ b/tests/auto/corelib/json/tst_qtjson.cpp @@ -145,6 +145,9 @@ private Q_SLOTS: void parseErrorOffset_data(); void parseErrorOffset(); + void implicitValueType(); + void implicitDocumentType(); + private: QString testDataDir; }; @@ -2908,5 +2911,52 @@ void tst_QtJson::parseErrorOffset() QCOMPARE(error.offset, errorOffset); } +void tst_QtJson::implicitValueType() +{ + QJsonObject rootObject{ + {"object", QJsonObject{{"value", 42}}}, + {"array", QJsonArray{665, 666, 667}} + }; + + QJsonValue objectValue = rootObject["object"]; + QCOMPARE(objectValue["value"].toInt(), 42); + QCOMPARE(objectValue["missingValue"], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(objectValue[123], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(objectValue["missingValue"].toInt(123), 123); + + QJsonValue arrayValue = rootObject["array"]; + QCOMPARE(arrayValue[1].toInt(), 666); + QCOMPARE(arrayValue[-1], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(arrayValue["asObject"], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(arrayValue[-1].toInt(123), 123); + + const QJsonObject constObject = rootObject; + QCOMPARE(constObject["object"]["value"].toInt(), 42); + QCOMPARE(constObject["array"][1].toInt(), 666); + + QJsonValue objectAsValue(rootObject); + QCOMPARE(objectAsValue["object"]["value"].toInt(), 42); + QCOMPARE(objectAsValue["array"][1].toInt(), 666); +} + +void tst_QtJson::implicitDocumentType() +{ + QJsonDocument emptyDocument; + QCOMPARE(emptyDocument["asObject"], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(emptyDocument[123], QJsonValue(QJsonValue::Undefined)); + + QJsonDocument objectDocument(QJsonObject{{"value", 42}}); + QCOMPARE(objectDocument["value"].toInt(), 42); + QCOMPARE(objectDocument["missingValue"], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(objectDocument[123], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(objectDocument["missingValue"].toInt(123), 123); + + QJsonDocument arrayDocument(QJsonArray{665, 666, 667}); + QCOMPARE(arrayDocument[1].toInt(), 666); + QCOMPARE(arrayDocument[-1], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(arrayDocument["asObject"], QJsonValue(QJsonValue::Undefined)); + QCOMPARE(arrayDocument[-1].toInt(123), 123); +} + QTEST_MAIN(tst_QtJson) #include "tst_qtjson.moc" diff --git a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp index ad40b0140d..feb704e0aa 100644 --- a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp +++ b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp @@ -30,8 +30,6 @@ #include <QtTest/QtTest> #include <qmath.h> -static const double PI = 3.14159265358979323846264338327950288; - class tst_QMath : public QObject { Q_OBJECT @@ -55,8 +53,9 @@ void tst_QMath::fastSinCos() { // Test evenly spaced angles from 0 to 2pi radians. const int LOOP_COUNT = 100000; + const qreal loopAngle = 2 * M_PI / (LOOP_COUNT - 1); for (int i = 0; i < LOOP_COUNT; ++i) { - qreal angle = i * 2 * PI / (LOOP_COUNT - 1); + qreal angle = i * loopAngle; QVERIFY(qAbs(qSin(angle) - qFastSin(angle)) < 1e-5); QVERIFY(qAbs(qCos(angle) - qFastCos(angle)) < 1e-5); } @@ -69,18 +68,18 @@ void tst_QMath::degreesToRadians_data() QTest::addColumn<double>("degreesDouble"); QTest::addColumn<double>("radiansDouble"); - QTest::newRow( "pi" ) << 180.0f << float(M_PI) << 180.0 << PI; - QTest::newRow( "doublepi" ) << 360.0f << float(2*M_PI) << 360.0 << 2*PI; - QTest::newRow( "halfpi" ) << 90.0f << float(M_PI_2) << 90.0 << PI/2; + QTest::newRow( "pi" ) << 180.0f << float(M_PI) << 180.0 << M_PI; + QTest::newRow( "doublepi" ) << 360.0f << float(2 * M_PI) << 360.0 << 2 * M_PI; + QTest::newRow( "halfpi" ) << 90.0f << float(M_PI_2) << 90.0 << M_PI_2; QTest::newRow( "random" ) << 123.1234567f << 2.1489097058516724f << 123.123456789123456789 << 2.148909707407169856192285627; QTest::newRow( "bigrandom" ) << 987654321.9876543f << 17237819.79023679f << 987654321987654321.987654321987654321 << 17237819790236794.0; QTest::newRow( "zero" ) << 0.0f << 0.0f << 0.0 << 0.0; - QTest::newRow( "minuspi" ) << -180.0f << float(-M_PI) << 180.0 << PI; - QTest::newRow( "minusdoublepi" ) << -360.0f << float(-2*M_PI) << -360.0 << -2*PI; - QTest::newRow( "minushalfpi" ) << -90.0f << float(-M_PI_2) << -90.0 << -PI/2; + QTest::newRow( "minuspi" ) << -180.0f << float(-M_PI) << 180.0 << M_PI; + QTest::newRow( "minusdoublepi" ) << -360.0f << float(-2 * M_PI) << -360.0 << -2 * M_PI; + QTest::newRow( "minushalfpi" ) << -90.0f << float(-M_PI_2) << -90.0 << -M_PI_2; QTest::newRow( "minusrandom" ) << -123.1234567f << -2.1489097058516724f << -123.123456789123456789 << -2.148909707407169856192285627; QTest::newRow( "minusbigrandom" ) << -987654321.9876543f << -17237819.79023679f << -987654321987654321.987654321987654321 << -17237819790236794.0; @@ -104,18 +103,18 @@ void tst_QMath::radiansToDegrees_data() QTest::addColumn<double>("radiansDouble"); QTest::addColumn<double>("degreesDouble"); - QTest::newRow( "pi" ) << float(M_PI) << 180.0f << PI << 180.0; - QTest::newRow( "doublepi" ) << float(2*M_PI) << 360.0f << 2*PI << 360.0; - QTest::newRow( "halfpi" ) << float(M_PI_2) << 90.0f<< PI/2 << 90.0; + QTest::newRow( "pi" ) << float(M_PI) << 180.0f << M_PI << 180.0; + QTest::newRow( "doublepi" ) << float(2 * M_PI) << 360.0f << 2 * M_PI << 360.0; + QTest::newRow( "halfpi" ) << float(M_PI_2) << 90.0f << M_PI_2 << 90.0; QTest::newRow( "random" ) << 123.1234567f << 7054.454427971739f << 123.123456789123456789 << 7054.4544330781363896676339209079742431640625; QTest::newRow( "bigrandom" ) << 987654321.9876543f << 56588424267.74745f << 987654321987654321.987654321987654321 << 56588424267747450880.0; QTest::newRow( "zero" ) << 0.0f << 0.0f << 0.0 << 0.0; - QTest::newRow( "minuspi" ) << float(-M_PI) << -180.0f << -PI << -180.0; - QTest::newRow( "minusdoublepi" ) << float(-2*M_PI) << -360.0f << -2*PI << -360.0; - QTest::newRow( "minushalfpi" ) << float(-M_PI_2) << -90.0f << -PI/2 << -90.0; + QTest::newRow( "minuspi" ) << float(-M_PI) << -180.0f << -M_PI << -180.0; + QTest::newRow( "minusdoublepi" ) << float(-2 * M_PI) << -360.0f << -2 * M_PI << -360.0; + QTest::newRow( "minushalfpi" ) << float(-M_PI_2) << -90.0f << -M_PI_2 << -90.0; QTest::newRow( "minusrandom" ) << -123.1234567f << -7054.454427971739f << -123.123456789123456789 << -7054.4544330781363896676339209079742431640625; QTest::newRow( "minusbigrandom" ) << -987654321.9876543f << -56588424267.74745f << -987654321987654321.987654321987654321 << -56588424267747450880.0; diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index e793d71fe2..35e14b7dbf 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -147,6 +147,77 @@ namespace MyNamespace { { Q_OBJECT }; + + class ClassWithSetterGetterSignals : public QObject + { + Q_OBJECT + + public: + int value1() const { return m_value1; } + void setValue1(int v) { + if (v != m_value1) { + m_value1 = v; + Q_EMIT value1Changed(); + } + } + + int value2() const { return m_value2; } + void setValue2(int v) { + if (v != m_value2) { + m_value2 = v; + Q_EMIT value2Changed(); + } + } + + Q_SIGNALS: + void value1Changed(); + void value2Changed(); + + private: + int m_value1 = 0; + int m_value2 = 0; + }; + + class ClassWithSetterGetterSignalsAddsProperties : public ClassWithSetterGetterSignals + { + Q_OBJECT + Q_PROPERTY(int value1 READ value1 WRITE setValue1 NOTIFY value1Changed) + Q_PROPERTY(int value2 READ value2 WRITE setValue2 NOTIFY value2Changed) + }; + + class ClassWithChangedSignal : public QObject + { + Q_OBJECT + + public: + int value1() const { return m_value1; } + void setValue1(int v) { + if (v != m_value1) { + m_value1 = v; + Q_EMIT propertiesChanged(); + } + } + + void thisIsNotASignal() { } + + Q_SIGNALS: + void propertiesChanged(); + + private: + int m_value1 = 0; + }; + + class ClassWithChangedSignalNewValue : public ClassWithChangedSignal + { + Q_OBJECT + + Q_PROPERTY(int value2 MEMBER m_value2 NOTIFY propertiesChanged) + Q_PROPERTY(int value3 MEMBER m_value3 NOTIFY thisIsNotASignal) + + private: + int m_value2 = 0; + int m_value3 = 0; + }; } @@ -200,8 +271,11 @@ public: private slots: void connectSlotsByName(); void invokeMetaMember(); + void invokePointer(); void invokeQueuedMetaMember(); + void invokeQueuedPointer(); void invokeBlockingQueuedMetaMember(); + void invokeBlockingQueuedPointer(); void invokeCustomTypes(); void invokeMetaConstructor(); void invokeTypedefTypes(); @@ -242,6 +316,8 @@ private slots: void inherits_data(); void inherits(); + void notifySignalsInParentClass(); + signals: void value6Changed(); void value7Changed(const QString &); @@ -427,6 +503,10 @@ public slots: + QString::number(o6.size()); } +public: + static void staticFunction0(); + static qint64 staticFunction1(); + signals: void sig0(); QString sig1(QString s1); @@ -440,8 +520,11 @@ private: public: QString slotResult; + static QString staticResult; }; +QString QtTestObject::staticResult; + QtTestObject::QtTestObject() { connect(this, SIGNAL(sig0()), this, SLOT(sl0())); @@ -500,6 +583,13 @@ void QtTestObject::testSender() void QtTestObject::slotWithUnregisteredParameterType(MyUnregisteredType) { slotResult = "slotWithUnregisteredReturnType"; } +void QtTestObject::staticFunction0() +{ + staticResult = "staticFunction0"; +} + +qint64 QtTestObject::staticFunction1() +{ staticResult = "staticFunction1"; return Q_INT64_C(123456789)*123456789; } void tst_QMetaObject::invokeMetaMember() { @@ -508,9 +598,18 @@ void tst_QMetaObject::invokeMetaMember() QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5"); QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X"); - QVERIFY(!QMetaObject::invokeMethod(0, 0)); - QVERIFY(!QMetaObject::invokeMethod(0, "sl0")); - QVERIFY(!QMetaObject::invokeMethod(&obj, 0)); + // Test nullptr + char *nullCharArray = nullptr; + const char *nullConstCharArray = nullptr; + QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray)); + QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray)); + QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0")); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection)); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection, QGenericReturnArgument())); + QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection, QGenericReturnArgument())); QVERIFY(QMetaObject::invokeMethod(&obj, "sl0")); QCOMPARE(obj.slotResult, QString("sl0")); @@ -639,6 +738,56 @@ void tst_QMetaObject::invokeMetaMember() QCOMPARE(obj.slotResult, QString("sl1:hehe")); } +void testFunction(){} + + +void tst_QMetaObject::invokePointer() +{ + QtTestObject obj; + QtTestObject *const nullTestObject = nullptr; + + QString t1("1"); + + // Test member functions + QVERIFY(!QMetaObject::invokeMethod(nullTestObject, &QtTestObject::sl0)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0)); + QCOMPARE(obj.slotResult, QString("sl0")); + + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::testSender)); + QCOMPARE(obj.slotResult, QString("0x0")); + + qint64 return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl14, &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(obj.slotResult, QString("sl14")); + + // signals + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0)); + QCOMPARE(obj.slotResult, QString("sl0")); + + // Test function pointers + QVERIFY(!QMetaObject::invokeMethod(0, &testFunction)); + QVERIFY(QMetaObject::invokeMethod(&obj, &testFunction)); + + QVERIFY(!QMetaObject::invokeMethod(0, &QtTestObject::staticFunction0)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0)); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction0")); + + return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction1, &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction1")); + + // Test lambdas + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.sl1(t1);})); + QCOMPARE(obj.slotResult, QString("sl1:1")); + + QString exp; + QVERIFY(QMetaObject::invokeMethod(&obj, [&]()->QString{return obj.sl1("bubu");}, &exp)); + QCOMPARE(exp, QString("yessir")); + QCOMPARE(obj.slotResult, QString("sl1:bubu")); +} + void tst_QMetaObject::invokeQueuedMetaMember() { QtTestObject obj; @@ -699,6 +848,44 @@ void tst_QMetaObject::invokeQueuedMetaMember() } } +void tst_QMetaObject::invokeQueuedPointer() +{ + QtTestObject obj; + + // Test member function + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0, Qt::QueuedConnection)); + QVERIFY(obj.slotResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, QString("sl0")); + + // signals + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0, Qt::QueuedConnection)); + QVERIFY(obj.slotResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, QString("sl0")); + + // Test function pointers + QtTestObject::staticResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0, Qt::QueuedConnection)); + QVERIFY(QtTestObject::staticResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction0")); + + // Test lambda + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.sl0();}, Qt::QueuedConnection)); + QVERIFY(obj.slotResult.isEmpty()); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, QString("sl0")); + + qint32 var = 0; + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: Unable to invoke methods with return values in queued connections"); + QVERIFY(!QMetaObject::invokeMethod(&obj, []()->qint32{return 1;}, Qt::QueuedConnection, &var)); + QCOMPARE(var, 0); +} + + void tst_QMetaObject::invokeBlockingQueuedMetaMember() { QThread t; @@ -832,6 +1019,62 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember() } +void tst_QMetaObject::invokeBlockingQueuedPointer() +{ + QtTestObject *const nullTestObject = nullptr; + + QThread t; + t.start(); + QtTestObject obj; + obj.moveToThread(&t); + + QString t1("1"); + + // Test member functions + QVERIFY(!QMetaObject::invokeMethod(nullTestObject, &QtTestObject::sl0, Qt::BlockingQueuedConnection)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("sl0")); + + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::testSender, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("0x0")); + + // return qint64 + qint64 return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl14, Qt::BlockingQueuedConnection, + &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(obj.slotResult, QString("sl14")); + + //test signals + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("sl0")); + + // Test function pointers + QVERIFY(!QMetaObject::invokeMethod(0, &testFunction, Qt::BlockingQueuedConnection)); + QVERIFY(QMetaObject::invokeMethod(&obj, &testFunction, Qt::BlockingQueuedConnection)); + + QVERIFY(!QMetaObject::invokeMethod(0, &QtTestObject::staticFunction0, Qt::BlockingQueuedConnection)); + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0, Qt::BlockingQueuedConnection)); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction0")); + + return64 = 0; + QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction1, Qt::BlockingQueuedConnection, &return64)); + QCOMPARE(return64, Q_INT64_C(123456789)*123456789); + QCOMPARE(QtTestObject::staticResult, QString("staticFunction1")); + + // Test lambdas + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.sl1(t1);}, Qt::BlockingQueuedConnection)); + QCOMPARE(obj.slotResult, QString("sl1:1")); + + QString exp; + QVERIFY(QMetaObject::invokeMethod(&obj, [&]()->QString{return obj.sl1("bubu");}, Qt::BlockingQueuedConnection, &exp)); + QCOMPARE(exp, QString("yessir")); + QCOMPARE(obj.slotResult, QString("sl1:bubu")); + + QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.moveToThread(QThread::currentThread());}, Qt::BlockingQueuedConnection)); + t.quit(); + QVERIFY(t.wait()); +} void tst_QMetaObject::qtMetaObjectInheritance() @@ -1502,5 +1745,18 @@ void tst_QMetaObject::inherits() QCOMPARE(derivedMetaObject->inherits(baseMetaObject), inheritsResult); } +void tst_QMetaObject::notifySignalsInParentClass() +{ + MyNamespace::ClassWithSetterGetterSignalsAddsProperties obj; + QCOMPARE(obj.metaObject()->property(obj.metaObject()->indexOfProperty("value1")).notifySignal().name(), QByteArray("value1Changed")); + QCOMPARE(obj.metaObject()->property(obj.metaObject()->indexOfProperty("value2")).notifySignal().name(), QByteArray("value2Changed")); + + MyNamespace::ClassWithChangedSignalNewValue obj2; + QCOMPARE(obj2.metaObject()->property(obj2.metaObject()->indexOfProperty("value2")).notifySignal().name(), QByteArray("propertiesChanged")); + + QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::notifySignal: cannot find the NOTIFY signal thisIsNotASignal in class MyNamespace::ClassWithChangedSignalNewValue for property 'value3'"); + obj2.metaObject()->property(obj2.metaObject()->indexOfProperty("value3")).notifySignal(); +} + QTEST_MAIN(tst_QMetaObject) #include "tst_qmetaobject.moc" diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 9ae39449f8..6bb031e357 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -806,6 +806,7 @@ void tst_QMetaObjectBuilder::enumerator() QMetaEnumBuilder enum1 = builder.addEnumerator("foo"); QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(!enum1.isFlag()); + QVERIFY(!enum1.isScoped()); QCOMPARE(enum1.keyCount(), 0); QCOMPARE(enum1.index(), 0); QCOMPARE(builder.enumeratorCount(), 1); @@ -814,6 +815,7 @@ void tst_QMetaObjectBuilder::enumerator() QMetaEnumBuilder enum2 = builder.addEnumerator("bar"); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(!enum2.isFlag()); + QVERIFY(!enum2.isScoped()); QCOMPARE(enum2.keyCount(), 0); QCOMPARE(enum2.index(), 1); QCOMPARE(builder.enumeratorCount(), 2); @@ -827,6 +829,7 @@ void tst_QMetaObjectBuilder::enumerator() // Modify the attributes on enum1. enum1.setIsFlag(true); + enum1.setIsScoped(true); QCOMPARE(enum1.addKey("ABC", 0), 0); QCOMPARE(enum1.addKey("DEF", 1), 1); QCOMPARE(enum1.addKey("GHI", -1), 2); @@ -834,6 +837,7 @@ void tst_QMetaObjectBuilder::enumerator() // Check that enum1 is changed, but enum2 is not. QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(enum1.isFlag()); + QVERIFY(enum1.isScoped()); QCOMPARE(enum1.keyCount(), 3); QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.key(0), QByteArray("ABC")); @@ -845,6 +849,7 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.value(2), -1); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(!enum2.isFlag()); + QVERIFY(!enum2.isScoped()); QCOMPARE(enum2.keyCount(), 0); QCOMPARE(enum2.index(), 1); @@ -856,6 +861,7 @@ void tst_QMetaObjectBuilder::enumerator() // This time check that only method2 changed. QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(enum1.isFlag()); + QVERIFY(enum1.isScoped()); QCOMPARE(enum1.keyCount(), 3); QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.key(0), QByteArray("ABC")); @@ -867,6 +873,7 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.value(2), -1); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(enum2.isFlag()); + QVERIFY(!enum2.isScoped()); QCOMPARE(enum2.keyCount(), 2); QCOMPARE(enum2.index(), 1); QCOMPARE(enum2.key(0), QByteArray("XYZ")); @@ -879,6 +886,7 @@ void tst_QMetaObjectBuilder::enumerator() enum1.removeKey(2); QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(enum1.isFlag()); + QVERIFY(enum1.isScoped()); QCOMPARE(enum1.keyCount(), 2); QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.key(0), QByteArray("ABC")); @@ -889,6 +897,7 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.value(2), -1); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(enum2.isFlag()); + QVERIFY(!enum2.isScoped()); QCOMPARE(enum2.keyCount(), 2); QCOMPARE(enum2.index(), 1); QCOMPARE(enum2.key(0), QByteArray("XYZ")); @@ -903,6 +912,7 @@ void tst_QMetaObjectBuilder::enumerator() enum2 = builder.enumerator(0); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(enum2.isFlag()); + QVERIFY(!enum2.isScoped()); QCOMPARE(enum2.keyCount(), 2); QCOMPARE(enum2.index(), 0); QCOMPARE(enum2.key(0), QByteArray("XYZ")); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index f9ddd59aaa..076610a0c5 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -155,7 +155,9 @@ void tst_QMetaType::defined() QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0); QCOMPARE(int(QMetaTypeId2<CustomQObject::CustomQEnum>::Defined), 1); QCOMPARE(int(QMetaTypeId2<CustomGadget>::Defined), 1); + QCOMPARE(int(QMetaTypeId2<CustomGadget*>::Defined), 1); QVERIFY(!QMetaTypeId2<GadgetDerived>::Defined); + QVERIFY(!QMetaTypeId2<GadgetDerived*>::Defined); QVERIFY(int(QMetaTypeId2<CustomQObject*>::Defined)); QVERIFY(!QMetaTypeId2<CustomQObject>::Defined); QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined); @@ -397,6 +399,7 @@ void tst_QMetaType::typeName_data() QTest::newRow("CustomQObject*") << ::qMetaTypeId<CustomQObject*>() << QString::fromLatin1("CustomQObject*"); QTest::newRow("CustomGadget") << ::qMetaTypeId<CustomGadget>() << QString::fromLatin1("CustomGadget"); + QTest::newRow("CustomGadget*") << ::qMetaTypeId<CustomGadget*>() << QString::fromLatin1("CustomGadget*"); QTest::newRow("CustomQObject::CustomQEnum") << ::qMetaTypeId<CustomQObject::CustomQEnum>() << QString::fromLatin1("CustomQObject::CustomQEnum"); QTest::newRow("Qt::ArrowType") << ::qMetaTypeId<Qt::ArrowType>() << QString::fromLatin1("Qt::ArrowType"); } @@ -1684,6 +1687,7 @@ public: }; Q_DECLARE_METATYPE(MyGadget); +Q_DECLARE_METATYPE(MyGadget*); Q_DECLARE_METATYPE(const QMetaObject *); Q_DECLARE_METATYPE(Qt::ScrollBarPolicy); Q_DECLARE_METATYPE(MyGadget::MyEnum); @@ -1693,16 +1697,18 @@ void tst_QMetaType::metaObject_data() QTest::addColumn<int>("type"); QTest::addColumn<const QMetaObject*>("result"); QTest::addColumn<bool>("isGadget"); + QTest::addColumn<bool>("isGadgetPtr"); QTest::addColumn<bool>("isQObjectPtr"); - QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << true; - QTest::newRow("QFile*") << ::qMetaTypeId<QFile*>() << &QFile::staticMetaObject << false << true; - QTest::newRow("MyObject*") << ::qMetaTypeId<MyObject*>() << &MyObject::staticMetaObject << false << true; - QTest::newRow("int") << int(QMetaType::Int) << static_cast<const QMetaObject *>(0) << false << false; - QTest::newRow("QEasingCurve") << ::qMetaTypeId<QEasingCurve>() << &QEasingCurve::staticMetaObject << true << false; - QTest::newRow("MyGadget") << ::qMetaTypeId<MyGadget>() << &MyGadget::staticMetaObject << true << false; - QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false; - QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false; + QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << false << true; + QTest::newRow("QFile*") << ::qMetaTypeId<QFile*>() << &QFile::staticMetaObject << false << false << true; + QTest::newRow("MyObject*") << ::qMetaTypeId<MyObject*>() << &MyObject::staticMetaObject << false << false << true; + QTest::newRow("int") << int(QMetaType::Int) << static_cast<const QMetaObject *>(0) << false << false << false; + QTest::newRow("QEasingCurve") << ::qMetaTypeId<QEasingCurve>() << &QEasingCurve::staticMetaObject << true << false << false; + QTest::newRow("MyGadget") << ::qMetaTypeId<MyGadget>() << &MyGadget::staticMetaObject << true << false << false; + QTest::newRow("MyGadget*") << ::qMetaTypeId<MyGadget*>() << &MyGadget::staticMetaObject << false << true << false; + QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false << false; + QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false << false; } @@ -1711,12 +1717,14 @@ void tst_QMetaType::metaObject() QFETCH(int, type); QFETCH(const QMetaObject *, result); QFETCH(bool, isGadget); + QFETCH(bool, isGadgetPtr); QFETCH(bool, isQObjectPtr); QCOMPARE(QMetaType::metaObjectForType(type), result); QMetaType mt(type); QCOMPARE(mt.metaObject(), result); QCOMPARE(!!(mt.flags() & QMetaType::IsGadget), isGadget); + QCOMPARE(!!(mt.flags() & QMetaType::PointerToGadget), isGadgetPtr); QCOMPARE(!!(mt.flags() & QMetaType::PointerToQObject), isQObjectPtr); } diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 642d48d721..3a52c684d0 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -140,6 +140,7 @@ private slots: void connectFunctorWithContext(); void connectFunctorWithContextUnique(); void connectFunctorDeadlock(); + void connectFunctorMoveOnly(); void connectStaticSlotWithObject(); void disconnectDoesNotLeakFunctor(); void contextDoesNotLeakFunctor(); @@ -6237,6 +6238,47 @@ void tst_QObject::connectFunctorDeadlock() sender.emitSignal1(); } +void tst_QObject::connectFunctorMoveOnly() +{ + struct MoveOnlyFunctor { + Q_DISABLE_COPY(MoveOnlyFunctor) + MoveOnlyFunctor(int *status) : status(status) {} + MoveOnlyFunctor(MoveOnlyFunctor &&o) : status(o.status) { o.status = nullptr; }; + void operator()(int i) { *status = i; } + void operator()() { *status = -8; } + int *status; + }; + + int status = 1; + SenderObject obj; + QEventLoop e; + + connect(&obj, &SenderObject::signal1, MoveOnlyFunctor(&status)); + QCOMPARE(status, 1); + obj.signal1(); + QCOMPARE(status, -8); + + connect(&obj, &SenderObject::signal7, MoveOnlyFunctor(&status)); + QCOMPARE(status, -8); + obj.signal7(7888, "Hello"); + QCOMPARE(status, 7888); + + // With a context + status = 1; + connect(&obj, &SenderObject::signal2, this, MoveOnlyFunctor(&status)); + QCOMPARE(status, 1); + obj.signal2(); + QCOMPARE(status, -8); + + // QueuedConnection + status = 1; + connect(&obj, &SenderObject::signal3, this, MoveOnlyFunctor(&status), Qt::QueuedConnection); + obj.signal3(); + QCOMPARE(status, 1); + QCoreApplication::processEvents(); + QCOMPARE(status, -8); +} + static int s_static_slot_checker = 1; class StaticSlotChecker : public QObject diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index fd704f582d..e17d8ee38b 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -836,7 +836,6 @@ void tst_QTimer::singleShotToFunctors() QTest::qWait(800); QCOMPARE(count, 2); -#if defined(Q_COMPILER_LAMBDA) QTimer::singleShot(0, [&count] { ++count; }); QCoreApplication::processEvents(); QCOMPARE(count, 3); @@ -855,7 +854,15 @@ void tst_QTimer::singleShotToFunctors() thread.quit(); thread.wait(); -#endif + + struct MoveOnly : CountedStruct { + Q_DISABLE_COPY(MoveOnly); + MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {}; + MoveOnly(int *c) : CountedStruct(c) {} + }; + QTimer::singleShot(0, MoveOnly(&count)); + QCoreApplication::processEvents(); + QCOMPARE(count, 5); _e.reset(); _t = Q_NULLPTR; diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index d16948fd5d..97fce88f19 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -3395,21 +3395,6 @@ void tst_QVariant::toIntFromDouble() const QCOMPARE(result, 2147483630); } -void tst_QVariant::setValue() -{ - QJsonDocument t; //we just take a value so that we're sure that it will be shared - QVariant v1 = QVariant::fromValue(t); - QVERIFY( v1.isDetached() ); - QVariant v2 = v1; - QVERIFY( !v1.isDetached() ); - QVERIFY( !v2.isDetached() ); - - v2.setValue(3); //set an integer value - - QVERIFY( v1.isDetached() ); - QVERIFY( v2.isDetached() ); -} - void tst_QVariant::fpStringRoundtrip_data() const { QTest::addColumn<QVariant>("number"); @@ -3641,6 +3626,20 @@ Q_DECLARE_METATYPE(MyMovable *) Q_DECLARE_METATYPE(MyNotMovable *) Q_DECLARE_METATYPE(QSharedDataPointer<MyShared>) +void tst_QVariant::setValue() +{ + MyNotMovable t; //we just take a value so that we're sure that it will be shared + QVariant v1 = QVariant::fromValue(t); + QVERIFY( v1.isDetached() ); + QVariant v2 = v1; + QVERIFY( !v1.isDetached() ); + QVERIFY( !v2.isDetached() ); + + v2.setValue(3); //set an integer value + + QVERIFY( v1.isDetached() ); + QVERIFY( v2.isDetached() ); +} void tst_QVariant::moreCustomTypes() { diff --git a/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp b/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp index d93603f641..c74bce3b5b 100644 --- a/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp +++ b/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp @@ -46,6 +46,7 @@ private slots: void genericIconName(); void iconName(); void suffixes(); + void gadget(); }; // ------------------------------------------------------------------------------------------------ @@ -201,5 +202,44 @@ void tst_qmimetype::suffixes() // ------------------------------------------------------------------------------------------------ +void tst_qmimetype::gadget() +{ + QMimeType instantiatedQMimeType ( + buildQMimeType ( + qMimeTypeName(), + qMimeTypeGenericIconName(), + qMimeTypeIconName(), + qMimeTypeGlobPatterns() + ) + ); + + const QMetaObject *metaObject = &instantiatedQMimeType.staticMetaObject; + + QCOMPARE(metaObject->className(), "QMimeType"); + QVariantMap properties; + for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + properties[property.name()] = property.readOnGadget(&instantiatedQMimeType); + } + + QCOMPARE(properties["valid"].toBool(), instantiatedQMimeType.isValid()); + QCOMPARE(properties["isDefault"].toBool(), instantiatedQMimeType.isDefault()); + QCOMPARE(properties["name"].toString(), instantiatedQMimeType.name()); + QCOMPARE(properties["comment"].toString(), instantiatedQMimeType.comment()); + QCOMPARE(properties["genericIconName"].toString(), instantiatedQMimeType.genericIconName()); + QCOMPARE(properties["iconName"].toString(), instantiatedQMimeType.iconName()); + QCOMPARE(properties["globPatterns"].toStringList(), instantiatedQMimeType.globPatterns()); + QCOMPARE(properties["parentMimeTypes"].toStringList(), instantiatedQMimeType.parentMimeTypes()); + QCOMPARE(properties["allAncestors"].toStringList(), instantiatedQMimeType.allAncestors()); + QCOMPARE(properties["aliases"].toStringList(), instantiatedQMimeType.aliases()); + QCOMPARE(properties["suffixes"].toStringList(), instantiatedQMimeType.suffixes()); + QCOMPARE(properties["preferredSuffix"].toString(), instantiatedQMimeType.preferredSuffix()); + QCOMPARE(properties["filterString"].toString(), instantiatedQMimeType.filterString()); + + QVERIFY(metaObject->indexOfMethod("inherits(QString)") >= 0); +} + +// ------------------------------------------------------------------------------------------------ + QTEST_GUILESS_MAIN(tst_qmimetype) #include "tst_qmimetype.moc" diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp index d3102c7ee5..9e3edc96ec 100644 --- a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp +++ b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp @@ -41,6 +41,7 @@ private slots: void fromChar(); void toString(); + void fromString_data(); void fromString(); void toByteArray(); void fromByteArray(); @@ -127,15 +128,58 @@ void tst_QUuid::toString() QCOMPARE(uuidB.toString(), QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}")); } +void tst_QUuid::fromString_data() +{ + QTest::addColumn<QUuid>("expected"); + QTest::addColumn<QString>("input"); + + QUuid invalid = {}; + +#define ROW(which, string) \ + QTest::addRow("%-38s -> %s", string, #which) << which << string + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); + ROW(uuidA, "fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5" ); + ROW(uuidA, "fc69b59e-cc34-4436-a43c-ee95d128b8c5" ); + + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c56"); // too long (not an error!) + ROW(invalid, "{fc69b59e-cc34-4436-a43c-ee95d128b8c" ); // premature end (within length limits) + ROW(invalid, " fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // leading space + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5 "); // trailing space (not an error!) + ROW(invalid, "{gc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // non-hex digit in 1st group + ROW(invalid, "{fc69b59e-cp34-4436-a43c-ee95d128b8c5}"); // non-hex digit in 2nd group + ROW(invalid, "{fc69b59e-cc34-44r6-a43c-ee95d128b8c5}"); // non-hex digit in 3rd group + ROW(invalid, "{fc69b59e-cc34-4436-a4yc-ee95d128b8c5}"); // non-hex digit in 4th group + ROW(invalid, "{fc69b59e-cc34-4436-a43c-ee95d128j8c5}"); // non-hex digit in last group + ROW(invalid, "(fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // wrong initial character + ROW(invalid, "{fc69b59e+cc34-4436-a43c-ee95d128b8c5}"); // wrong 1st separator + ROW(invalid, "{fc69b59e-cc34*4436-a43c-ee95d128b8c5}"); // wrong 2nd separator + ROW(invalid, "{fc69b59e-cc34-44366a43c-ee95d128b8c5}"); // wrong 3rd separator + ROW(invalid, "{fc69b59e-cc34-4436-a43c\303\244ee95d128b8c5}"); // wrong 4th separator (ä) + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5)"); // wrong final character (not an error!) + + ROW(uuidB, "{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"); +#undef ROW +} + void tst_QUuid::fromString() { - QCOMPARE(uuidA, QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}"))); - QCOMPARE(uuidA, QUuid(QString("fc69b59e-cc34-4436-a43c-ee95d128b8c5}"))); - QCOMPARE(uuidA, QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c5"))); - QCOMPARE(uuidA, QUuid(QString("fc69b59e-cc34-4436-a43c-ee95d128b8c5"))); - QCOMPARE(QUuid(), QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c"))); + QFETCH(const QUuid, expected); + QFETCH(const QString, input); - QCOMPARE(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"))); + const auto inputL1 = input.toLatin1(); + const auto inputU8 = input.toUtf8(); + + QCOMPARE(expected, QUuid(input)); + QCOMPARE(expected, QUuid(inputU8)); + QCOMPARE(expected, QUuid(inputL1)); + + QCOMPARE(expected, QUuid::fromString(input)); + + // for QLatin1String, construct one whose data() is not NUL-terminated: + const auto longerInputL1 = inputL1 + '5'; // the '5' makes the premature end check incorrectly succeed + const auto inputL1S = QLatin1String(longerInputL1.data(), inputL1.size()); + QCOMPARE(expected, QUuid::fromString(inputL1S)); } void tst_QUuid::toByteArray() diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm b/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm index d90bff65b3..41ccece115 100644 --- a/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm +++ b/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm @@ -36,7 +36,7 @@ void tst_QUuid_darwinTypes() { // QUuid <-> CFUUID { - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + const auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const CFUUIDRef cfuuid = qtUuid.toCFUUID(); QCOMPARE(QUuid::fromCFUUID(cfuuid), qtUuid); CFStringRef cfstring = CFUUIDCreateString(0, cfuuid); @@ -45,10 +45,10 @@ void tst_QUuid_darwinTypes() CFRelease(cfuuid); } { - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const CFUUIDRef cfuuid = qtUuid.toCFUUID(); QUuid qtUuidCopy(qtUuid); - qtUuid = QUuid(QByteArrayLiteral("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify + qtUuid = QUuid::fromString(QLatin1String("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify QCOMPARE(QUuid::fromCFUUID(cfuuid), qtUuidCopy); CFStringRef cfstring = CFUUIDCreateString(0, cfuuid); QCOMPARE(QString::fromCFString(cfstring), qtUuidCopy.toString().mid(1, 36).toUpper()); @@ -59,7 +59,7 @@ void tst_QUuid_darwinTypes() { QMacAutoReleasePool pool; - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + const auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const NSUUID *nsuuid = qtUuid.toNSUUID(); QCOMPARE(QUuid::fromNSUUID(nsuuid), qtUuid); QCOMPARE(QString::fromNSString([nsuuid UUIDString]), qtUuid.toString().mid(1, 36).toUpper()); @@ -67,10 +67,10 @@ void tst_QUuid_darwinTypes() { QMacAutoReleasePool pool; - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const NSUUID *nsuuid = qtUuid.toNSUUID(); QUuid qtUuidCopy(qtUuid); - qtUuid = QUuid(QByteArrayLiteral("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify + qtUuid = QUuid::fromString(QLatin1String("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify QCOMPARE(QUuid::fromNSUUID(nsuuid), qtUuidCopy); QCOMPARE(QString::fromNSString([nsuuid UUIDString]), qtUuidCopy.toString().mid(1, 36).toUpper()); } diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index a546cad225..37b052bf1d 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -1205,8 +1205,6 @@ void tst_QFuture::pause() Interface.reportFinished(); } -const int resultCount = 1000; - class ResultObject : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp index 2970b2e118..ba470a77c9 100644 --- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp +++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp @@ -42,6 +42,7 @@ private slots: void tryAcquireWithTimeout(); void tryAcquireWithTimeoutStarvation(); void producerConsumer(); + void raii(); }; static QSemaphore *semaphore = 0; @@ -417,5 +418,54 @@ void tst_QSemaphore::producerConsumer() consumer.wait(); } +void tst_QSemaphore::raii() +{ + QSemaphore sem; + + QCOMPARE(sem.available(), 0); + + // basic operation: + { + QSemaphoreReleaser r0; + const QSemaphoreReleaser r1(sem); + const QSemaphoreReleaser r2(sem, 2); + + QCOMPARE(r0.semaphore(), nullptr); + QCOMPARE(r1.semaphore(), &sem); + QCOMPARE(r2.semaphore(), &sem); + } + + QCOMPARE(sem.available(), 3); + + // cancel: + { + const QSemaphoreReleaser r1(sem); + QSemaphoreReleaser r2(sem, 2); + + QCOMPARE(r2.cancel(), &sem); + QCOMPARE(r2.semaphore(), nullptr); + } + + QCOMPARE(sem.available(), 4); + + // move-assignment: + { + const QSemaphoreReleaser r1(sem); + QSemaphoreReleaser r2(sem, 2); + + QCOMPARE(sem.available(), 4); + + r2 = QSemaphoreReleaser(); + + QCOMPARE(sem.available(), 6); + + r2 = QSemaphoreReleaser(sem, 42); + + QCOMPARE(sem.available(), 6); + } + + QCOMPARE(sem.available(), 49); +} + QTEST_MAIN(tst_QSemaphore) #include "tst_qsemaphore.moc" diff --git a/tests/auto/corelib/thread/qthread/qthread.pro b/tests/auto/corelib/thread/qthread/qthread.pro index e0ef506d2c..37552f1fca 100644 --- a/tests/auto/corelib/thread/qthread/qthread.pro +++ b/tests/auto/corelib/thread/qthread/qthread.pro @@ -2,6 +2,8 @@ CONFIG += testcase TARGET = tst_qthread QT = core testlib SOURCES = tst_qthread.cpp +qtConfig(c++14):CONFIG += c++14 +qtConfig(c++1z):CONFIG += c++1z INCLUDEPATH += ../../../../shared/ HEADERS += ../../../../shared/emulationdetector.h diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 5c8522a313..27a617ec85 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -47,6 +47,10 @@ #endif #endif +#ifndef QT_NO_EXCEPTIONS +#include <exception> +#endif + #include "emulationdetector.h" class tst_QThread : public QObject @@ -100,6 +104,8 @@ private slots: void stressTest(); void quitLock(); + + void create(); }; enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute }; @@ -1330,6 +1336,259 @@ void tst_QThread::quitLock() QVERIFY(exitThreadCalled); } +void tst_QThread::create() +{ +#ifndef QTHREAD_HAS_CREATE + QSKIP("This test requires QThread::create"); +#else + { + const auto &function = [](){}; + QScopedPointer<QThread> thread(QThread::create(function)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + } + + { + // no side effects before starting + int i = 0; + const auto &function = [&i]() { i = 42; }; + QScopedPointer<QThread> thread(QThread::create(function)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + QCOMPARE(i, 0); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, 42); + } + + { + // control thread progress + QSemaphore semaphore1; + QSemaphore semaphore2; + + const auto &function = [&semaphore1, &semaphore2]() -> void + { + semaphore1.acquire(); + semaphore2.release(); + }; + + QScopedPointer<QThread> thread(QThread::create(function)); + + QVERIFY(thread); + thread->start(); + QTRY_VERIFY(thread->isRunning()); + semaphore1.release(); + semaphore2.acquire(); + QVERIFY(thread->wait()); + QVERIFY(!thread->isRunning()); + } + + { + // ignore return values + const auto &function = []() { return 42; }; + QScopedPointer<QThread> thread(QThread::create(function)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + } + + { + // return value of create + QScopedPointer<QThread> thread; + QSemaphore s; + const auto &function = [&thread, &s]() -> void + { + s.acquire(); + QCOMPARE(thread.data(), QThread::currentThread()); + }; + + thread.reset(QThread::create(function)); + QVERIFY(thread); + thread->start(); + QTRY_VERIFY(thread->isRunning()); + s.release(); + QVERIFY(thread->wait()); + } + + { + // move-only parameters + struct MoveOnlyValue { + explicit MoveOnlyValue(int v) : v(v) {} + ~MoveOnlyValue() = default; + MoveOnlyValue(const MoveOnlyValue &) = delete; + MoveOnlyValue(MoveOnlyValue &&) = default; + MoveOnlyValue &operator=(const MoveOnlyValue &) = delete; + MoveOnlyValue &operator=(MoveOnlyValue &&) = default; + int v; + }; + + struct MoveOnlyFunctor { + explicit MoveOnlyFunctor(int *i) : i(i) {} + ~MoveOnlyFunctor() = default; + MoveOnlyFunctor(const MoveOnlyFunctor &) = delete; + MoveOnlyFunctor(MoveOnlyFunctor &&) = default; + MoveOnlyFunctor &operator=(const MoveOnlyFunctor &) = delete; + MoveOnlyFunctor &operator=(MoveOnlyFunctor &&) = default; + int operator()() { return (*i = 42); } + int *i; + }; + + { + int i = 0; + MoveOnlyFunctor f(&i); + QScopedPointer<QThread> thread(QThread::create(std::move(f))); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, 42); + } + +#if defined(__cpp_init_captures) && __cpp_init_captures >= 201304 + { + int i = 0; + MoveOnlyValue mo(123); + auto moveOnlyFunction = [&i, mo = std::move(mo)]() { i = mo.v; }; + QScopedPointer<QThread> thread(QThread::create(std::move(moveOnlyFunction))); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, 123); + } +#endif // __cpp_init_captures + +#ifdef QTHREAD_HAS_VARIADIC_CREATE + { + int i = 0; + const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; }; + QScopedPointer<QThread> thread(QThread::create(function, MoveOnlyValue(123))); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, 123); + } + + { + int i = 0; + const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; }; + MoveOnlyValue mo(-1); + QScopedPointer<QThread> thread(QThread::create(function, std::move(mo))); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, -1); + } +#endif // QTHREAD_HAS_VARIADIC_CREATE + } + +#ifdef QTHREAD_HAS_VARIADIC_CREATE + { + // simple parameter passing + int i = 0; + const auto &function = [&i](int j, int k) { i = j * k; }; + QScopedPointer<QThread> thread(QThread::create(function, 3, 4)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + QCOMPARE(i, 0); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, 12); + } + + { + // ignore return values (with parameters) + const auto &function = [](double d) { return d * 2.0; }; + QScopedPointer<QThread> thread(QThread::create(function, 3.14)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + } + + { + // handling of pointers to member functions, std::ref, etc. + struct S { + S() : v(0) {} + void doSomething() { ++v; } + int v; + }; + + S object; + + QCOMPARE(object.v, 0); + + QScopedPointer<QThread> thread; + thread.reset(QThread::create(&S::doSomething, object)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + + QCOMPARE(object.v, 0); // a copy was passed, this should still be 0 + + thread.reset(QThread::create(&S::doSomething, std::ref(object))); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + + QCOMPARE(object.v, 1); + + thread.reset(QThread::create(&S::doSomething, &object)); + QVERIFY(thread); + QVERIFY(!thread->isRunning()); + thread->start(); + QVERIFY(thread->wait()); + + QCOMPARE(object.v, 2); + } + + { + // std::ref into ordinary reference + int i = 42; + const auto &function = [](int &i) { i *= 2; }; + QScopedPointer<QThread> thread(QThread::create(function, std::ref(i))); + QVERIFY(thread); + thread->start(); + QVERIFY(thread->wait()); + QCOMPARE(i, 84); + } + +#ifndef QT_NO_EXCEPTIONS + { + // exceptions when copying/decaying the arguments are thrown at build side and won't terminate + class ThreadException : public std::exception + { + }; + + struct ThrowWhenCopying + { + ThrowWhenCopying() = default; + ThrowWhenCopying(const ThrowWhenCopying &) + { + throw ThreadException(); + } + ~ThrowWhenCopying() = default; + ThrowWhenCopying &operator=(const ThrowWhenCopying &) = default; + }; + + const auto &function = [](const ThrowWhenCopying &){}; + QScopedPointer<QThread> thread; + ThrowWhenCopying t; + QVERIFY_EXCEPTION_THROWN(thread.reset(QThread::create(function, t)), ThreadException); + QVERIFY(!thread); + } +#endif // QT_NO_EXCEPTIONS +#endif // QTHREAD_HAS_VARIADIC_CREATE +#endif // QTHREAD_HAS_CREATE +} + class StopableJob : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp index fdc4ecb5c8..1dcd642023 100644 --- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp +++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp @@ -961,21 +961,6 @@ void tst_QThreadPool::cancel() QSemaphore sem(0); QSemaphore startedThreads(0); - class SemaphoreReleaser - { - QSemaphore &sem; - int n; - Q_DISABLE_COPY(SemaphoreReleaser) - public: - explicit SemaphoreReleaser(QSemaphore &sem, int n) - : sem(sem), n(n) {} - - ~SemaphoreReleaser() - { - sem.release(n); - } - }; - class BlockingRunnable : public QRunnable { public: @@ -1014,7 +999,7 @@ void tst_QThreadPool::cancel() // ensure that the QThreadPool doesn't deadlock if any of the checks fail // and cause an early return: - const SemaphoreReleaser semReleaser(sem, runs); + const QSemaphoreReleaser semReleaser(sem, runs); count.store(0); QAtomicInt dtorCounter = 0; @@ -1048,21 +1033,6 @@ void tst_QThreadPool::tryTake() QSemaphore sem(0); QSemaphore startedThreads(0); - class SemaphoreReleaser - { - QSemaphore &sem; - int n; - Q_DISABLE_COPY(SemaphoreReleaser) - public: - explicit SemaphoreReleaser(QSemaphore &sem, int n) - : sem(sem), n(n) {} - - ~SemaphoreReleaser() - { - sem.release(n); - } - }; - class BlockingRunnable : public QRunnable { public: @@ -1101,7 +1071,7 @@ void tst_QThreadPool::tryTake() // ensure that the QThreadPool doesn't deadlock if any of the checks fail // and cause an early return: - const SemaphoreReleaser semReleaser(sem, Runs); + const QSemaphoreReleaser semReleaser(sem, Runs); count.store(0); QAtomicInt dtorCounter = 0; diff --git a/tests/auto/corelib/tools/containerapisymmetry/.gitignore b/tests/auto/corelib/tools/containerapisymmetry/.gitignore new file mode 100644 index 0000000000..172ca970f2 --- /dev/null +++ b/tests/auto/corelib/tools/containerapisymmetry/.gitignore @@ -0,0 +1 @@ +tst_containerapisymmetry diff --git a/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro b/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro new file mode 100644 index 0000000000..30dc8026ef --- /dev/null +++ b/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_containerapisymmetry +SOURCES += tst_containerapisymmetry.cpp +QT = core testlib + +# This test does not work with strict iterators +DEFINES -= QT_STRICT_ITERATORS diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp new file mode 100644 index 0000000000..3b8111f1a3 --- /dev/null +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include "qbytearray.h" +#include "qlinkedlist.h" +#include "qlist.h" +#include "qstring.h" +#include "qvarlengtharray.h" +#include "qvector.h" + +#include <vector> // for reference + +class tst_ContainerApiSymmetry : public QObject +{ + Q_OBJECT + +private: + template <typename Container> + void front_back_impl() const; + +private Q_SLOTS: + void front_back_std_vector() { front_back_impl<std::vector<int>>(); } + void front_back_QVector() { front_back_impl<QVector<int>>(); } + void front_back_QList() { front_back_impl<QList<qintptr>>(); } + void front_back_QLinkedList() { front_back_impl<QLinkedList<int>>(); } + void front_back_QVarLengthArray() { front_back_impl<QVarLengthArray<int>>(); } + void front_back_QString() { front_back_impl<QString>(); } + void front_back_QStringRef() { front_back_impl<QStringRef>(); } + void front_back_QStringView() { front_back_impl<QStringView>(); } + void front_back_QLatin1String() { front_back_impl<QLatin1String>(); } + void front_back_QByteArray() { front_back_impl<QByteArray>(); } +}; + +template <typename Container> +Container make(int size) +{ + Container c; + int i = 1; + while (size--) + c.push_back(typename Container::value_type(i++)); + return c; +} + +static QString s_string = QStringLiteral("\1\2\3\4\5\6\7"); + +template <> QStringRef make(int size) { return s_string.leftRef(size); } +template <> QStringView make(int size) { return QStringView(s_string).left(size); } +template <> QLatin1String make(int size) { return QLatin1String("\1\2\3\4\5\6\7", size); } + +template <typename T> T clean(T &&t) { return std::forward<T>(t); } +inline QChar clean(QCharRef ch) { return ch; } +inline char clean(QByteRef ch) { return ch; } +inline char clean(QLatin1Char ch) { return ch.toLatin1(); } + +template <typename Container> +void tst_ContainerApiSymmetry::front_back_impl() const +{ + using V = typename Container::value_type; + auto c1 = make<Container>(1); + QCOMPARE(clean(c1.front()), V(1)); + QCOMPARE(clean(c1.back()), V(1)); + QCOMPARE(clean(qAsConst(c1).front()), V(1)); + QCOMPARE(clean(qAsConst(c1).back()), V(1)); + + auto c2 = make<Container>(2); + QCOMPARE(clean(c2.front()), V(1)); + QCOMPARE(clean(c2.back()), V(2)); + QCOMPARE(clean(qAsConst(c2).front()), V(1)); + QCOMPARE(clean(qAsConst(c2).back()), V(2)); +} + +QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry) +#include "tst_containerapisymmetry.moc" diff --git a/tests/auto/corelib/tools/qchar/tst_qchar.cpp b/tests/auto/corelib/tools/qchar/tst_qchar.cpp index e5a6e953d3..cf4f6d21e2 100644 --- a/tests/auto/corelib/tools/qchar/tst_qchar.cpp +++ b/tests/auto/corelib/tools/qchar/tst_qchar.cpp @@ -36,6 +36,8 @@ class tst_QChar : public QObject { Q_OBJECT private slots: + void fromChar16_t(); + void fromWchar_t(); void operator_eqeq_null(); void operators_data(); void operators(); @@ -72,6 +74,33 @@ private slots: void unicodeVersion(); }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + +void tst_QChar::fromChar16_t() +{ +#if defined(Q_COMPILER_UNICODE_STRINGS) + QChar aUmlaut = u'\u00E4'; // German small letter a-umlaut + QCOMPARE(aUmlaut, QChar(0xE4)); + QChar replacementCharacter = u'\uFFFD'; + QCOMPARE(replacementCharacter, QChar(QChar::ReplacementCharacter)); +#else + QSKIP("This test requires C++11 char16_t support enabled in the compiler."); +#endif +} + +void tst_QChar::fromWchar_t() +{ +#if defined(Q_OS_WIN) + QChar aUmlaut = L'\u00E4'; // German small letter a-umlaut + QCOMPARE(aUmlaut, QChar(0xE4)); + QChar replacementCharacter = L'\uFFFD'; + QCOMPARE(replacementCharacter, QChar(QChar::ReplacementCharacter)); +#else + QSKIP("This is a Windows-only test."); +#endif +} + void tst_QChar::operator_eqeq_null() { { diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp index 28ad2d193c..f9024e017c 100644 --- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp @@ -2484,11 +2484,7 @@ void tst_QDateTime::fromStringToStringLocale() QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::SystemLocaleDate), Qt::SystemLocaleDate), dateTime); QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::LocaleDate), Qt::LocaleDate), dateTime); - QEXPECT_FAIL("data0", "This format is apparently failing because of a bug in the datetime parser. (QTBUG-22833)", Continue); QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::DefaultLocaleLongDate), Qt::DefaultLocaleLongDate), dateTime); -#ifndef Q_OS_WIN - QEXPECT_FAIL("data0", "This format is apparently failing because of a bug in the datetime parser. (QTBUG-22833)", Continue); -#endif QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::SystemLocaleLongDate), Qt::SystemLocaleLongDate), dateTime); QLocale::setDefault(def); diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index 943b4316ff..0015efacfa 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -60,6 +60,7 @@ private slots: void compare2(); void iterators(); // sligthly modified from tst_QMap void keyIterator(); + void keyValueIterator(); void keys_values_uniqueKeys(); // slightly modified from tst_QMap void noNeedlessRehashes(); @@ -1124,6 +1125,60 @@ void tst_QHash::keyIterator() Q_STATIC_ASSERT(std::is_default_constructible<keyIterator>::value); } +void tst_QHash::keyValueIterator() +{ + QHash<int, int> hash; + typedef QHash<int, int>::const_key_value_iterator::value_type entry_type; + + for (int i = 0; i < 100; ++i) + hash.insert(i, i * 100); + + auto key_value_it = hash.constKeyValueBegin(); + auto it = hash.cbegin(); + + + for (int i = 0; i < hash.size(); ++i) { + QVERIFY(key_value_it != hash.constKeyValueEnd()); + QVERIFY(it != hash.cend()); + + entry_type pair(it.key(), it.value()); + QCOMPARE(*key_value_it, pair); + ++key_value_it; + ++it; + } + + QVERIFY(key_value_it == hash.constKeyValueEnd()); + QVERIFY(it == hash.cend()); + + int key = 50; + int value = 50 * 100; + entry_type pair(key, value); + key_value_it = std::find(hash.constKeyValueBegin(), hash.constKeyValueEnd(), pair); + it = std::find(hash.cbegin(), hash.cend(), value); + + QVERIFY(key_value_it != hash.constKeyValueEnd()); + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + ++it; + ++key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + --it; + --key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + ++it; + ++key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + --it; + --key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + key = 99; + value = 99 * 100; + QCOMPARE(std::count(hash.constKeyValueBegin(), hash.constKeyValueEnd(), entry_type(key, value)), 1); +} + void tst_QHash::rehash_isnt_quadratic() { // this test should be incredibly slow if rehash() is quadratic diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index 4a81adf9fe..124e3cdf00 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -49,6 +49,7 @@ public slots: void init(); private Q_SLOTS: + void consistent(); void qhash(); void qhash_of_empty_and_null_qstring(); void qhash_of_empty_and_null_qbytearray(); @@ -61,6 +62,17 @@ private Q_SLOTS: void setGlobalQHashSeed(); }; +void tst_QHashFunctions::consistent() +{ + // QString-like + { + const QString s = QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16); + + QCOMPARE(qHash(s), qHash(QStringRef(&s))); + QCOMPARE(qHash(s), qHash(QStringView(s))); + } +} + void tst_QHashFunctions::initTestCase() { Q_STATIC_ASSERT(int(RandomSeed) > 0); @@ -160,6 +172,14 @@ void tst_QHashFunctions::qhash_of_empty_and_null_qstring() QString null, empty(""); QCOMPARE(null, empty); QCOMPARE(qHash(null, seed), qHash(empty, seed)); + + QStringRef nullRef, emptyRef(&empty); + QCOMPARE(nullRef, emptyRef); + QCOMPARE(qHash(nullRef, seed), qHash(emptyRef, seed)); + + QStringView nullView, emptyView(empty); + QCOMPARE(nullView, emptyView); + QCOMPARE(qHash(nullView, seed), qHash(emptyView, seed)); } void tst_QHashFunctions::qhash_of_empty_and_null_qbytearray() @@ -264,17 +284,17 @@ void tst_QHashFunctions::rangeCommutative() void tst_QHashFunctions::setGlobalQHashSeed() { // Setter works as advertised - qSetGlobalQHashSeed(0x10101010); - QCOMPARE(qGlobalQHashSeed(), 0x10101010); + qSetGlobalQHashSeed(0); + QCOMPARE(qGlobalQHashSeed(), 0); // Creating a new QHash doesn't reset the seed QHash<QString, int> someHash; someHash.insert("foo", 42); - QCOMPARE(qGlobalQHashSeed(), 0x10101010); + QCOMPARE(qGlobalQHashSeed(), 0); // Reset works as advertised qSetGlobalQHashSeed(-1); - QVERIFY(qGlobalQHashSeed() != -1); + QVERIFY(qGlobalQHashSeed() > 0); } QTEST_APPLESS_MAIN(tst_QHashFunctions) diff --git a/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp b/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp index a68671d899..c8373b6ae9 100644 --- a/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp +++ b/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp @@ -48,6 +48,7 @@ private Q_SLOTS: void midLeftRight(); void nullString(); void emptyString(); + void iterators(); void relationalOperators_data(); void relationalOperators(); }; @@ -155,6 +156,22 @@ void tst_QLatin1String::emptyString() } } +void tst_QLatin1String::iterators() +{ + QLatin1String hello("hello"); + QLatin1String olleh("olleh"); + + QVERIFY(std::equal(hello.begin(), hello.end(), + olleh.rbegin())); + QVERIFY(std::equal(hello.rbegin(), hello.rend(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size()))); + + QVERIFY(std::equal(hello.cbegin(), hello.cend(), + olleh.rbegin())); + QVERIFY(std::equal(hello.crbegin(), hello.crend(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size()))); +} + void tst_QLatin1String::relationalOperators_data() { QTest::addColumn<QLatin1StringContainer>("lhs"); diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index 5d13d9e454..a08d600d8c 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -137,6 +137,9 @@ private slots: void textDirection_data(); void textDirection(); + void formattedDataSize_data(); + void formattedDataSize(); + private: QString m_decimal, m_thousand, m_sdate, m_ldate, m_time; QString m_sysapp; @@ -1163,6 +1166,9 @@ void tst_QLocale::dayOfWeek() QCOMPARE(QLocale::c().toString(date, "ddd"), shortName); QCOMPARE(QLocale::c().toString(date, "dddd"), longName); + + QCOMPARE(QLocale::c().toString(date, QStringViewLiteral("ddd")), shortName); + QCOMPARE(QLocale::c().toString(date, QStringViewLiteral("dddd")), longName); } void tst_QLocale::formatDate_data() @@ -1205,6 +1211,7 @@ void tst_QLocale::formatDate() QLocale l(QLocale::C); QCOMPARE(l.toString(date, format), result); + QCOMPARE(l.toString(date, QStringView(format)), result); } @@ -1260,6 +1267,7 @@ void tst_QLocale::formatTime() QLocale l(QLocale::C); QCOMPARE(l.toString(time, format), result); + QCOMPARE(l.toString(time, QStringView(format)), result); } @@ -1421,6 +1429,7 @@ void tst_QLocale::formatDateTime() QLocale l(localeName); QCOMPARE(l.toString(dateTime, format), result); + QCOMPARE(l.toString(dateTime, QStringView(format)), result); } void tst_QLocale::formatTimeZone() @@ -2143,9 +2152,9 @@ void tst_QLocale::timeFormat() QCOMPARE(c.timeFormat(QLocale::NarrowFormat), c.timeFormat(QLocale::ShortFormat)); const QLocale no("no_NO"); - QCOMPARE(no.timeFormat(QLocale::NarrowFormat), QLatin1String("HH.mm")); - QCOMPARE(no.timeFormat(QLocale::ShortFormat), QLatin1String("HH.mm")); - QCOMPARE(no.timeFormat(QLocale::LongFormat), QLatin1String("HH.mm.ss t")); + QCOMPARE(no.timeFormat(QLocale::NarrowFormat), QLatin1String("HH:mm")); + QCOMPARE(no.timeFormat(QLocale::ShortFormat), QLatin1String("HH:mm")); + QCOMPARE(no.timeFormat(QLocale::LongFormat), QLatin1String("HH:mm:ss t")); const QLocale id("id_ID"); QCOMPARE(id.timeFormat(QLocale::ShortFormat), QLatin1String("HH.mm")); @@ -2167,9 +2176,9 @@ void tst_QLocale::dateTimeFormat() QCOMPARE(c.dateTimeFormat(QLocale::NarrowFormat), c.dateTimeFormat(QLocale::ShortFormat)); const QLocale no("no_NO"); - QCOMPARE(no.dateTimeFormat(QLocale::NarrowFormat), QLatin1String("dd.MM.yyyy HH.mm")); - QCOMPARE(no.dateTimeFormat(QLocale::ShortFormat), QLatin1String("dd.MM.yyyy HH.mm")); - QCOMPARE(no.dateTimeFormat(QLocale::LongFormat), QLatin1String("dddd d. MMMM yyyy HH.mm.ss t")); + QCOMPARE(no.dateTimeFormat(QLocale::NarrowFormat), QLatin1String("dd.MM.yyyy HH:mm")); + QCOMPARE(no.dateTimeFormat(QLocale::ShortFormat), QLatin1String("dd.MM.yyyy HH:mm")); + QCOMPARE(no.dateTimeFormat(QLocale::LongFormat), QLatin1String("dddd d. MMMM yyyy HH:mm:ss t")); } void tst_QLocale::monthName() @@ -2450,8 +2459,8 @@ void tst_QLocale::textDirection_data() default: break; } - QString testName = QLocalePrivate::languageToCode(QLocale::Language(language)); - QTest::newRow(testName.toLatin1().constData()) << language << int(QLocale::AnyScript) << rightToLeft; + const QLatin1String testName = QLocalePrivate::languageToCode(QLocale::Language(language)); + QTest::newRow(qPrintable(testName)) << language << int(QLocale::AnyScript) << rightToLeft; } QTest::newRow("pa_Arab") << int(QLocale::Punjabi) << int(QLocale::ArabicScript) << true; QTest::newRow("uz_Arab") << int(QLocale::Uzbek) << int(QLocale::ArabicScript) << true; @@ -2467,5 +2476,80 @@ void tst_QLocale::textDirection() QCOMPARE(locale.textDirection() == Qt::RightToLeft, rightToLeft); } +void tst_QLocale::formattedDataSize_data() +{ + QTest::addColumn<QLocale::Language>("language"); + QTest::addColumn<int>("decimalPlaces"); + QTest::addColumn<QLocale::DataSizeFormats>("units"); + QTest::addColumn<int>("bytes"); + QTest::addColumn<QString>("output"); + + struct { + const char *name; + QLocale::Language lang; + const char *bytes; + const char abbrev; + const char sep; // decimal separator + } data[] = { + { "English", QLocale::English, "bytes", 'B', '.' }, + { "French", QLocale::French, "octets", 'o', ',' }, + { "C", QLocale::C, "bytes", 'B', '.' } + }; + + for (const auto row : data) { +#define ROWB(id, deci, num, text) \ + QTest::addRow("%s-%s", row.name, id) \ + << row.lang << deci << format \ + << num << (QString(text) + QChar(' ') + QString(row.bytes)) +#define ROWQ(id, deci, num, head, tail) \ + QTest::addRow("%s-%s", row.name, id) \ + << row.lang << deci << format \ + << num << (QString(head) + QChar(row.sep) + QString(tail) + QChar(row.abbrev)) + + // Metatype system fails to handle raw enum members as format; needs variable + { + const QLocale::DataSizeFormats format = QLocale::DataSizeIecFormat; + ROWB("IEC-0", 2, 0, "0"); + ROWB("IEC-10", 2, 10, "10"); + ROWQ("IEC-12Ki", 2, 12345, "12", "06 Ki"); + ROWQ("IEC-16Ki", 2, 16384, "16", "00 Ki"); + ROWQ("IEC-1235k", 2, 1234567, "1", "18 Mi"); + ROWQ("IEC-1374k", 2, 1374744, "1", "31 Mi"); + ROWQ("IEC-1234M", 2, 1234567890, "1", "15 Gi"); + } + { + const QLocale::DataSizeFormats format = QLocale::DataSizeTraditionalFormat; + ROWB("Trad-0", 2, 0, "0"); + ROWB("Trad-10", 2, 10, "10"); + ROWQ("Trad-12Ki", 2, 12345, "12", "06 k"); + ROWQ("Trad-16Ki", 2, 16384, "16", "00 k"); + ROWQ("Trad-1235k", 2, 1234567, "1", "18 M"); + ROWQ("Trad-1374k", 2, 1374744, "1", "31 M"); + ROWQ("Trad-1234M", 2, 1234567890, "1", "15 G"); + } + { + const QLocale::DataSizeFormats format = QLocale::DataSizeSIFormat; + ROWB("Decimal-0", 2, 0, "0"); + ROWB("Decimal-10", 2, 10, "10"); + ROWQ("Decimal-16Ki", 2, 16384, "16", "38 k"); + ROWQ("Decimal-1234k", 2, 1234567, "1", "23 M"); + ROWQ("Decimal-1374k", 2, 1374744, "1", "37 M"); + ROWQ("Decimal-1234M", 2, 1234567890, "1", "23 G"); + } +#undef ROWQ +#undef ROWB + } +} + +void tst_QLocale::formattedDataSize() +{ + QFETCH(QLocale::Language, language); + QFETCH(int, decimalPlaces); + QFETCH(QLocale::DataSizeFormats, units); + QFETCH(int, bytes); + QFETCH(QString, output); + QCOMPARE(QLocale(language).formattedDataSize(bytes, decimalPlaces, units), output); +} + QTEST_MAIN(tst_QLocale) #include "tst_qlocale.moc" diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp index f42ffc0471..b39444e76f 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -61,6 +61,7 @@ private slots: void iterators(); void keyIterator(); + void keyValueIterator(); void keys_values_uniqueKeys(); void qmultimap_specific(); @@ -863,6 +864,59 @@ void tst_QMap::keyIterator() Q_STATIC_ASSERT(std::is_default_constructible<keyIterator>::value); } +void tst_QMap::keyValueIterator() +{ + QMap<int, int> map; + typedef QMap<int, int>::const_key_value_iterator::value_type entry_type; + + for (int i = 0; i < 100; ++i) + map.insert(i, i * 100); + + auto key_value_it = map.constKeyValueBegin(); + auto it = map.cbegin(); + + for (int i = 0; i < map.size(); ++i) { + QVERIFY(key_value_it != map.constKeyValueEnd()); + QVERIFY(it != map.cend()); + + entry_type pair(it.key(), it.value()); + QCOMPARE(*key_value_it, pair); + ++key_value_it; + ++it; + } + + QVERIFY(key_value_it == map.constKeyValueEnd()); + QVERIFY(it == map.cend()); + + int key = 50; + int value = 50 * 100; + entry_type pair(key, value); + key_value_it = std::find(map.constKeyValueBegin(), map.constKeyValueEnd(), pair); + it = std::find(map.cbegin(), map.cend(), value); + + QVERIFY(key_value_it != map.constKeyValueEnd()); + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + ++it; + ++key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + --it; + --key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + ++it; + ++key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + + --it; + --key_value_it; + QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); + key = 99; + value = 99 * 100; + QCOMPARE(std::count(map.constKeyValueBegin(), map.constKeyValueEnd(), entry_type(key, value)), 1); +} + void tst_QMap::keys_values_uniqueKeys() { QMap<QString, int> map; diff --git a/tests/auto/corelib/tools/qrect/tst_qrect.cpp b/tests/auto/corelib/tools/qrect/tst_qrect.cpp index d3c6412b0d..1c2221ec29 100644 --- a/tests/auto/corelib/tools/qrect/tst_qrect.cpp +++ b/tests/auto/corelib/tools/qrect/tst_qrect.cpp @@ -171,6 +171,7 @@ private slots: void containsPointF_data(); void containsPointF(); void smallRects() const; + void toRect(); }; // Used to work around some floating point precision problems. @@ -4331,5 +4332,36 @@ void tst_QRect::smallRects() const QVERIFY(r1 != r2); } +void tst_QRect::toRect() +{ + for (qreal x = 1.0; x < 2.0; x += 0.25) { + for (qreal y = 1.0; y < 2.0; y += 0.25) { + for (qreal w = 1.0; w < 2.0; w += 0.25) { + for (qreal h = 1.0; h < 2.0; h += 0.25) { + const QRectF rectf(x, y, w, h); + const QRectF rect = rectf.toRect(); + QVERIFY(qAbs(rect.x() - rectf.x()) < 1.0); + QVERIFY(qAbs(rect.y() - rectf.y()) < 1.0); + QVERIFY(qAbs(rect.width() - rectf.width()) < 1.0); + QVERIFY(qAbs(rect.height() - rectf.height()) < 1.0); + QVERIFY(qAbs(rect.right() - rectf.right()) < 1.0); + QVERIFY(qAbs(rect.bottom() - rectf.bottom()) < 1.0); + + const QRectF arect = rectf.toAlignedRect(); + QVERIFY(qAbs(arect.x() - rectf.x()) < 1.0); + QVERIFY(qAbs(arect.y() - rectf.y()) < 1.0); + QVERIFY(qAbs(arect.width() - rectf.width()) < 2.0); + QVERIFY(qAbs(arect.height() - rectf.height()) < 2.0); + QVERIFY(qAbs(arect.right() - rectf.right()) < 1.0); + QVERIFY(qAbs(arect.bottom() - rectf.bottom()) < 1.0); + + QVERIFY(arect.contains(rectf)); + QVERIFY(arect.contains(rect)); + } + } + } + } +} + QTEST_MAIN(tst_QRect) #include "tst_qrect.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp index 2a93250ba5..c828551e44 100644 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp @@ -169,6 +169,7 @@ void consistencyCheck(const QRegularExpressionMatch &match) int length = match.capturedLength(i); QString captured = match.captured(i); QStringRef capturedRef = match.capturedRef(i); + QStringView capturedView = match.capturedView(i); if (!captured.isNull()) { QVERIFY(startPos >= 0); @@ -177,11 +178,13 @@ void consistencyCheck(const QRegularExpressionMatch &match) QVERIFY(endPos >= startPos); QVERIFY((endPos - startPos) == length); QVERIFY(captured == capturedRef); + QVERIFY(captured == capturedView); } else { QVERIFY(startPos == -1); QVERIFY(endPos == -1); QVERIFY((endPos - startPos) == length); QVERIFY(capturedRef.isNull()); + QVERIFY(capturedView.isNull()); } } } diff --git a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp index 21efaede00..a4b06d1b3b 100644 --- a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp +++ b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp @@ -88,7 +88,7 @@ class MySubClass : public MyClass void tst_QScopedPointer::useSubClassInConstructor() { /* Use a syntax which users typically would do. */ - QScopedPointer<MyClass> p(new MyClass()); + QScopedPointer<MyClass> p(new MySubClass()); } void tst_QScopedPointer::dataOnValue() diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 24f16b9911..70ccc72630 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -4726,8 +4726,8 @@ void tst_QString::arg() QString s14( "%1%2%3" ); QCOMPARE( s4.arg("foo"), QLatin1String("[foo]") ); - QCOMPARE( s5.arg("foo"), QLatin1String("[foo]") ); - QCOMPARE( s6.arg("foo"), QLatin1String("[foo]") ); + QCOMPARE( s5.arg(QLatin1String("foo")), QLatin1String("[foo]") ); + QCOMPARE( s6.arg(QStringViewLiteral("foo")), QLatin1String("[foo]") ); QCOMPARE( s7.arg("foo"), QLatin1String("[foo]") ); QCOMPARE( s8.arg("foo"), QLatin1String("[foo %1]") ); QCOMPARE( s8.arg("foo").arg("bar"), QLatin1String("[foo bar]") ); @@ -4788,11 +4788,11 @@ void tst_QString::arg() QCOMPARE( QString("%%%1%%%2").arg("foo").arg("bar"), QLatin1String("%%foo%%bar") ); QCOMPARE( QString("%1").arg("hello", -10), QLatin1String("hello ") ); - QCOMPARE( QString("%1").arg("hello", -5), QLatin1String("hello") ); - QCOMPARE( QString("%1").arg("hello", -2), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QLatin1String("hello"), -5), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QStringViewLiteral("hello"), -2), QLatin1String("hello") ); QCOMPARE( QString("%1").arg("hello", 0), QLatin1String("hello") ); - QCOMPARE( QString("%1").arg("hello", 2), QLatin1String("hello") ); - QCOMPARE( QString("%1").arg("hello", 5), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QLatin1String("hello"), 2), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QStringViewLiteral("hello"), 5), QLatin1String("hello") ); QCOMPARE( QString("%1").arg("hello", 10), QLatin1String(" hello") ); QCOMPARE( QString("%1%1").arg("hello"), QLatin1String("hellohello") ); QCOMPARE( QString("%2%1").arg("hello"), QLatin1String("%2hello") ); @@ -6067,14 +6067,6 @@ void tst_QString::compare_data() lower += QChar(QChar::lowSurrogate(0x10428)); QTest::newRow("data8") << upper << lower << -1 << 0; - QTest::newRow("vectorized-boundaries-7") << QString("1234567") << QString("abcdefg") << -1 << -1; - QTest::newRow("vectorized-boundaries-8") << QString("12345678") << QString("abcdefgh") << -1 << -1; - QTest::newRow("vectorized-boundaries-9") << QString("123456789") << QString("abcdefghi") << -1 << -1; - - QTest::newRow("vectorized-boundaries-15") << QString("123456789012345") << QString("abcdefghiklmnop") << -1 << -1; - QTest::newRow("vectorized-boundaries-16") << QString("1234567890123456") << QString("abcdefghiklmnopq") << -1 << -1; - QTest::newRow("vectorized-boundaries-17") << QString("12345678901234567") << QString("abcdefghiklmnopqr") << -1 << -1; - // embedded nulls // These don't work as of now. It's OK that these don't work since \0 is not a valid unicode /*QTest::newRow("data10") << QString(QByteArray("\0", 1)) << QString(QByteArray("\0", 1)) << 0 << 0; @@ -6083,6 +6075,44 @@ void tst_QString::compare_data() QTest::newRow("data13") << QString("ab\0c") << QString(QByteArray("ab\0c", 4)) << 0 << 0; QTest::newRow("data14") << QString(QByteArray("ab\0c", 4)) << QString("abc") << -1 << -1; QTest::newRow("data15") << QString("abc") << QString(QByteArray("ab\0c", 4)) << 1 << 1;*/ + + // All tests below (generated by the 3 for-loops) are meant to excercise the vectorized versions + // of ucstrncmp. + + QString in1, in2; + for (int i = 0; i < 70; ++i) { + in1 += QString::number(i % 10); + in2 += QString::number((70 - i + 1) % 10); + } + Q_ASSERT(in1.length() == in2.length()); + Q_ASSERT(in1 != in2); + Q_ASSERT(in1.at(0) < in2.at(0)); + for (int i = 0; i < in1.length(); ++i) { + Q_ASSERT(in1.at(i) != in2.at(i)); + } + + for (int i = 1; i <= 65; ++i) { + QString inp1 = in1.left(i); + QString inp2 = in2.left(i); + QTest::addRow("all-different-%d", i) << inp1 << inp2 << -1 << -1; + } + + for (int i = 1; i <= 65; ++i) { + QString start(i - 1, 'a'); + + QString in = start + QLatin1Char('a'); + QTest::addRow("all-same-%d", i) << in << in << 0 << 0; + + QString in2 = start + QLatin1Char('b'); + QTest::addRow("last-different-%d", i) << in << in2 << -1 << -1; + } + + for (int i = 0; i < 16; ++i) { + QString in1(16, 'a'); + QString in2 = in1; + in2[i] = 'b'; + QTest::addRow("all-same-except-char-%d", i) << in1 << in2 << -1 << -1; + } } static bool isLatin(const QString &s) diff --git a/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro b/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro index bc38b17949..a4e91e38bd 100644 --- a/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro +++ b/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro @@ -3,3 +3,4 @@ TARGET = tst_qstringapisymmetry QT = core testlib SOURCES = tst_qstringapisymmetry.cpp qtConfig(c++14): CONFIG += c++14 +qtConfig(c++1z): CONFIG += c++1z diff --git a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp index 9d9b47b61e..e482b409a5 100644 --- a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -31,6 +31,7 @@ #undef QT_ASCII_CAST_WARNINGS #include <QString> +#include <QStringView> #include <QChar> #include <QStringRef> #include <QLatin1String> @@ -44,6 +45,7 @@ Q_DECLARE_METATYPE(QStringRef) template <typename T> QString toQString(const T &t) { return QString(t); } QString toQString(const QStringRef &ref) { return ref.toString(); } +QString toQString(QStringView view) { return view.toString(); } // FIXME: these are missing at the time of writing, add them, then remove the dummies here: #define MAKE_RELOP(op, A1, A2) \ @@ -81,13 +83,15 @@ class tst_QStringApiSymmetry : public QObject void compare_impl() const; private Q_SLOTS: - // test all combinations of {QChar, QStringRef, QString, QLatin1String, QByteArray, const char*} + // test all combinations of {QChar, QStringRef, QString, QStringView, QLatin1String, QByteArray, const char*} void compare_QChar_QChar_data() { compare_data(false); } void compare_QChar_QChar() { compare_impl<QChar, QChar>(); } void compare_QChar_QStringRef_data() { compare_data(false); } void compare_QChar_QStringRef() { compare_impl<QChar, QStringRef>(); } void compare_QChar_QString_data() { compare_data(false); } void compare_QChar_QString() { compare_impl<QChar, QString>(); } + void compare_QChar_QStringView_data() { compare_data(false); } + void compare_QChar_QStringView() { compare_impl<QChar, QStringView>(); } void compare_QChar_QLatin1String_data() { compare_data(false); } void compare_QChar_QLatin1String() { compare_impl<QChar, QLatin1String>(); } void compare_QChar_QByteArray_data() { compare_data(false); } @@ -101,6 +105,8 @@ private Q_SLOTS: void compare_QStringRef_QStringRef() { compare_impl<QStringRef, QStringRef>(); } void compare_QStringRef_QString_data() { compare_data(); } void compare_QStringRef_QString() { compare_impl<QStringRef, QString>(); } + void compare_QStringRef_QStringView_data() { compare_data(); } + void compare_QStringRef_QStringView() { compare_impl<QStringRef, QStringView>(); } void compare_QStringRef_QLatin1String_data() { compare_data(); } void compare_QStringRef_QLatin1String() { compare_impl<QStringRef, QLatin1String>(); } void compare_QStringRef_QByteArray_data() { compare_data(); } @@ -114,6 +120,8 @@ private Q_SLOTS: void compare_QString_QStringRef() { compare_impl<QString, QStringRef>(); } void compare_QString_QString_data() { compare_data(); } void compare_QString_QString() { compare_impl<QString, QString>(); } + void compare_QString_QStringView_data() { compare_data(); } + void compare_QString_QStringView() { compare_impl<QString, QStringView>(); } void compare_QString_QLatin1String_data() { compare_data(); } void compare_QString_QLatin1String() { compare_impl<QString, QLatin1String>(); } void compare_QString_QByteArray_data() { compare_data(); } @@ -121,12 +129,25 @@ private Q_SLOTS: void compare_QString_const_char_star_data() { compare_data(); } void compare_QString_const_char_star() { compare_impl<QString, const char *>(); } + void compare_QStringView_QChar_data() { compare_data(false); } + void compare_QStringView_QChar() { compare_impl<QStringView, QChar>(); } + void compare_QStringView_QStringRef_data() { compare_data(); } + void compare_QStringView_QStringRef() { compare_impl<QStringView, QStringRef>(); } + void compare_QStringView_QString_data() { compare_data(); } + void compare_QStringView_QString() { compare_impl<QStringView, QString>(); } + void compare_QStringView_QStringView_data() { compare_data(); } + void compare_QStringView_QStringView() { compare_impl<QStringView, QStringView>(); } + void compare_QStringView_QLatin1String_data() { compare_data(); } + void compare_QStringView_QLatin1String() { compare_impl<QStringView, QLatin1String>(); } + void compare_QLatin1String_QChar_data() { compare_data(false); } void compare_QLatin1String_QChar() { compare_impl<QLatin1String, QChar>(); } void compare_QLatin1String_QStringRef_data() { compare_data(); } void compare_QLatin1String_QStringRef() { compare_impl<QLatin1String, QStringRef>(); } void compare_QLatin1String_QString_data() { compare_data(); } void compare_QLatin1String_QString() { compare_impl<QLatin1String, QString>(); } + void compare_QLatin1String_QStringView_data() { compare_data(); } + void compare_QLatin1String_QStringView() { compare_impl<QLatin1String, QStringView>(); } void compare_QLatin1String_QLatin1String_data() { compare_data(); } void compare_QLatin1String_QLatin1String() { compare_impl<QLatin1String, QLatin1String>(); } void compare_QLatin1String_QByteArray_data() { compare_data(); } @@ -160,6 +181,181 @@ private Q_SLOTS: //void compare_const_char_star_const_char_star_data() { compare_data(); } //void compare_const_char_star_const_char_star() { compare_impl<const char *, const char *>(); } +private: + void startsWith_data(bool rhsIsQChar = false); + template <typename Haystack, typename Needle> void startsWith_impl() const; + + void endsWith_data(bool rhsIsQChar = false); + template <typename Haystack, typename Needle> void endsWith_impl() const; + +private Q_SLOTS: + // test all combinations of {QString, QStringRef, QStringView, QLatin1String} x {QString, QStringRef, QStringView, QLatin1String, QChar}: + void startsWith_QString_QString_data() { startsWith_data(); } + void startsWith_QString_QString() { startsWith_impl<QString, QString>(); } + void startsWith_QString_QStringRef_data() { startsWith_data(); } + void startsWith_QString_QStringRef() { startsWith_impl<QString, QStringRef>(); } + void startsWith_QString_QStringView_data() { startsWith_data(); } + void startsWith_QString_QStringView() { startsWith_impl<QString, QStringView>(); } + void startsWith_QString_QLatin1String_data() { startsWith_data(); } + void startsWith_QString_QLatin1String() { startsWith_impl<QString, QLatin1String>(); } + void startsWith_QString_QChar_data() { startsWith_data(false); } + void startsWith_QString_QChar() { startsWith_impl<QString, QChar>(); } + + void startsWith_QStringRef_QString_data() { startsWith_data(); } + void startsWith_QStringRef_QString() { startsWith_impl<QStringRef, QString>(); } + void startsWith_QStringRef_QStringRef_data() { startsWith_data(); } + void startsWith_QStringRef_QStringRef() { startsWith_impl<QStringRef, QStringRef>(); } + void startsWith_QStringRef_QStringView_data() { startsWith_data(); } + void startsWith_QStringRef_QStringView() { startsWith_impl<QStringRef, QStringView>(); } + void startsWith_QStringRef_QLatin1String_data() { startsWith_data(); } + void startsWith_QStringRef_QLatin1String() { startsWith_impl<QStringRef, QLatin1String>(); } + void startsWith_QStringRef_QChar_data() { startsWith_data(false); } + void startsWith_QStringRef_QChar() { startsWith_impl<QStringRef, QChar>(); } + + void startsWith_QStringView_QString_data() { startsWith_data(); } + void startsWith_QStringView_QString() { startsWith_impl<QStringView, QString>(); } + void startsWith_QStringView_QStringRef_data() { startsWith_data(); } + void startsWith_QStringView_QStringRef() { startsWith_impl<QStringView, QStringRef>(); } + void startsWith_QStringView_QStringView_data() { startsWith_data(); } + void startsWith_QStringView_QStringView() { startsWith_impl<QStringView, QStringView>(); } + void startsWith_QStringView_QLatin1String_data() { startsWith_data(); } + void startsWith_QStringView_QLatin1String() { startsWith_impl<QStringView, QLatin1String>(); } + void startsWith_QStringView_QChar_data() { startsWith_data(false); } + void startsWith_QStringView_QChar() { startsWith_impl<QStringView, QChar>(); } + + void startsWith_QLatin1String_QString_data() { startsWith_data(); } + void startsWith_QLatin1String_QString() { startsWith_impl<QLatin1String, QString>(); } + void startsWith_QLatin1String_QStringRef_data() { startsWith_data(); } + void startsWith_QLatin1String_QStringRef() { startsWith_impl<QLatin1String, QStringRef>(); } + void startsWith_QLatin1String_QStringView_data() { startsWith_data(); } + void startsWith_QLatin1String_QStringView() { startsWith_impl<QLatin1String, QStringView>(); } + void startsWith_QLatin1String_QLatin1String_data() { startsWith_data(); } + void startsWith_QLatin1String_QLatin1String() { startsWith_impl<QLatin1String, QLatin1String>(); } + void startsWith_QLatin1String_QChar_data() { startsWith_data(false); } + void startsWith_QLatin1String_QChar() { startsWith_impl<QLatin1String, QChar>(); } + + void endsWith_QString_QString_data() { endsWith_data(); } + void endsWith_QString_QString() { endsWith_impl<QString, QString>(); } + void endsWith_QString_QStringRef_data() { endsWith_data(); } + void endsWith_QString_QStringRef() { endsWith_impl<QString, QStringRef>(); } + void endsWith_QString_QStringView_data() { endsWith_data(); } + void endsWith_QString_QStringView() { endsWith_impl<QString, QStringView>(); } + void endsWith_QString_QLatin1String_data() { endsWith_data(); } + void endsWith_QString_QLatin1String() { endsWith_impl<QString, QLatin1String>(); } + void endsWith_QString_QChar_data() { endsWith_data(false); } + void endsWith_QString_QChar() { endsWith_impl<QString, QChar>(); } + + void endsWith_QStringRef_QString_data() { endsWith_data(); } + void endsWith_QStringRef_QString() { endsWith_impl<QStringRef, QString>(); } + void endsWith_QStringRef_QStringRef_data() { endsWith_data(); } + void endsWith_QStringRef_QStringRef() { endsWith_impl<QStringRef, QStringRef>(); } + void endsWith_QStringRef_QStringView_data() { endsWith_data(); } + void endsWith_QStringRef_QStringView() { endsWith_impl<QStringRef, QStringView>(); } + void endsWith_QStringRef_QLatin1String_data() { endsWith_data(); } + void endsWith_QStringRef_QLatin1String() { endsWith_impl<QStringRef, QLatin1String>(); } + void endsWith_QStringRef_QChar_data() { endsWith_data(false); } + void endsWith_QStringRef_QChar() { endsWith_impl<QStringRef, QChar>(); } + + void endsWith_QStringView_QString_data() { endsWith_data(); } + void endsWith_QStringView_QString() { endsWith_impl<QStringView, QString>(); } + void endsWith_QStringView_QStringRef_data() { endsWith_data(); } + void endsWith_QStringView_QStringRef() { endsWith_impl<QStringView, QStringRef>(); } + void endsWith_QStringView_QStringView_data() { endsWith_data(); } + void endsWith_QStringView_QStringView() { endsWith_impl<QStringView, QStringView>(); } + void endsWith_QStringView_QLatin1String_data() { endsWith_data(); } + void endsWith_QStringView_QLatin1String() { endsWith_impl<QStringView, QLatin1String>(); } + void endsWith_QStringView_QChar_data() { endsWith_data(false); } + void endsWith_QStringView_QChar() { endsWith_impl<QStringView, QChar>(); } + + void endsWith_QLatin1String_QString_data() { endsWith_data(); } + void endsWith_QLatin1String_QString() { endsWith_impl<QLatin1String, QString>(); } + void endsWith_QLatin1String_QStringRef_data() { endsWith_data(); } + void endsWith_QLatin1String_QStringRef() { endsWith_impl<QLatin1String, QStringRef>(); } + void endsWith_QLatin1String_QStringView_data() { endsWith_data(); } + void endsWith_QLatin1String_QStringView() { endsWith_impl<QLatin1String, QStringView>(); } + void endsWith_QLatin1String_QLatin1String_data() { endsWith_data(); } + void endsWith_QLatin1String_QLatin1String() { endsWith_impl<QLatin1String, QLatin1String>(); } + void endsWith_QLatin1String_QChar_data() { endsWith_data(false); } + void endsWith_QLatin1String_QChar() { endsWith_impl<QLatin1String, QChar>(); } + +private: + void mid_data(); + template <typename String> void mid_impl(); + + void left_data(); + template <typename String> void left_impl(); + + void right_data(); + template <typename String> void right_impl(); + + void chop_data(); + template <typename String> void chop_impl(); + + void truncate_data() { left_data(); } + template <typename String> void truncate_impl(); + +private Q_SLOTS: + + void mid_QString_data() { mid_data(); } + void mid_QString() { mid_impl<QString>(); } + void mid_QStringRef_data() { mid_data(); } + void mid_QStringRef() { mid_impl<QStringRef>(); } + void mid_QStringView_data() { mid_data(); } + void mid_QStringView() { mid_impl<QStringView>(); } + void mid_QLatin1String_data() { mid_data(); } + void mid_QLatin1String() { mid_impl<QLatin1String>(); } + void mid_QByteArray_data() { mid_data(); } + void mid_QByteArray() { mid_impl<QByteArray>(); } + + void left_truncate_QString_data() { left_data(); } + void left_truncate_QString() { left_impl<QString>(); } + void left_truncate_QStringRef_data() { left_data(); } + void left_truncate_QStringRef() { left_impl<QStringRef>(); } + void left_truncate_QStringView_data() { left_data(); } + void left_truncate_QStringView() { left_impl<QStringView>(); } + void left_truncate_QLatin1String_data() { left_data(); } + void left_truncate_QLatin1String() { left_impl<QLatin1String>(); } + void left_truncate_QByteArray_data() { left_data(); } + void left_truncate_QByteArray() { left_impl<QByteArray>(); } + + void right_QString_data() { right_data(); } + void right_QString() { right_impl<QString>(); } + void right_QStringRef_data() { right_data(); } + void right_QStringRef() { right_impl<QStringRef>(); } + void right_QStringView_data() { right_data(); } + void right_QStringView() { right_impl<QStringView>(); } + void right_QLatin1String_data() { right_data(); } + void right_QLatin1String() { right_impl<QLatin1String>(); } + void right_QByteArray_data() { right_data(); } + void right_QByteArray() { right_impl<QByteArray>(); } + + void chop_QString_data() { chop_data(); } + void chop_QString() { chop_impl<QString>(); } + void chop_QStringRef_data() { chop_data(); } + void chop_QStringRef() { chop_impl<QStringRef>(); } + void chop_QStringView_data() { chop_data(); } + void chop_QStringView() { chop_impl<QStringView>(); } + void chop_QLatin1String_data() { chop_data(); } + void chop_QLatin1String() { chop_impl<QLatin1String>(); } + void chop_QByteArray_data() { chop_data(); } + void chop_QByteArray() { chop_impl<QByteArray>(); } + +private: + void trimmed_data(); + template <typename String> void trimmed_impl(); + +private Q_SLOTS: + void trim_trimmed_QString_data() { trimmed_data(); } + void trim_trimmed_QString() { trimmed_impl<QString>(); } + void trim_trimmed_QStringRef_data() { trimmed_data(); } + void trim_trimmed_QStringRef() { trimmed_impl<QStringRef>(); } + void trim_trimmed_QStringView_data() { trimmed_data(); } + void trim_trimmed_QStringView() { trimmed_impl<QStringView>(); } + void trim_trimmed_QLatin1String_data() { trimmed_data(); } + void trim_trimmed_QLatin1String() { trimmed_impl<QLatin1String>(); } + void trim_trimmed_QByteArray_data() { trimmed_data(); } + void trim_trimmed_QByteArray() { trimmed_impl<QByteArray>(); } + // // UTF-16-only checks: // @@ -183,21 +379,29 @@ private Q_SLOTS: void toLocal8Bit_QString() { toLocal8Bit_impl<QString>(); } void toLocal8Bit_QStringRef_data() { toLocal8Bit_data(); } void toLocal8Bit_QStringRef() { toLocal8Bit_impl<QStringRef>(); } + void toLocal8Bit_QStringView_data() { toLocal8Bit_data(); } + void toLocal8Bit_QStringView() { toLocal8Bit_impl<QStringView>(); } void toLatin1_QString_data() { toLatin1_data(); } void toLatin1_QString() { toLatin1_impl<QString>(); } void toLatin1_QStringRef_data() { toLatin1_data(); } void toLatin1_QStringRef() { toLatin1_impl<QStringRef>(); } + void toLatin1_QStringView_data() { toLatin1_data(); } + void toLatin1_QStringView() { toLatin1_impl<QStringView>(); } void toUtf8_QString_data() { toUtf8_data(); } void toUtf8_QString() { toUtf8_impl<QString>(); } void toUtf8_QStringRef_data() { toUtf8_data(); } void toUtf8_QStringRef() { toUtf8_impl<QStringRef>(); } + void toUtf8_QStringView_data() { toUtf8_data(); } + void toUtf8_QStringView() { toUtf8_impl<QStringView>(); } void toUcs4_QString_data() { toUcs4_data(); } void toUcs4_QString() { toUcs4_impl<QString>(); } void toUcs4_QStringRef_data() { toUcs4_data(); } void toUcs4_QStringRef() { toUcs4_impl<QStringRef>(); } + void toUcs4_QStringView_data() { toUcs4_data(); } + void toUcs4_QStringView() { toUcs4_impl<QStringView>(); } }; void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty) @@ -217,6 +421,9 @@ void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty) QTest::newRow("null <> empty") << QStringRef() << QLatin1String() << QStringRef(&empty) << QLatin1String("") << 0 << 0; + QTest::newRow("empty <> null") << QStringRef(&empty) << QLatin1String("") + << QStringRef() << QLatin1String() + << 0 << 0; } #define ROW(lhs, rhs) \ @@ -239,10 +446,20 @@ void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty) #undef ROW } +template <typename String> String detached(String s) +{ + if (!s.isNull()) { // detaching loses nullness, but we need to preserve it + auto d = s.data(); + Q_UNUSED(d); + } + return s; +} + template <class Str> Str make(const QStringRef &sf, QLatin1String l1, const QByteArray &u8); template <> QChar make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.isEmpty() ? QChar() : sf.at(0); } template <> QStringRef make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf; } template <> QString make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.toString(); } +template <> QStringView make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf; } template <> QLatin1String make(const QStringRef &, QLatin1String l1, const QByteArray &) { return l1; } template <> QByteArray make(const QStringRef &, QLatin1String, const QByteArray &u8) { return u8; } template <> const char * make(const QStringRef &, QLatin1String, const QByteArray &u8) { return u8.data(); } @@ -260,6 +477,38 @@ struct has_nothrow_compare { }; template <typename LHS, typename RHS> +struct has_qCompareStrings { + enum { value = !std::is_same<LHS, QChar>::value && !std::is_same<RHS, QChar>::value && + !is_utf8_encoded<LHS>::value && !is_utf8_encoded<RHS>::value }; +}; + +template <typename LHS, typename RHS> +using if_has_qCompareStrings = typename std::enable_if<has_qCompareStrings<LHS, RHS>::value, bool>::type; + +template <typename LHS, typename RHS> +using if_lacks_qCompareStrings = typename std::enable_if<!has_qCompareStrings<LHS, RHS>::value, bool>::type; + +static inline Q_DECL_CONSTEXPR int sign(int x) Q_DECL_NOTHROW +{ + return x < 0 ? -1 : + x > 0 ? +1 : + /*else*/ 0 ; +} + +template <typename LHS, typename RHS, if_has_qCompareStrings<LHS, RHS> = true> +int qCompareStringsWrapper(const LHS &lhs, const RHS &rhs, Qt::CaseSensitivity cs, int) + Q_DECL_NOEXCEPT_EXPR(noexcept(qCompareStrings(lhs, rhs, cs))) +{ + return qCompareStrings(lhs, rhs, cs); +} + +template <typename LHS, typename RHS, if_lacks_qCompareStrings<LHS, RHS> = true> +int qCompareStringsWrapper(const LHS &, const RHS &, Qt::CaseSensitivity, int result) +{ + return result; +} + +template <typename LHS, typename RHS> void tst_QStringApiSymmetry::compare_impl() const { QFETCH(QStringRef, lhsUnicode); @@ -267,6 +516,7 @@ void tst_QStringApiSymmetry::compare_impl() const QFETCH(QStringRef, rhsUnicode); QFETCH(QLatin1String, rhsLatin1); QFETCH(int, caseSensitiveCompareResult); + QFETCH(const int, caseInsensitiveCompareResult); const auto lhsU8 = lhsUnicode.toUtf8(); const auto rhsU8 = rhsUnicode.toUtf8(); @@ -283,6 +533,10 @@ void tst_QStringApiSymmetry::compare_impl() const # define QVERIFY_NOEXCEPT(expr) #endif + QCOMPARE(sign(qCompareStringsWrapper(lhs, rhs, Qt::CaseSensitive, caseSensitiveCompareResult)), + sign(caseSensitiveCompareResult)); + QCOMPARE(sign(qCompareStringsWrapper(lhs, rhs, Qt::CaseInsensitive, caseInsensitiveCompareResult)), + sign(caseInsensitiveCompareResult)); #define CHECK(op) \ QVERIFY_NOEXCEPT(lhs op rhs); \ do { if (caseSensitiveCompareResult op 0) { \ @@ -300,6 +554,529 @@ void tst_QStringApiSymmetry::compare_impl() const #undef CHECK } +static QString empty = QLatin1String(""); +// the tests below rely on the fact that these objects' names match their contents: +static QString a = QStringLiteral("a"); +static QString A = QStringLiteral("A"); +static QString b = QStringLiteral("b"); +static QString B = QStringLiteral("B"); +static QString c = QStringLiteral("c"); +static QString C = QStringLiteral("C"); +static QString ab = QStringLiteral("ab"); +static QString aB = QStringLiteral("aB"); +static QString Ab = QStringLiteral("Ab"); +static QString AB = QStringLiteral("AB"); +static QString bc = QStringLiteral("bc"); +static QString bC = QStringLiteral("bC"); +static QString Bc = QStringLiteral("Bc"); +static QString BC = QStringLiteral("BC"); +static QString abc = QStringLiteral("abc"); +static QString abC = QStringLiteral("abC"); +static QString aBc = QStringLiteral("aBc"); +static QString aBC = QStringLiteral("aBC"); +static QString Abc = QStringLiteral("Abc"); +static QString AbC = QStringLiteral("AbC"); +static QString ABc = QStringLiteral("ABc"); +static QString ABC = QStringLiteral("ABC"); + +void tst_QStringApiSymmetry::startsWith_data(bool rhsHasVariableLength) +{ + QTest::addColumn<QStringRef>("haystackU16"); + QTest::addColumn<QLatin1String>("haystackL1"); + QTest::addColumn<QStringRef>("needleU16"); + QTest::addColumn<QLatin1String>("needleL1"); + QTest::addColumn<bool>("resultCS"); + QTest::addColumn<bool>("resultCIS"); + + if (rhsHasVariableLength) { + QTest::addRow("null ~= ^null") << QStringRef() << QLatin1String() + << QStringRef() << QLatin1String() << true << true; + QTest::addRow("empty ~= ^null") << QStringRef(&empty) << QLatin1String("") + << QStringRef() << QLatin1String() << true << true; + QTest::addRow("a ~= ^null") << QStringRef(&a) << QLatin1String("a") + << QStringRef() << QLatin1String() << true << true; + QTest::addRow("null ~= ^empty") << QStringRef() << QLatin1String() + << QStringRef(&empty) << QLatin1String("") << false << false; + QTest::addRow("a ~= ^empty") << QStringRef(&a) << QLatin1String("a") + << QStringRef(&empty) << QLatin1String("") << true << true; + QTest::addRow("empty ~= ^empty") << QStringRef(&empty) << QLatin1String("") + << QStringRef(&empty) << QLatin1String("") << true << true; + } + QTest::addRow("null ~= ^a") << QStringRef() << QLatin1String() + << QStringRef(&a) << QLatin1String("a") << false << false; + QTest::addRow("empty ~= ^a") << QStringRef(&empty) << QLatin1String("") + << QStringRef(&a) << QLatin1String("a") << false << false; + +#define ROW(h, n, cs, cis) \ + QTest::addRow("%s ~= ^%s", #h, #n) << QStringRef(&h) << QLatin1String(#h) \ + << QStringRef(&n) << QLatin1String(#n) \ + << bool(cs) << bool(cis) + ROW(a, a, 1, 1); + ROW(a, A, 0, 1); + ROW(a, b, 0, 0); + + if (rhsHasVariableLength) + ROW(a, aB, 0, 0); + + ROW(ab, a, 1, 1); + if (rhsHasVariableLength) { + ROW(ab, ab, 1, 1); + ROW(ab, aB, 0, 1); + ROW(ab, Ab, 0, 1); + } + ROW(ab, c, 0, 0); + + if (rhsHasVariableLength) + ROW(ab, abc, 0, 0); + + ROW(Abc, c, 0, 0); + if (rhsHasVariableLength) { + ROW(Abc, ab, 0, 1); + ROW(Abc, aB, 0, 1); + ROW(Abc, Ab, 1, 1); + ROW(Abc, AB, 0, 1); + ROW(aBC, ab, 0, 1); + ROW(aBC, aB, 1, 1); + ROW(aBC, Ab, 0, 1); + ROW(aBC, AB, 0, 1); + } + ROW(ABC, b, 0, 0); + ROW(ABC, a, 0, 1); +#undef ROW +} + +template <typename Haystack, typename Needle> +void tst_QStringApiSymmetry::startsWith_impl() const +{ + QFETCH(const QStringRef, haystackU16); + QFETCH(const QLatin1String, haystackL1); + QFETCH(const QStringRef, needleU16); + QFETCH(const QLatin1String, needleL1); + QFETCH(const bool, resultCS); + QFETCH(const bool, resultCIS); + + const auto haystackU8 = haystackU16.toUtf8(); + const auto needleU8 = needleU16.toUtf8(); + + const auto haystack = make<Haystack>(haystackU16, haystackL1, haystackU8); + const auto needle = make<Needle>(needleU16, needleL1, needleU8); + + QCOMPARE(haystack.startsWith(needle), resultCS); + QCOMPARE(haystack.startsWith(needle, Qt::CaseSensitive), resultCS); + QCOMPARE(haystack.startsWith(needle, Qt::CaseInsensitive), resultCIS); +} + +void tst_QStringApiSymmetry::endsWith_data(bool rhsHasVariableLength) +{ + QTest::addColumn<QStringRef>("haystackU16"); + QTest::addColumn<QLatin1String>("haystackL1"); + QTest::addColumn<QStringRef>("needleU16"); + QTest::addColumn<QLatin1String>("needleL1"); + QTest::addColumn<bool>("resultCS"); + QTest::addColumn<bool>("resultCIS"); + + if (rhsHasVariableLength) { + QTest::addRow("null ~= null$") << QStringRef() << QLatin1String() + << QStringRef() << QLatin1String() << true << true; + QTest::addRow("empty ~= null$") << QStringRef(&empty) << QLatin1String("") + << QStringRef() << QLatin1String() << true << true; + QTest::addRow("a ~= null$") << QStringRef(&a) << QLatin1String("a") + << QStringRef() << QLatin1String() << true << true; + QTest::addRow("null ~= empty$") << QStringRef() << QLatin1String() + << QStringRef(&empty) << QLatin1String("") << false << false; + QTest::addRow("a ~= empty$") << QStringRef(&a) << QLatin1String("a") + << QStringRef(&empty) << QLatin1String("") << true << true; + QTest::addRow("empty ~= empty$") << QStringRef(&empty) << QLatin1String("") + << QStringRef(&empty) << QLatin1String("") << true << true; + } + QTest::addRow("null ~= a$") << QStringRef() << QLatin1String() + << QStringRef(&a) << QLatin1String("a") << false << false; + QTest::addRow("empty ~= a$") << QStringRef(&empty) << QLatin1String("") + << QStringRef(&a) << QLatin1String("a") << false << false; + +#define ROW(h, n, cs, cis) \ + QTest::addRow("%s ~= %s$", #h, #n) << QStringRef(&h) << QLatin1String(#h) \ + << QStringRef(&n) << QLatin1String(#n) \ + << bool(cs) << bool(cis) + ROW(a, a, 1, 1); + ROW(a, A, 0, 1); + ROW(a, b, 0, 0); + + if (rhsHasVariableLength) + ROW(b, ab, 0, 0); + + ROW(ab, b, 1, 1); + if (rhsHasVariableLength) { + ROW(ab, ab, 1, 1); + ROW(ab, aB, 0, 1); + ROW(ab, Ab, 0, 1); + } + ROW(ab, c, 0, 0); + + if (rhsHasVariableLength) + ROW(bc, abc, 0, 0); + + ROW(Abc, c, 1, 1); + if (rhsHasVariableLength) { + ROW(Abc, bc, 1, 1); + ROW(Abc, bC, 0, 1); + ROW(Abc, Bc, 0, 1); + ROW(Abc, BC, 0, 1); + ROW(aBC, bc, 0, 1); + ROW(aBC, bC, 0, 1); + ROW(aBC, Bc, 0, 1); + ROW(aBC, BC, 1, 1); + } + ROW(ABC, b, 0, 0); + ROW(ABC, a, 0, 0); +#undef ROW +} + +template <typename Haystack, typename Needle> +void tst_QStringApiSymmetry::endsWith_impl() const +{ + QFETCH(const QStringRef, haystackU16); + QFETCH(const QLatin1String, haystackL1); + QFETCH(const QStringRef, needleU16); + QFETCH(const QLatin1String, needleL1); + QFETCH(const bool, resultCS); + QFETCH(const bool, resultCIS); + + const auto haystackU8 = haystackU16.toUtf8(); + const auto needleU8 = needleU16.toUtf8(); + + const auto haystack = make<Haystack>(haystackU16, haystackL1, haystackU8); + const auto needle = make<Needle>(needleU16, needleL1, needleU8); + + QCOMPARE(haystack.endsWith(needle), resultCS); + QCOMPARE(haystack.endsWith(needle, Qt::CaseSensitive), resultCS); + QCOMPARE(haystack.endsWith(needle, Qt::CaseInsensitive), resultCIS); +} + +void tst_QStringApiSymmetry::mid_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("pos"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + QTest::addColumn<QStringRef>("result2"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << 0 << QStringRef() << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << 0 << QStringRef(&empty) << QStringRef(&empty); + + // Some classes' mid() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, p, n, r1, r2) \ + QTest::addRow("%s%d%d", #base, p, n) << QStringRef(&base) << QLatin1String(#base) << p << n << QStringRef(&r1) << QStringRef(&r2) + + ROW(a, 0, 0, a, empty); + ROW(a, 0, 1, a, a); + ROW(a, 1, 0, empty, empty); + + ROW(ab, 0, 0, ab, empty); + ROW(ab, 0, 1, ab, a); + ROW(ab, 0, 2, ab, ab); + ROW(ab, 1, 0, b, empty); + ROW(ab, 1, 1, b, b); + ROW(ab, 2, 0, empty, empty); + + ROW(abc, 0, 0, abc, empty); + ROW(abc, 0, 1, abc, a); + ROW(abc, 0, 2, abc, ab); + ROW(abc, 0, 3, abc, abc); + ROW(abc, 1, 0, bc, empty); + ROW(abc, 1, 1, bc, b); + ROW(abc, 1, 2, bc, bc); + ROW(abc, 2, 0, c, empty); + ROW(abc, 2, 1, c, c); + ROW(abc, 3, 0, empty, empty); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::mid_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, pos); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + QFETCH(const QStringRef, result2); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + { + const auto mid = s.mid(pos); + const auto mid2 = s.mid(pos, n); + + QCOMPARE(mid, result); + QCOMPARE(mid.isNull(), result.isNull()); + QCOMPARE(mid.isEmpty(), result.isEmpty()); + + QCOMPARE(mid2, result2); + QCOMPARE(mid2.isNull(), result2.isNull()); + QCOMPARE(mid2.isEmpty(), result2.isEmpty()); + } + { + const auto mid = detached(s).mid(pos); + const auto mid2 = detached(s).mid(pos, n); + + QCOMPARE(mid, result); + QCOMPARE(mid.isNull(), result.isNull()); + QCOMPARE(mid.isEmpty(), result.isEmpty()); + + QCOMPARE(mid2, result2); + QCOMPARE(mid2.isNull(), result2.isNull()); + QCOMPARE(mid2.isEmpty(), result2.isEmpty()); + } +} + +void tst_QStringApiSymmetry::left_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty); + + // Some classes' left() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, n, res) \ + QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res); + + ROW(a, 0, empty); + ROW(a, 1, a); + + ROW(ab, 0, empty); + ROW(ab, 1, a); + ROW(ab, 2, ab); + + ROW(abc, 0, empty); + ROW(abc, 1, a); + ROW(abc, 2, ab); + ROW(abc, 3, abc); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::left_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + { + const auto left = s.left(n); + + QCOMPARE(left, result); + QCOMPARE(left.isNull(), result.isNull()); + QCOMPARE(left.isEmpty(), result.isEmpty()); + } + { + const auto left = detached(s).left(n); + + QCOMPARE(left, result); + QCOMPARE(left.isNull(), result.isNull()); + QCOMPARE(left.isEmpty(), result.isEmpty()); + } + { + auto left = s; + left.truncate(n); + + QCOMPARE(left, result); + QCOMPARE(left.isNull(), result.isNull()); + QCOMPARE(left.isEmpty(), result.isEmpty()); + } +} + +void tst_QStringApiSymmetry::right_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty); + + // Some classes' right() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, n, res) \ + QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res); + + ROW(a, 0, empty); + ROW(a, 1, a); + + ROW(ab, 0, empty); + ROW(ab, 1, b); + ROW(ab, 2, ab); + + ROW(abc, 0, empty); + ROW(abc, 1, c); + ROW(abc, 2, bc); + ROW(abc, 3, abc); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::right_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + { + const auto right = s.right(n); + + QCOMPARE(right, result); + QCOMPARE(right.isNull(), result.isNull()); + QCOMPARE(right.isEmpty(), result.isEmpty()); + } + { + const auto right = detached(s).right(n); + + QCOMPARE(right, result); + QCOMPARE(right.isNull(), result.isNull()); + QCOMPARE(right.isEmpty(), result.isEmpty()); + } +} + +void tst_QStringApiSymmetry::chop_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty); + + // Some classes' truncate() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, n, res) \ + QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res); + + ROW(a, 0, a); + ROW(a, 1, empty); + + ROW(ab, 0, ab); + ROW(ab, 1, a); + ROW(ab, 2, empty); + + ROW(abc, 0, abc); + ROW(abc, 1, ab); + ROW(abc, 2, a); + ROW(abc, 3, empty); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::chop_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + { + const auto chopped = s.chopped(n); + + QCOMPARE(chopped, result); + QCOMPARE(chopped.isNull(), result.isNull()); + QCOMPARE(chopped.isEmpty(), result.isEmpty()); + } + { + const auto chopped = detached(s).chopped(n); + + QCOMPARE(chopped, result); + QCOMPARE(chopped.isNull(), result.isNull()); + QCOMPARE(chopped.isEmpty(), result.isEmpty()); + } + { + auto chopped = s; + chopped.chop(n); + + QCOMPARE(chopped, result); + QCOMPARE(chopped.isNull(), result.isNull()); + QCOMPARE(chopped.isEmpty(), result.isEmpty()); + } +} + +void tst_QStringApiSymmetry::trimmed_data() +{ + QTest::addColumn<QString>("unicode"); + QTest::addColumn<QStringRef>("result"); + + const auto latin1Whitespace = QLatin1String(" \r\n\t\f\v"); + + QTest::addRow("null") << QString() << QStringRef(); + + auto add = [latin1Whitespace](const QString &str) { + // run through all substrings of latin1Whitespace + for (int len = 0; len < latin1Whitespace.size(); ++len) { + for (int pos = 0; pos < latin1Whitespace.size() - len; ++pos) { + const QString unicode = latin1Whitespace.mid(pos, len) + str + latin1Whitespace.mid(pos, len); + const QScopedPointer<const char> escaped(QTest::toString(unicode)); + QTest::addRow("%s", escaped.data()) << unicode << QStringRef(&str); + } + } + }; + + add(empty); + add(a); + add(ab); +} + +template <typename String> +void tst_QStringApiSymmetry::trimmed_impl() +{ + QFETCH(const QString, unicode); + QFETCH(const QStringRef, result); + + const auto utf8 = unicode.toUtf8(); + const auto l1s = unicode.toLatin1(); + const auto l1 = l1s.isNull() ? QLatin1String() : QLatin1String(l1s); + + const auto ref = unicode.isNull() ? QStringRef() : QStringRef(&unicode); + const auto s = make<String>(ref, l1, utf8); + + QCOMPARE(s.isNull(), unicode.isNull()); + + { + const auto trimmed = s.trimmed(); + + QCOMPARE(trimmed, result); + QCOMPARE(trimmed.isNull(), result.isNull()); + QCOMPARE(trimmed.isEmpty(), result.isEmpty()); + } + { + const auto trimmed = detached(s).trimmed(); + + QCOMPARE(trimmed, result); + QCOMPARE(trimmed.isNull(), result.isNull()); + QCOMPARE(trimmed.isEmpty(), result.isEmpty()); + } +} + // // // UTF-16-only checks: @@ -309,6 +1086,7 @@ void tst_QStringApiSymmetry::compare_impl() const template <class Str> Str make(const QString &s); template <> QStringRef make(const QString &s) { return QStringRef(&s); } template <> QString make(const QString &s) { return s; } +template <> QStringView make(const QString &s) { return s; } #define REPEAT_16X(X) X X X X X X X X X X X X X X X X #define LONG_STRING_256 REPEAT_16X("0123456789abcdef") diff --git a/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp b/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp index 319f772516..7d5504c22c 100644 --- a/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp +++ b/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp @@ -326,22 +326,22 @@ void tst_QStringIterator::position() QLatin1Char('p') // codeunit count: 35 }; + static const int stringDataSize = sizeof(stringData) / sizeof(stringData[0]); - const QString string(stringData, sizeof(stringData) / sizeof(stringData[0])); - QStringIterator i(string); + QStringIterator i(QStringView(stringData, stringDataSize)); - QCOMPARE(i.position(), string.constBegin()); + QCOMPARE(i.position(), stringData); QVERIFY(i.hasNext()); QVERIFY(!i.hasPrevious()); - i.setPosition(string.constEnd()); - QCOMPARE(i.position(), string.constEnd()); + i.setPosition(stringData + stringDataSize); + QCOMPARE(i.position(), stringData + stringDataSize); QVERIFY(!i.hasNext()); QVERIFY(i.hasPrevious()); #define QCHAR_UNICODE_VALUE(x) ((uint)(QChar(x).unicode())) - const QString::const_iterator begin = string.constBegin(); + const QChar *begin = stringData; i.setPosition(begin); QCOMPARE(i.position(), begin); QCOMPARE(i.peekNext(), QCHAR_UNICODE_VALUE(QLatin1Char('a'))); diff --git a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp index 2385aa992c..9f054190e5 100644 --- a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp +++ b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp @@ -264,6 +264,15 @@ void tst_QStringList::contains() QVERIFY(list.contains("ARTHUR", Qt::CaseInsensitive)); QVERIFY(list.contains("dent", Qt::CaseInsensitive)); QVERIFY(!list.contains("hans", Qt::CaseInsensitive)); + + QVERIFY(list.contains(QLatin1String("arthur"))); + QVERIFY(!list.contains(QLatin1String("ArthuR"))); + QVERIFY(!list.contains(QLatin1String("Hans"))); + QVERIFY(list.contains(QLatin1String("arthur"), Qt::CaseInsensitive)); + QVERIFY(list.contains(QLatin1String("ArthuR"), Qt::CaseInsensitive)); + QVERIFY(list.contains(QLatin1String("ARTHUR"), Qt::CaseInsensitive)); + QVERIFY(list.contains(QLatin1String("dent"), Qt::CaseInsensitive)); + QVERIFY(!list.contains(QLatin1String("hans"), Qt::CaseInsensitive)); } void tst_QStringList::removeDuplicates_data() diff --git a/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp b/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp index d2374fe0ae..473f563f9b 100644 --- a/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp +++ b/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp @@ -1862,7 +1862,9 @@ void tst_QStringRef::double_conversion() void tst_QStringRef::trimmed() { - QString a; + QVERIFY(QStringRef().trimmed().isNull()); + QString a = ""; + QVERIFY(!QStringRef(&a).trimmed().isNull()); QStringRef b; a = "Text"; b = a.leftRef(-1); diff --git a/tests/auto/corelib/tools/qstringview/.gitignore b/tests/auto/corelib/tools/qstringview/.gitignore new file mode 100644 index 0000000000..5f757d448a --- /dev/null +++ b/tests/auto/corelib/tools/qstringview/.gitignore @@ -0,0 +1 @@ +tst_qstringview diff --git a/tests/auto/corelib/tools/qstringview/qstringview.pro b/tests/auto/corelib/tools/qstringview/qstringview.pro new file mode 100644 index 0000000000..e0e9973c91 --- /dev/null +++ b/tests/auto/corelib/tools/qstringview/qstringview.pro @@ -0,0 +1,6 @@ +CONFIG += testcase +TARGET = tst_qstringview +QT = core testlib +contains(QT_CONFIG, c++14):CONFIG *= c++14 +contains(QT_CONFIG, c++1z):CONFIG *= c++1z +SOURCES += tst_qstringview.cpp diff --git a/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp new file mode 100644 index 0000000000..9617afb651 --- /dev/null +++ b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp @@ -0,0 +1,555 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QStringView> +#include <QString> +#include <QChar> +#include <QStringRef> + +#include <QTest> + +#include <string> + +template <typename T> +using CanConvert = std::is_convertible<T, QStringView>; + +Q_STATIC_ASSERT(!CanConvert<QLatin1String>::value); +Q_STATIC_ASSERT(!CanConvert<const char*>::value); +Q_STATIC_ASSERT(!CanConvert<QByteArray>::value); + +// QStringView qchar_does_not_compile() { return QStringView(QChar('a')); } +// QStringView qlatin1string_does_not_compile() { return QStringView(QLatin1String("a")); } +// QStringView const_char_star_does_not_compile() { return QStringView("a"); } +// QStringView qbytearray_does_not_compile() { return QStringView(QByteArray("a")); } + +// +// QChar +// + +Q_STATIC_ASSERT(!CanConvert<QChar>::value); + +Q_STATIC_ASSERT(CanConvert<QChar[123]>::value); + +Q_STATIC_ASSERT(CanConvert< QString >::value); +Q_STATIC_ASSERT(CanConvert<const QString >::value); +Q_STATIC_ASSERT(CanConvert< QString&>::value); +Q_STATIC_ASSERT(CanConvert<const QString&>::value); + +Q_STATIC_ASSERT(CanConvert< QStringRef >::value); +Q_STATIC_ASSERT(CanConvert<const QStringRef >::value); +Q_STATIC_ASSERT(CanConvert< QStringRef&>::value); +Q_STATIC_ASSERT(CanConvert<const QStringRef&>::value); + + +// +// ushort +// + +Q_STATIC_ASSERT(!CanConvert<ushort>::value); + +Q_STATIC_ASSERT(CanConvert<ushort[123]>::value); + +Q_STATIC_ASSERT(CanConvert< ushort*>::value); +Q_STATIC_ASSERT(CanConvert<const ushort*>::value); + + +// +// char16_t +// + +#if defined(Q_COMPILER_UNICODE_STRINGS) + +Q_STATIC_ASSERT(!CanConvert<char16_t>::value); + +Q_STATIC_ASSERT(CanConvert< char16_t*>::value); +Q_STATIC_ASSERT(CanConvert<const char16_t*>::value); + +#endif + +#if defined(Q_STDLIB_UNICODE_STRINGS) + +Q_STATIC_ASSERT(CanConvert< std::u16string >::value); +Q_STATIC_ASSERT(CanConvert<const std::u16string >::value); +Q_STATIC_ASSERT(CanConvert< std::u16string&>::value); +Q_STATIC_ASSERT(CanConvert<const std::u16string&>::value); + +#endif + + +// +// wchar_t +// + +Q_CONSTEXPR bool CanConvertFromWCharT = +#ifdef Q_OS_WIN + true +#else + false +#endif + ; + +Q_STATIC_ASSERT(!CanConvert<wchar_t>::value); + +Q_STATIC_ASSERT(CanConvert< wchar_t*>::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert<const wchar_t*>::value == CanConvertFromWCharT); + +Q_STATIC_ASSERT(CanConvert< std::wstring >::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert<const std::wstring >::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert< std::wstring&>::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert<const std::wstring&>::value == CanConvertFromWCharT); + + +class tst_QStringView : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void constExpr() const; + void basics() const; + void at() const; + + void fromQString() const; + void fromQStringRef() const; + + void fromQCharStar() const + { + const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; + fromLiteral(str); + } + + void fromUShortStar() const + { + const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; + fromLiteral(str); + } + + void fromChar16TStar() const + { +#if defined(Q_COMPILER_UNICODE_STRINGS) + fromLiteral(u"Hello, World!"); +#else + QSKIP("This test requires C++11 char16_t support enabled in the compiler"); +#endif + } + + void fromWCharTStar() const + { +#ifdef Q_OS_WIN + fromLiteral(L"Hello, World!"); +#else + QSKIP("This is a Windows-only test"); +#endif + } + + void fromQCharRange() const + { + const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; + fromRange(std::begin(str), std::end(str)); + } + + void fromUShortRange() const + { + const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; + fromRange(std::begin(str), std::end(str)); + } + + void fromChar16TRange() const + { +#if defined(Q_COMPILER_UNICODE_STRINGS) + const char16_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; + fromRange(std::begin(str), std::end(str)); +#else + QSKIP("This test requires C++11 char16_t support enabled in the compiler"); +#endif + } + + void fromWCharTRange() const + { +#ifdef Q_OS_WIN + const wchar_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; + fromRange(std::begin(str), std::end(str)); +#else + QSKIP("This is a Windows-only test"); +#endif + } + + // std::basic_string + void fromStdStringWCharT() const + { +#ifdef Q_OS_WIN + fromStdString<wchar_t>(); +#else + QSKIP("This is a Windows-only test"); +#endif + } + void fromStdStringChar16T() const + { +#ifdef Q_STDLIB_UNICODE_STRINGS + fromStdString<char16_t>(); +#else + QSKIP("This test requires C++11 char16_t support enabled in compiler & stdlib"); +#endif + } + +private: + template <typename String> + void conversion_tests(String arg) const; + template <typename Char> + void fromLiteral(const Char *arg) const; + template <typename Char> + void fromRange(const Char *first, const Char *last) const; + template <typename Char, typename Container> + void fromContainer() const; + template <typename Char> + void fromStdString() const { fromContainer<Char, std::basic_string<Char> >(); } +}; + +void tst_QStringView::constExpr() const +{ + // compile-time checks +#ifdef Q_COMPILER_CONSTEXPR + { + constexpr QStringView sv; + Q_STATIC_ASSERT(sv.size() == 0); + Q_STATIC_ASSERT(sv.isNull()); + Q_STATIC_ASSERT(sv.empty()); + Q_STATIC_ASSERT(sv.isEmpty()); + Q_STATIC_ASSERT(sv.utf16() == nullptr); + + constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); + Q_STATIC_ASSERT(sv2.isNull()); + Q_STATIC_ASSERT(sv2.empty()); + } + { + constexpr QStringView sv = QStringViewLiteral(""); + Q_STATIC_ASSERT(sv.size() == 0); + Q_STATIC_ASSERT(!sv.isNull()); + Q_STATIC_ASSERT(sv.empty()); + Q_STATIC_ASSERT(sv.isEmpty()); + Q_STATIC_ASSERT(sv.utf16() != nullptr); + + constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); + Q_STATIC_ASSERT(!sv2.isNull()); + Q_STATIC_ASSERT(sv2.empty()); + } + { + constexpr QStringView sv = QStringViewLiteral("Hello"); + Q_STATIC_ASSERT(sv.size() == 5); + Q_STATIC_ASSERT(!sv.empty()); + Q_STATIC_ASSERT(!sv.isEmpty()); + Q_STATIC_ASSERT(!sv.isNull()); + Q_STATIC_ASSERT(*sv.utf16() == 'H'); + Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); + + constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); + Q_STATIC_ASSERT(!sv2.isNull()); + Q_STATIC_ASSERT(!sv2.empty()); + Q_STATIC_ASSERT(sv2.size() == 5); + } +#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) + { + Q_STATIC_ASSERT(QStringView(u"Hello").size() == 5); + constexpr QStringView sv = u"Hello"; + Q_STATIC_ASSERT(sv.size() == 5); + Q_STATIC_ASSERT(!sv.empty()); + Q_STATIC_ASSERT(!sv.isEmpty()); + Q_STATIC_ASSERT(!sv.isNull()); + Q_STATIC_ASSERT(*sv.utf16() == 'H'); + Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); + + constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); + Q_STATIC_ASSERT(!sv2.isNull()); + Q_STATIC_ASSERT(!sv2.empty()); + Q_STATIC_ASSERT(sv2.size() == 5); + } +#else // storage_type is wchar_t + { + Q_STATIC_ASSERT(QStringView(L"Hello").size() == 5); + constexpr QStringView sv = L"Hello"; + Q_STATIC_ASSERT(sv.size() == 5); + Q_STATIC_ASSERT(!sv.empty()); + Q_STATIC_ASSERT(!sv.isEmpty()); + Q_STATIC_ASSERT(!sv.isNull()); + Q_STATIC_ASSERT(*sv.utf16() == 'H'); + Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); + + constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); + Q_STATIC_ASSERT(!sv2.isNull()); + Q_STATIC_ASSERT(!sv2.empty()); + Q_STATIC_ASSERT(sv2.size() == 5); + } +#endif +#endif +} + +void tst_QStringView::basics() const +{ + QStringView sv1; + + // a default-constructed QStringView is null: + QVERIFY(sv1.isNull()); + // which implies it's empty(); + QVERIFY(sv1.isEmpty()); + + QStringView sv2; + + QVERIFY(sv2 == sv1); + QVERIFY(!(sv2 != sv1)); +} + +void tst_QStringView::at() const +{ + QString hello("Hello"); + QStringView sv(hello); + QCOMPARE(sv.at(0), QChar('H')); QCOMPARE(sv[0], QChar('H')); + QCOMPARE(sv.at(1), QChar('e')); QCOMPARE(sv[1], QChar('e')); + QCOMPARE(sv.at(2), QChar('l')); QCOMPARE(sv[2], QChar('l')); + QCOMPARE(sv.at(3), QChar('l')); QCOMPARE(sv[3], QChar('l')); + QCOMPARE(sv.at(4), QChar('o')); QCOMPARE(sv[4], QChar('o')); +} + +void tst_QStringView::fromQString() const +{ + QString null; + QString empty = ""; + + QVERIFY( QStringView(null).isNull()); + QVERIFY( QStringView(null).isEmpty()); + QVERIFY( QStringView(empty).isEmpty()); + QVERIFY(!QStringView(empty).isNull()); + + conversion_tests(QString("Hello World!")); +} + +void tst_QStringView::fromQStringRef() const +{ + QStringRef null; + QString emptyS = ""; + QStringRef empty(&emptyS); + + QVERIFY( QStringView(null).isNull()); + QVERIFY( QStringView(null).isEmpty()); + QVERIFY( QStringView(empty).isEmpty()); + QVERIFY(!QStringView(empty).isNull()); + + conversion_tests(QString("Hello World!").midRef(6)); +} + +template <typename Char> +void tst_QStringView::fromLiteral(const Char *arg) const +{ + const Char *null = nullptr; + const Char empty[] = { 0 }; + + QCOMPARE(QStringView(null).size(), qssize_t(0)); + QCOMPARE(QStringView(null).data(), nullptr); + QCOMPARE(QStringView(empty).size(), qssize_t(0)); + QCOMPARE(static_cast<const void*>(QStringView(empty).data()), + static_cast<const void*>(empty)); + + QVERIFY( QStringView(null).isNull()); + QVERIFY( QStringView(null).isEmpty()); + QVERIFY( QStringView(empty).isEmpty()); + QVERIFY(!QStringView(empty).isNull()); + + conversion_tests(arg); +} + +template <typename Char> +void tst_QStringView::fromRange(const Char *first, const Char *last) const +{ + const Char *null = nullptr; + QCOMPARE(QStringView(null, null).size(), 0); + QCOMPARE(QStringView(null, null).data(), nullptr); + QCOMPARE(QStringView(first, first).size(), 0); + QCOMPARE(static_cast<const void*>(QStringView(first, first).data()), + static_cast<const void*>(first)); + + const auto sv = QStringView(first, last); + QCOMPARE(sv.size(), last - first); + QCOMPARE(static_cast<const void*>(sv.data()), + static_cast<const void*>(first)); + + // can't call conversion_tests() here, as it requires a single object +} + +template <typename Char, typename Container> +void tst_QStringView::fromContainer() const +{ + const QString s = "Hello World!"; + + Container c; + // unspecified whether empty containers make null QStringViews + QVERIFY(QStringView(c).isEmpty()); + + QCOMPARE(sizeof(Char), sizeof(QChar)); + + const auto *data = reinterpret_cast<const Char *>(s.utf16()); + std::copy(data, data + s.size(), std::back_inserter(c)); + conversion_tests(std::move(c)); +} + +namespace help { +template <typename T> +size_t size(const T &t) { return size_t(t.size()); } +template <typename T> +size_t size(const T *t) +{ + size_t result = 0; + if (t) { + while (*t++) + ++result; + } + return result; +} +size_t size(const QChar *t) +{ + size_t result = 0; + if (t) { + while (!t++->isNull()) + ++result; + } + return result; +} + +template <typename T> +typename T::const_iterator cbegin(const T &t) { return t.cbegin(); } +template <typename T> +const T * cbegin(const T *t) { return t; } + +template <typename T> +typename T::const_iterator cend(const T &t) { return t.cend(); } +template <typename T> +const T * cend(const T *t) { return t + size(t); } + +template <typename T> +typename T::const_reverse_iterator crbegin(const T &t) { return t.crbegin(); } +template <typename T> +std::reverse_iterator<const T*> crbegin(const T *t) { return std::reverse_iterator<const T*>(cend(t)); } + +template <typename T> +typename T::const_reverse_iterator crend(const T &t) { return t.crend(); } +template <typename T> +std::reverse_iterator<const T*> crend(const T *t) { return std::reverse_iterator<const T*>(cbegin(t)); } + +} // namespace help + +template <typename String> +void tst_QStringView::conversion_tests(String string) const +{ + // copy-construct: + { + QStringView sv = string; + + QCOMPARE(help::size(sv), help::size(string)); + + // check iterators: + + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.cbegin(), sv.size()))); + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.begin(), sv.size()))); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv.crbegin())); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv.rbegin())); + + QCOMPARE(sv, string); + } + + QStringView sv; + + // copy-assign: + { + sv = string; + + QCOMPARE(help::size(sv), help::size(string)); + + // check relational operators: + + QCOMPARE(sv, string); + QCOMPARE(string, sv); + + QVERIFY(!(sv != string)); + QVERIFY(!(string != sv)); + + QVERIFY(!(sv < string)); + QVERIFY(sv <= string); + QVERIFY(!(sv > string)); + QVERIFY(sv >= string); + + QVERIFY(!(string < sv)); + QVERIFY(string <= sv); + QVERIFY(!(string > sv)); + QVERIFY(string >= sv); + } + + // copy-construct from rvalue (QStringView never assumes ownership): + { + QStringView sv2 = std::move(string); + QCOMPARE(sv2, sv); + QCOMPARE(sv2, string); + } + + // copy-assign from rvalue (QStringView never assumes ownership): + { + QStringView sv2; + sv2 = std::move(string); + QCOMPARE(sv2, sv); + QCOMPARE(sv2, string); + } +} + +QTEST_APPLESS_MAIN(tst_QStringView) +#include "tst_qstringview.moc" diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp index e75ed5cc67..714eb8a22a 100644 --- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp @@ -754,7 +754,7 @@ void tst_QTimeZone::icuTest() void tst_QTimeZone::tzTest() { -#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_MAC +#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_DARWIN // 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(); @@ -917,12 +917,12 @@ void tst_QTimeZone::tzTest() QDateTime dt(QDate(2016, 3, 28), QTime(0, 0, 0), Qt::UTC); QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, QString("+07")); } -#endif // Q_OS_UNIX +#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !Q_OS_DARWIN } void tst_QTimeZone::macTest() { -#if defined(QT_BUILD_INTERNAL) && defined (Q_OS_MAC) +#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_DARWIN) // 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(); @@ -969,7 +969,7 @@ void tst_QTimeZone::macTest() } testCetPrivate(tzp); -#endif // Q_OS_MAC +#endif // QT_BUILD_INTERNAL && Q_OS_DARWIN } void tst_QTimeZone::darwinTypes() diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index 0806ad1318..3971353cbb 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -30,8 +30,6 @@ #include <qvarlengtharray.h> #include <qvariant.h> -const int N = 1; - class tst_QVarLengthArray : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp index 2e34e82388..9812d93a50 100644 --- a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp +++ b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp @@ -513,6 +513,14 @@ void tst_QVersionNumber::fromString() QCOMPARE(QVersionNumber::fromString(constructionString), expectedVersion); QCOMPARE(QVersionNumber::fromString(constructionString, &index), expectedVersion); QCOMPARE(index, suffixIndex); + + QCOMPARE(QVersionNumber::fromString(QStringView(constructionString)), expectedVersion); + QCOMPARE(QVersionNumber::fromString(QStringView(constructionString), &index), expectedVersion); + QCOMPARE(index, suffixIndex); + + QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1())), expectedVersion); + QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1()), &index), expectedVersion); + QCOMPARE(index, suffixIndex); } void tst_QVersionNumber::toString_data() diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index e45771a704..6720307d59 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -1,6 +1,7 @@ TEMPLATE=subdirs SUBDIRS=\ collections \ + containerapisymmetry \ qalgorithms \ qarraydata \ qarraydata_strictiterators \ @@ -56,6 +57,7 @@ SUBDIRS=\ qstringlist \ qstringmatcher \ qstringref \ + qstringview \ qtextboundaryfinder \ qtime \ qtimezone \ diff --git a/tests/auto/gui/gui.pro b/tests/auto/gui/gui.pro index 2fd3024afe..d7cda11513 100644 --- a/tests/auto/gui/gui.pro +++ b/tests/auto/gui/gui.pro @@ -9,8 +9,11 @@ SUBDIRS = \ painting \ qopenglconfig \ qopengl \ + qvulkan \ text \ util \ itemmodels \ !qtConfig(opengl): SUBDIRS -= qopengl qopenglconfig + +!qtConfig(vulkan): SUBDIRS -= qvulkan diff --git a/tests/auto/gui/image/qicon/tst_qicon.cpp b/tests/auto/gui/image/qicon/tst_qicon.cpp index 175179699d..bf8f7ade9e 100644 --- a/tests/auto/gui/image/qicon/tst_qicon.cpp +++ b/tests/auto/gui/image/qicon/tst_qicon.cpp @@ -530,16 +530,6 @@ void tst_QIcon::streamAvailableSizes() } } - -static inline bool operator<(const QSize &lhs, const QSize &rhs) -{ - if (lhs.width() < rhs.width()) - return true; - else if (lhs.width() == lhs.width()) - return lhs.height() < lhs.height(); - return false; -} - #ifndef QT_NO_WIDGETS void tst_QIcon::task184901_badCache() { diff --git a/tests/auto/gui/image/qiconhighdpi/icons/misc/button.9.png b/tests/auto/gui/image/qiconhighdpi/icons/misc/button.9.png Binary files differnew file mode 100644 index 0000000000..1a560a1d74 --- /dev/null +++ b/tests/auto/gui/image/qiconhighdpi/icons/misc/button.9.png diff --git a/tests/auto/gui/image/qiconhighdpi/icons/misc/button@2x.9.png b/tests/auto/gui/image/qiconhighdpi/icons/misc/button@2x.9.png Binary files differnew file mode 100644 index 0000000000..f010dc55c7 --- /dev/null +++ b/tests/auto/gui/image/qiconhighdpi/icons/misc/button@2x.9.png diff --git a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp index ce7f68a0a6..51892cca04 100644 --- a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp +++ b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp @@ -39,6 +39,7 @@ private slots: void initTestCase(); void fromTheme_data(); void fromTheme(); + void ninePatch(); }; tst_QIconHighDpi::tst_QIconHighDpi() @@ -182,6 +183,24 @@ void tst_QIconHighDpi::fromTheme() QCOMPARE(pixmap.devicePixelRatio(), expectedDpr); } +void tst_QIconHighDpi::ninePatch() +{ + const QIcon icon(":/icons/misc/button.9.png"); + const int dpr = qCeil(qApp->devicePixelRatio()); + + switch (dpr) { + case 1: + QCOMPARE(icon.availableSizes().size(), 1); + QCOMPARE(icon.availableSizes().at(0), QSize(42, 42)); + break; + case 2: + QCOMPARE(icon.availableSizes().size(), 2); + QCOMPARE(icon.availableSizes().at(0), QSize(42, 42)); + QCOMPARE(icon.availableSizes().at(1), QSize(82, 82)); + break; + } +} + int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); diff --git a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc index 80b5e38ee6..5cc1c6d9b1 100644 --- a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc +++ b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc @@ -4,5 +4,7 @@ <file>icons/testtheme/22x22/actions/appointment-new.png</file> <file>icons/testtheme/index.theme</file> <file>icons/testtheme/22x22@2/actions/appointment-new.png</file> + <file>icons/misc/button.9.png</file> + <file>icons/misc/button@2x.9.png</file> </qresource> </RCC> diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index fac785ac86..af769403bf 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -222,65 +222,65 @@ private: const QString m_prefix; }; -static QString formatToString(QImage::Format format) +static QLatin1String formatToString(QImage::Format format) { switch (format) { case QImage::Format_Invalid: - return QStringLiteral("Invalid"); + return QLatin1String("Invalid"); case QImage::Format_Mono: - return QStringLiteral("Mono"); + return QLatin1String("Mono"); case QImage::Format_MonoLSB: - return QStringLiteral("MonoLSB"); + return QLatin1String("MonoLSB"); case QImage::Format_Indexed8: - return QStringLiteral("Indexed8"); + return QLatin1String("Indexed8"); case QImage::Format_RGB32: - return QStringLiteral("RGB32"); + return QLatin1String("RGB32"); case QImage::Format_ARGB32: - return QStringLiteral("ARGB32"); + return QLatin1String("ARGB32"); case QImage::Format_ARGB32_Premultiplied: - return QStringLiteral("ARGB32pm"); + return QLatin1String("ARGB32pm"); case QImage::Format_RGB16: - return QStringLiteral("RGB16"); + return QLatin1String("RGB16"); case QImage::Format_ARGB8565_Premultiplied: - return QStringLiteral("ARGB8565pm"); + return QLatin1String("ARGB8565pm"); case QImage::Format_RGB666: - return QStringLiteral("RGB666"); + return QLatin1String("RGB666"); case QImage::Format_ARGB6666_Premultiplied: - return QStringLiteral("ARGB6666pm"); + return QLatin1String("ARGB6666pm"); case QImage::Format_RGB555: - return QStringLiteral("RGB555"); + return QLatin1String("RGB555"); case QImage::Format_ARGB8555_Premultiplied: - return QStringLiteral("ARGB8555pm"); + return QLatin1String("ARGB8555pm"); case QImage::Format_RGB888: - return QStringLiteral("RGB888"); + return QLatin1String("RGB888"); case QImage::Format_RGB444: - return QStringLiteral("RGB444"); + return QLatin1String("RGB444"); case QImage::Format_ARGB4444_Premultiplied: - return QStringLiteral("ARGB4444pm"); + return QLatin1String("ARGB4444pm"); case QImage::Format_RGBX8888: - return QStringLiteral("RGBx88888"); + return QLatin1String("RGBx88888"); case QImage::Format_RGBA8888: - return QStringLiteral("RGBA88888"); + return QLatin1String("RGBA88888"); case QImage::Format_RGBA8888_Premultiplied: - return QStringLiteral("RGBA88888pm"); + return QLatin1String("RGBA88888pm"); case QImage::Format_BGR30: - return QStringLiteral("BGR30"); + return QLatin1String("BGR30"); case QImage::Format_A2BGR30_Premultiplied: - return QStringLiteral("A2BGR30pm"); + return QLatin1String("A2BGR30pm"); case QImage::Format_RGB30: - return QStringLiteral("RGB30"); + return QLatin1String("RGB30"); case QImage::Format_A2RGB30_Premultiplied: - return QStringLiteral("A2RGB30pm"); + return QLatin1String("A2RGB30pm"); case QImage::Format_Alpha8: - return QStringLiteral("Alpha8"); + return QLatin1String("Alpha8"); case QImage::Format_Grayscale8: - return QStringLiteral("Grayscale8"); + return QLatin1String("Grayscale8"); default: break; }; Q_UNREACHABLE(); qWarning("Unhandled image format"); - return QStringLiteral("unknown"); + return QLatin1String("unknown"); } tst_QImage::tst_QImage() @@ -2321,7 +2321,7 @@ void tst_QImage::rgbSwapped_data() QTest::addColumn<QImage::Format>("format"); for (int i = QImage::Format_Indexed8; i < QImage::Format_Alpha8; ++i) { - QTest::newRow(qPrintable(formatToString(QImage::Format(i)))) << QImage::Format(i); + QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); } } @@ -2576,11 +2576,12 @@ void tst_QImage::inplaceMirrored_data() continue; if (i == QImage::Format_RGB444 || i == QImage::Format_ARGB4444_Premultiplied) continue; - QTest::newRow(qPrintable(formatToString(QImage::Format(i)) + QStringLiteral(", vertical"))) + const auto fmt = formatToString(QImage::Format(i)); + QTest::addRow("%s, vertical", fmt.data()) << QImage::Format(i) << true << false; - QTest::newRow(qPrintable(formatToString(QImage::Format(i)) + QStringLiteral(", horizontal"))) + QTest::addRow("%s, horizontal", fmt.data()) << QImage::Format(i) << false << true; - QTest::newRow(qPrintable(formatToString(QImage::Format(i)) + QStringLiteral(", horizontal+vertical"))) + QTest::addRow("%s, horizontal+vertical", fmt.data()) << QImage::Format(i) << true << true; } } @@ -2744,12 +2745,12 @@ void tst_QImage::genericRgbConversion_data() QTest::addColumn<QImage::Format>("dest_format"); for (int i = QImage::Format_RGB32; i < QImage::Format_Alpha8; ++i) { - const QString formatI = formatToString(QImage::Format(i)); + const QLatin1String formatI = formatToString(QImage::Format(i)); for (int j = QImage::Format_RGB32; j < QImage::Format_Alpha8; ++j) { if (i == j) continue; - const QString test = formatI + QLatin1String(" -> ") + formatToString(QImage::Format(j)); - QTest::newRow(qPrintable(test)) << QImage::Format(i) << QImage::Format(j); + QTest::addRow("%s -> %s", formatI.data(), formatToString(QImage::Format(j)).data()) + << QImage::Format(i) << QImage::Format(j); } } } @@ -2786,8 +2787,8 @@ void tst_QImage::inplaceRgbConversion_data() for (int j = QImage::Format_RGB32; j < QImage::Format_Alpha8; ++j) { if (i == j) continue; - QString test = QString::fromLatin1("%1 -> %2").arg(formatToString(QImage::Format(i))).arg(formatToString(QImage::Format(j))); - QTest::newRow(qPrintable(test)) << QImage::Format(i) << QImage::Format(j); + QTest::addRow("%s -> %s", formatToString(QImage::Format(i)).data(), formatToString(QImage::Format(j)).data()) + << QImage::Format(i) << QImage::Format(j); } } } @@ -2953,7 +2954,7 @@ void tst_QImage::invertPixelsRGB_data() QTest::addColumn<QImage::Format>("image_format"); for (int i = QImage::Format_RGB32; i < QImage::Format_Alpha8; ++i) { - QTest::newRow(qPrintable(formatToString(QImage::Format(i)))) << QImage::Format(i); + QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); } } @@ -3130,10 +3131,10 @@ void tst_QImage::rgb30Repremul_data() { QTest::addColumn<uint>("color"); for (int i = 255; i > 0; i -= 15) { - QTest::newRow(qPrintable(QStringLiteral("100% red=") + QString::number(i))) << qRgba(i, 0, 0, 0xff); - QTest::newRow(qPrintable(QStringLiteral("75% red=") + QString::number(i))) << qRgba(i, 0, 0, 0xc0); - QTest::newRow(qPrintable(QStringLiteral("50% red=") + QString::number(i))) << qRgba(i, 0, 0, 0x80); - QTest::newRow(qPrintable(QStringLiteral("37.5% red=") + QString::number(i))) << qRgba(i, 0, 0, 0x60); + QTest::addRow("100%% red=%d", i) << qRgba(i, 0, 0, 0xff); + QTest::addRow("75%% red=%d", i) << qRgba(i, 0, 0, 0xc0); + QTest::addRow("50%% red=%d", i) << qRgba(i, 0, 0, 0x80); + QTest::addRow("37.5%% red=%d", i) << qRgba(i, 0, 0, 0x60); } } @@ -3393,7 +3394,7 @@ void tst_QImage::toCGImage_data() QImage::Format_RGBA8888, QImage::Format_RGBX8888, QImage::Format_ARGB32_Premultiplied }; for (int i = QImage::Format_Invalid; i < QImage::Format_Grayscale8; ++i) { - QTest::newRow(qPrintable(formatToString(QImage::Format(i)))) + QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i) << supported.contains(QImage::Format(i)); } } diff --git a/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp b/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp index d5c624833c..2381fd9246 100644 --- a/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp +++ b/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp @@ -77,6 +77,9 @@ private slots: void saveWithNoFormat(); void saveToTemporaryFile(); + + void writeEmpty(); + private: QTemporaryDir m_temporaryDir; QString prefix; @@ -529,5 +532,18 @@ void tst_QImageWriter::saveToTemporaryFile() } } +void tst_QImageWriter::writeEmpty() +{ + // check writing a null QImage errors gracefully + QTemporaryDir dir; + QVERIFY2(dir.isValid(), qPrintable(dir.errorString())); + QString fileName(dir.path() + QLatin1String("/testimage.bmp")); + QVERIFY(!QFileInfo(fileName).exists()); + QImageWriter writer(fileName); + QVERIFY(!writer.write(QImage())); + QCOMPARE(writer.error(), QImageWriter::InvalidImageError); + QVERIFY(!QFileInfo(fileName).exists()); +} + QTEST_MAIN(tst_QImageWriter) #include "tst_qimagewriter.moc" diff --git a/tests/auto/gui/kernel/kernel.pro b/tests/auto/gui/kernel/kernel.pro index 559395a9ae..46786262c0 100644 --- a/tests/auto/gui/kernel/kernel.pro +++ b/tests/auto/gui/kernel/kernel.pro @@ -2,6 +2,7 @@ TEMPLATE=subdirs SUBDIRS=\ qbackingstore \ qclipboard \ + qcursor \ qdrag \ qevent \ qfileopenevent \ diff --git a/tests/auto/gui/kernel/qcursor/qcursor.pro b/tests/auto/gui/kernel/qcursor/qcursor.pro new file mode 100644 index 0000000000..16e7d7c41c --- /dev/null +++ b/tests/auto/gui/kernel/qcursor/qcursor.pro @@ -0,0 +1,6 @@ +QT += testlib +TARGET = tst_qcursor +CONFIG += testcase +CONFIG -= app_bundle + +SOURCES += tst_qcursor.cpp diff --git a/tests/auto/gui/kernel/qcursor/tst_qcursor.cpp b/tests/auto/gui/kernel/qcursor/tst_qcursor.cpp new file mode 100644 index 0000000000..d505f5a655 --- /dev/null +++ b/tests/auto/gui/kernel/qcursor/tst_qcursor.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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/QTest> +#include <qcursor.h> +#include <qpixmap.h> +#include <qbitmap.h> + +class tst_QCursor : public QObject +{ + Q_OBJECT + +private slots: + void equality(); +}; + +#define VERIFY_EQUAL(lhs, rhs) \ + QVERIFY(lhs == rhs); \ + QVERIFY(rhs == lhs); \ + QVERIFY(!(rhs != lhs)); \ + QVERIFY(!(lhs != rhs)) + +#define VERIFY_DIFFERENT(lhs, rhs) \ + QVERIFY(lhs != rhs); \ + QVERIFY(rhs != lhs); \ + QVERIFY(!(rhs == lhs)); \ + QVERIFY(!(lhs == rhs)) + +void tst_QCursor::equality() +{ + VERIFY_EQUAL(QCursor(), QCursor()); + VERIFY_EQUAL(QCursor(Qt::CrossCursor), QCursor(Qt::CrossCursor)); + VERIFY_DIFFERENT(QCursor(Qt::CrossCursor), QCursor()); + + // Shape + QCursor shapeCursor(Qt::WaitCursor); + VERIFY_EQUAL(shapeCursor, shapeCursor); + QCursor shapeCursorCopy(shapeCursor); + VERIFY_EQUAL(shapeCursor, shapeCursorCopy); + shapeCursorCopy.setShape(Qt::DragMoveCursor); + VERIFY_DIFFERENT(shapeCursor, shapeCursorCopy); + shapeCursorCopy.setShape(shapeCursor.shape()); + VERIFY_EQUAL(shapeCursor, shapeCursorCopy); + + // Pixmap + QPixmap pixmap(16, 16); + QCursor pixmapCursor(pixmap); + VERIFY_EQUAL(pixmapCursor, pixmapCursor); + VERIFY_EQUAL(pixmapCursor, QCursor(pixmapCursor)); + VERIFY_EQUAL(pixmapCursor, QCursor(pixmap)); + VERIFY_DIFFERENT(pixmapCursor, QCursor()); + VERIFY_DIFFERENT(pixmapCursor, QCursor(pixmap, 5, 5)); + VERIFY_DIFFERENT(pixmapCursor, QCursor(QPixmap(16, 16))); + VERIFY_DIFFERENT(pixmapCursor, shapeCursor); + + // Bitmap & mask + QBitmap bitmap(16, 16); + QBitmap mask(16, 16); + QCursor bitmapCursor(bitmap, mask); + VERIFY_EQUAL(bitmapCursor, bitmapCursor); + VERIFY_EQUAL(bitmapCursor, QCursor(bitmapCursor)); + VERIFY_EQUAL(bitmapCursor, QCursor(bitmap, mask)); + VERIFY_DIFFERENT(bitmapCursor, QCursor()); + VERIFY_DIFFERENT(bitmapCursor, QCursor(bitmap, mask, 5, 5)); + VERIFY_DIFFERENT(bitmapCursor, QCursor(bitmap, QBitmap(16, 16))); + VERIFY_DIFFERENT(bitmapCursor, QCursor(QBitmap(16, 16), mask)); + VERIFY_DIFFERENT(bitmapCursor, shapeCursor); + VERIFY_DIFFERENT(bitmapCursor, pixmapCursor); + + // Empty pixmap + QPixmap emptyPixmap; + QCursor emptyPixmapCursor(emptyPixmap); + QCOMPARE(emptyPixmapCursor.shape(), Qt::ArrowCursor); + VERIFY_EQUAL(emptyPixmapCursor, QCursor()); + VERIFY_EQUAL(emptyPixmapCursor, QCursor(emptyPixmap, 5, 5)); + VERIFY_DIFFERENT(emptyPixmapCursor, shapeCursor); + VERIFY_DIFFERENT(emptyPixmapCursor, pixmapCursor); + VERIFY_DIFFERENT(emptyPixmapCursor, bitmapCursor); + + // Empty bitmap & mask + QBitmap emptyBitmap; + QCursor emptyBitmapCursor(emptyBitmap, emptyBitmap); + QCOMPARE(emptyBitmapCursor.shape(), Qt::ArrowCursor); + VERIFY_EQUAL(emptyBitmapCursor, QCursor()); + VERIFY_EQUAL(emptyBitmapCursor, QCursor(emptyBitmap, emptyBitmap, 5, 5)); + VERIFY_EQUAL(emptyBitmapCursor, QCursor(emptyBitmap, mask)); + VERIFY_EQUAL(emptyBitmapCursor, QCursor(bitmap, emptyBitmap)); + VERIFY_EQUAL(emptyBitmapCursor, emptyPixmapCursor); + VERIFY_DIFFERENT(emptyBitmapCursor, shapeCursor); + VERIFY_DIFFERENT(emptyBitmapCursor, pixmapCursor); + VERIFY_DIFFERENT(emptyBitmapCursor, bitmapCursor); +} + +#undef VERIFY_EQUAL +#undef VERIFY_DIFFERENT + +QTEST_MAIN(tst_QCursor) +#include "tst_qcursor.moc" diff --git a/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp b/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp index 6394a956bd..f8b6bf064a 100644 --- a/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp +++ b/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp @@ -36,14 +36,18 @@ #include <QLibraryInfo> #ifdef Q_OS_MAC -#ifdef Q_OS_OSX -#include <Carbon/Carbon.h> -#endif struct MacSpecialKey { int key; ushort macSymbol; }; +// Unicode code points for the glyphs associated with these keys +// Defined by Carbon headers but not anywhere in Cocoa +static const int kShiftUnicode = 0x21E7; +static const int kControlUnicode = 0x2303; +static const int kOptionUnicode = 0x2325; +static const int kCommandUnicode = 0x2318; + static const int NumEntries = 21; static const MacSpecialKey entries[NumEntries] = { { Qt::Key_Escape, 0x238B }, @@ -61,12 +65,10 @@ static const MacSpecialKey entries[NumEntries] = { { Qt::Key_Down, 0x2193 }, { Qt::Key_PageUp, 0x21DE }, { Qt::Key_PageDown, 0x21DF }, -#ifdef Q_OS_OSX { Qt::Key_Shift, kShiftUnicode }, { Qt::Key_Control, kCommandUnicode }, { Qt::Key_Meta, kControlUnicode }, { Qt::Key_Alt, kOptionUnicode }, -#endif { Qt::Key_CapsLock, 0x21EA }, }; diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index 92f7182249..9ef8699164 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -1786,16 +1786,27 @@ void tst_QWindow::mask() { QRegion mask = QRect(10, 10, 800 - 20, 600 - 20); - QWindow window; - window.resize(800, 600); - window.setMask(mask); + { + QWindow window; + window.resize(800, 600); + QCOMPARE(window.mask(), QRegion()); - QCOMPARE(window.mask(), QRegion()); + window.create(); + window.setMask(mask); + QCOMPARE(window.mask(), mask); + } - window.create(); - window.setMask(mask); + { + QWindow window; + window.resize(800, 600); + QCOMPARE(window.mask(), QRegion()); + + window.setMask(mask); + QCOMPARE(window.mask(), mask); + window.create(); + QCOMPARE(window.mask(), mask); + } - QCOMPARE(window.mask(), mask); } void tst_QWindow::initialSize() diff --git a/tests/auto/gui/painting/qcolor/tst_qcolor.cpp b/tests/auto/gui/painting/qcolor/tst_qcolor.cpp index 1ce7e797fc..6809aea086 100644 --- a/tests/auto/gui/painting/qcolor/tst_qcolor.cpp +++ b/tests/auto/gui/painting/qcolor/tst_qcolor.cpp @@ -545,19 +545,32 @@ void tst_QColor::setNamedColor_data() QColor bySetNamedColor; \ bySetNamedColor.setNamedColor(expr); \ auto byCtor = QColor(expr); \ - QTest::newRow(e.name + QByteArrayLiteral(#expr)) \ + QTest::addRow("%s: %s", e.name, #expr) \ << byCtor << bySetNamedColor << expected; \ } while (0) \ /*end*/ - ROW(QLatin1String(e.name)); - ROW(QString(QLatin1String(e.name))); + const auto l1 = QLatin1String(e.name); + const auto l1UpperBA = QByteArray(e.name).toUpper(); + const auto l1Upper = QLatin1String(l1UpperBA); + const auto l1SpaceBA = QByteArray(e.name).insert(1, ' '); + const auto l1Space = QLatin1String(l1SpaceBA); + + const auto u16 = QString(l1); + const auto u16Upper = u16.toUpper(); + const auto u16Space = QString(u16).insert(1, ' '); + + ROW(l1); + ROW(u16); + ROW(QStringView(u16)); // name should be case insensitive - ROW(QLatin1String(QByteArray(e.name).toUpper())); - ROW(QString(e.name).toUpper()); + ROW(l1Upper); + ROW(u16Upper); + ROW(QStringView(u16Upper)); // spaces should be ignored - ROW(QLatin1String(QByteArray(e.name).insert(1, ' '))); - ROW(QString(e.name).insert(1, ' ')); + ROW(l1Space); + ROW(u16Space); + ROW(QStringView(u16Space)); #undef ROW } } diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 8db4489ec1..ed5b9fabb6 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -397,51 +397,6 @@ void tst_QPainter::cleanupTestCase() QFile::remove(QLatin1String("foo.png")); } -static const char* const maskSource_data[] = { -"16 13 6 1", -". c None", -"d c #000000", -"# c #999999", -"c c #cccccc", -"b c #ffff00", -"a c #ffffff", -"...#####........", -"..#aaaaa#.......", -".#abcbcba######.", -".#acbcbcaaaaaa#d", -".#abcbcbcbcbcb#d", -"#############b#d", -"#aaaaaaaaaa##c#d", -"#abcbcbcbcbbd##d", -".#abcbcbcbcbcd#d", -".#acbcbcbcbcbd#d", -"..#acbcbcbcbb#dd", -"..#############d", -"...ddddddddddddd"}; - -static const char* const maskResult_data[] = { -"16 13 6 1", -". c #ff0000", -"d c #000000", -"# c #999999", -"c c #cccccc", -"b c #ffff00", -"a c #ffffff", -"...#####........", -"..#aaaaa#.......", -".#abcbcba######.", -".#acbcbcaaaaaa#d", -".#abcbcbcbcbcb#d", -"#############b#d", -"#aaaaaaaaaa##c#d", -"#abcbcbcbcbbd##d", -".#abcbcbcbcbcd#d", -".#acbcbcbcbcbd#d", -"..#acbcbcbcbb#dd", -"..#############d", -"...ddddddddddddd"}; - - #ifndef QT_NO_WIDGETS void tst_QPainter::drawPixmap_comp_data() { @@ -3557,11 +3512,9 @@ void tst_QPainter::drawImage_data() continue; for (int odd_x = 0; odd_x <= 1; ++odd_x) { for (int odd_width = 0; odd_width <= 1; ++odd_width) { - QString description = - QString("srcFormat %1, dstFormat %2, odd x: %3, odd width: %4") - .arg(srcFormat).arg(dstFormat).arg(odd_x).arg(odd_width); - - QTest::newRow(qPrintable(description)) << (10 + odd_x) << 10 << (20 + odd_width) << 20 + QTest::addRow("srcFormat %d, dstFormat %d, odd x: %d, odd width: %d", + srcFormat, dstFormat, odd_x, odd_width) + << (10 + odd_x) << 10 << (20 + odd_width) << 20 << QImage::Format(srcFormat) << QImage::Format(dstFormat); } diff --git a/tests/auto/gui/painting/qpolygon/tst_qpolygon.cpp b/tests/auto/gui/painting/qpolygon/tst_qpolygon.cpp index 13b6e28f5f..bf3e5dfb52 100644 --- a/tests/auto/gui/painting/qpolygon/tst_qpolygon.cpp +++ b/tests/auto/gui/painting/qpolygon/tst_qpolygon.cpp @@ -49,6 +49,8 @@ private slots: void boundingRectF(); void makeEllipse(); void swap(); + void intersections_data(); + void intersections(); }; tst_QPolygon::tst_QPolygon() @@ -159,5 +161,45 @@ void tst_QPolygon::swap() QCOMPARE(p2.count(),3); } +void tst_QPolygon::intersections_data() +{ + QTest::addColumn<QPolygon>("poly1"); + QTest::addColumn<QPolygon>("poly2"); + QTest::addColumn<bool>("result"); + + QTest::newRow("empty intersects nothing") + << QPolygon() + << QPolygon(QVector<QPoint>() << QPoint(0,0) << QPoint(10,10) << QPoint(-10,10)) + << false; + QTest::newRow("identical triangles") + << QPolygon(QVector<QPoint>() << QPoint(0,0) << QPoint(10,10) << QPoint(-10,10)) + << QPolygon(QVector<QPoint>() << QPoint(0,0) << QPoint(10,10) << QPoint(-10,10)) + << true; + QTest::newRow("not intersecting") + << QPolygon(QVector<QPoint>() << QPoint(0,0) << QPoint(10,10) << QPoint(-10,10)) + << QPolygon(QVector<QPoint>() << QPoint(0,20) << QPoint(10,12) << QPoint(-10,12)) + << false; + QTest::newRow("clean intersection of squares") + << QPolygon(QVector<QPoint>() << QPoint(0,0) << QPoint(0,10) << QPoint(10,10) << QPoint(10,0)) + << QPolygon(QVector<QPoint>() << QPoint(5,5) << QPoint(5,15) << QPoint(15,15) << QPoint(15,5)) + << true; + QTest::newRow("clean contains of squares") + << QPolygon(QVector<QPoint>() << QPoint(0,0) << QPoint(0,10) << QPoint(10,10) << QPoint(10,0)) + << QPolygon(QVector<QPoint>() << QPoint(5,5) << QPoint(5,8) << QPoint(8,8) << QPoint(8,5)) + << true; +} + +void tst_QPolygon::intersections() +{ + QFETCH(QPolygon, poly1); + QFETCH(QPolygon, poly2); + QFETCH(bool, result); + + QCOMPARE(poly2.intersects(poly1), poly1.intersects(poly2)); + QCOMPARE(poly2.intersected(poly1).isEmpty(), poly1.intersected(poly2).isEmpty()); + QCOMPARE(!poly1.intersected(poly2).isEmpty(), poly1.intersects(poly2)); + QCOMPARE(poly1.intersects(poly2), result); +} + QTEST_APPLESS_MAIN(tst_QPolygon) #include "tst_qpolygon.moc" diff --git a/tests/auto/gui/painting/qtransform/tst_qtransform.cpp b/tests/auto/gui/painting/qtransform/tst_qtransform.cpp index 0c089cdaa3..0a6a95daca 100644 --- a/tests/auto/gui/painting/qtransform/tst_qtransform.cpp +++ b/tests/auto/gui/painting/qtransform/tst_qtransform.cpp @@ -29,7 +29,6 @@ #include <QtTest/QtTest> #include "qtransform.h" -#include <math.h> #include <qpolygon.h> #include <qdebug.h> @@ -67,10 +66,6 @@ private: Q_DECLARE_METATYPE(QTransform) -#if defined(Q_OS_WIN) && !defined(M_PI) -#define M_PI 3.14159265897932384626433832795f -#endif - void tst_QTransform::mapRect_data() { mapping_data(); diff --git a/tests/auto/gui/painting/qwmatrix/tst_qwmatrix.cpp b/tests/auto/gui/painting/qwmatrix/tst_qwmatrix.cpp index a79526c434..46420b49c5 100644 --- a/tests/auto/gui/painting/qwmatrix/tst_qwmatrix.cpp +++ b/tests/auto/gui/painting/qwmatrix/tst_qwmatrix.cpp @@ -118,127 +118,96 @@ void tst_QWMatrix::mapping_data() #define M_PI 3.14159265897932384626433832795f #endif + const auto rotate = [](qreal degrees) { + const qreal rad = M_PI * degrees / 180.; + return QMatrix(std::cos(rad), -std::sin(rad), + std::sin(rad), std::cos(rad), 0, 0); + }; + // rotations - float deg = 0.; - QTest::newRow( "rot 0 a" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 0 a" ) << rotate(0.) << QRect( 0, 0, 30, 40 ) << QPolygon ( QRect( 0, 0, 30, 40 ) ); - deg = 0.00001f; - QTest::newRow( "rot 0 b" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 0 b" ) << rotate(0.00001f) << QRect( 0, 0, 30, 40 ) << QPolygon ( QRect( 0, 0, 30, 40 ) ); - deg = 0.; - QTest::newRow( "rot 0 c" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 0 c" ) << rotate(0.) << QRect( 10, 20, 30, 40 ) << QPolygon ( QRect( 10, 20, 30, 40 ) ); - deg = 0.00001f; - QTest::newRow( "rot 0 d" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 0 d" ) << rotate(0.00001f) << QRect( 10, 20, 30, 40 ) << QPolygon ( QRect( 10, 20, 30, 40 ) ); #if 0 - // rotations - deg = 90.; - QTest::newRow( "rotscale 90 a" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + const auto rotScale = [](qreal degrees, qreal scale) { + const qreal rad = M_PI * degrees / 180.; + return QMatrix(scale * std::cos(rad), -scale * std::sin(rad), + scale * std::sin(rad), scale * std::cos(rad), 0, 0); + }; + // rotations with scaling + QTest::newRow( "rotscale 90 a" ) << rotScale(90., 10) << QRect( 0, 0, 30, 40 ) << QPolygon( QRect( 0, -299, 400, 300 ) ); - deg = 90.00001; - QTest::newRow( "rotscale 90 b" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 90 b" ) << rotScale(90.00001, 10) << QRect( 0, 0, 30, 40 ) << QPolygon( QRect( 0, -299, 400, 300 ) ); - deg = 90.; - QTest::newRow( "rotscale 90 c" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 90 c" ) << rotScale(90., 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( 200, -399, 400, 300 ) ); - deg = 90.00001; - QTest::newRow( "rotscale 90 d" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 90 d" ) << rotScale(90.00001, 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( 200, -399, 400, 300 ) ); - deg = 180.; - QTest::newRow( "rotscale 180 a" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 180 a" ) << rotScale(180., 10) << QRect( 0, 0, 30, 40 ) << QPolygon( QRect( -299, -399, 300, 400 ) ); - deg = 180.000001; - QTest::newRow( "rotscale 180 b" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 180 b" ) << rotScale(180.000001, 10) << QRect( 0, 0, 30, 40 ) << QPolygon( QRect( -299, -399, 300, 400 ) ); - deg = 180.; - QTest::newRow( "rotscale 180 c" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 180 c" ) << rotScale(180., 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( -399, -599, 300, 400 ) ); - deg = 180.000001; - QTest::newRow( "rotscale 180 d" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 180 d" ) << rotScale(180.000001, 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( -399, -599, 300, 400 ) ); - deg = 270.; - QTest::newRow( "rotscale 270 a" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 270 a" ) << rotScale(270., 10) << QRect( 0, 0, 30, 40 ) << QPolygon( QRect( -399, 00, 400, 300 ) ); - deg = 270.0000001; - QTest::newRow( "rotscale 270 b" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 270 b" ) << rotScale(270.0000001, 10) << QRect( 0, 0, 30, 40 ) << QPolygon( QRect( -399, 00, 400, 300 ) ); - deg = 270.; - QTest::newRow( "rotscale 270 c" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 270 c" ) << rotScale(270., 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( -599, 100, 400, 300 ) ); - deg = 270.000001; - QTest::newRow( "rotscale 270 d" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rotscale 270 d" ) << rotScale(270.000001, 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( -599, 100, 400, 300 ) ); // rotations that are not multiples of 90 degrees. mapRect returns the bounding rect here. - deg = 45; - QTest::newRow( "rot 45 a" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 45 a" ) << rotate(45) << QRect( 0, 0, 10, 10 ) << QPolygon( QRect( 0, -7, 14, 14 ) ); - QTest::newRow( "rot 45 b" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 45 b" ) << rotate(45) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( 21, -14, 49, 49 ) ); - QTest::newRow( "rot 45 c" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 45 c" ) << rotScale(45, 10) << QRect( 0, 0, 10, 10 ) << QPolygon( QRect( 0, -70, 141, 141 ) ); - QTest::newRow( "rot 45 d" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot 45 d" ) << rotScale(45, 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( 212, -141, 495, 495 ) ); - deg = -45; - QTest::newRow( "rot -45 a" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot -45 a" ) << rotate(-45) << QRect( 0, 0, 10, 10 ) << QPolygon( QRect( -7, 0, 14, 14 ) ); - QTest::newRow( "rot -45 b" ) << QMatrix( std::cos( M_PI*deg/180. ), -std::sin( M_PI*deg/180. ), - std::sin( M_PI*deg/180. ), std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot -45 b" ) << rotate(-45) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( -35, 21, 49, 49 ) ); - QTest::newRow( "rot -45 c" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot -45 c" ) << rotScale(-45, 10) << QRect( 0, 0, 10, 10 ) << QPolygon( QRect( -70, 0, 141, 141 ) ); - QTest::newRow( "rot -45 d" ) << QMatrix( 10*std::cos( M_PI*deg/180. ), -10*std::sin( M_PI*deg/180. ), - 10*std::sin( M_PI*deg/180. ), 10*std::cos( M_PI*deg/180. ), 0, 0 ) + QTest::newRow( "rot -45 d" ) << rotScale(-45, 10) << QRect( 10, 20, 30, 40 ) << QPolygon( QRect( -353, 212, 495, 495 ) ); #endif diff --git a/tests/auto/gui/qvulkan/qvulkan.pro b/tests/auto/gui/qvulkan/qvulkan.pro new file mode 100644 index 0000000000..0db990a2d6 --- /dev/null +++ b/tests/auto/gui/qvulkan/qvulkan.pro @@ -0,0 +1,9 @@ +############################################################ +# Project file for autotest for gui/vulkan functionality +############################################################ + +CONFIG += testcase +TARGET = tst_qvulkan +QT += gui-private core-private testlib + +SOURCES += tst_qvulkan.cpp diff --git a/tests/auto/gui/qvulkan/tst_qvulkan.cpp b/tests/auto/gui/qvulkan/tst_qvulkan.cpp new file mode 100644 index 0000000000..8027935003 --- /dev/null +++ b/tests/auto/gui/qvulkan/tst_qvulkan.cpp @@ -0,0 +1,435 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 <QtGui/QVulkanInstance> +#include <QtGui/QVulkanFunctions> +#include <QtGui/QVulkanWindow> + +#include <QtTest/QtTest> + +#include <QSignalSpy> + +class tst_QVulkan : public QObject +{ + Q_OBJECT + +private slots: + void vulkanInstance(); + void vulkanCheckSupported(); + void vulkanPlainWindow(); + void vulkanVersionRequest(); + void vulkanWindow(); + void vulkanWindowRenderer(); + void vulkanWindowGrab(); +}; + +void tst_QVulkan::vulkanInstance() +{ + QVulkanInstance inst; + if (!inst.create()) + QSKIP("Vulkan init failed; skip"); + + QVERIFY(inst.isValid()); + QVERIFY(inst.vkInstance() != VK_NULL_HANDLE); + QVERIFY(inst.functions()); + QVERIFY(!inst.flags().testFlag(QVulkanInstance::NoDebugOutputRedirect)); + + inst.destroy(); + + QVERIFY(!inst.isValid()); + QVERIFY(inst.handle() == nullptr); + + inst.setFlags(QVulkanInstance::NoDebugOutputRedirect); + // pass a bogus layer and extension + inst.setExtensions(QByteArrayList() << "abcdefg" << "notanextension"); + inst.setLayers(QByteArrayList() << "notalayer"); + QVERIFY(inst.create()); + + QVERIFY(inst.isValid()); + QVERIFY(inst.vkInstance() != VK_NULL_HANDLE); + QVERIFY(inst.handle() != nullptr); + QVERIFY(inst.functions()); + QVERIFY(inst.flags().testFlag(QVulkanInstance::NoDebugOutputRedirect)); + QVERIFY(!inst.extensions().contains("abcdefg")); + QVERIFY(!inst.extensions().contains("notanextension")); + QVERIFY(!inst.extensions().contains("notalayer")); + // at least the surface extensions should be there however + QVERIFY(inst.extensions().contains("VK_KHR_surface")); + + QVERIFY(inst.getInstanceProcAddr("vkGetDeviceQueue")); +} + +void tst_QVulkan::vulkanCheckSupported() +{ + // Test the early calls to supportedLayers/extensions that need the library + // and some basics, but do not initialize the instance. + QVulkanInstance inst; + QVERIFY(!inst.isValid()); + + QVulkanInfoVector<QVulkanLayer> vl = inst.supportedLayers(); + qDebug() << vl; + QVERIFY(!inst.isValid()); + + QVulkanInfoVector<QVulkanExtension> ve = inst.supportedExtensions(); + qDebug() << ve; + QVERIFY(!inst.isValid()); + + if (inst.create()) { // skip the rest when Vulkan is not supported at all + QVERIFY(!ve.isEmpty()); + QVERIFY(ve == inst.supportedExtensions()); + } +} + +void tst_QVulkan::vulkanPlainWindow() +{ + QVulkanInstance inst; + if (!inst.create()) + QSKIP("Vulkan init failed; skip"); + + QWindow w; + w.setSurfaceType(QSurface::VulkanSurface); + w.setVulkanInstance(&inst); + w.resize(1024, 768); + w.show(); + QTest::qWaitForWindowExposed(&w); + + QCOMPARE(w.vulkanInstance(), &inst); + + VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(&w); + QVERIFY(surface != VK_NULL_HANDLE); + + // exercise supportsPresent (and QVulkanFunctions) a bit + QVulkanFunctions *f = inst.functions(); + VkPhysicalDevice physDev; + uint32_t count = 1; + VkResult err = f->vkEnumeratePhysicalDevices(inst.vkInstance(), &count, &physDev); + if (err != VK_SUCCESS) + QSKIP("No physical devices; skip"); + + VkPhysicalDeviceProperties physDevProps; + f->vkGetPhysicalDeviceProperties(physDev, &physDevProps); + qDebug("Device name: %s Driver version: %d.%d.%d", physDevProps.deviceName, + VK_VERSION_MAJOR(physDevProps.driverVersion), VK_VERSION_MINOR(physDevProps.driverVersion), + VK_VERSION_PATCH(physDevProps.driverVersion)); + + bool supports = inst.supportsPresent(physDev, 0, &w); + qDebug("queue family 0 supports presenting to window = %d", supports); +} + +void tst_QVulkan::vulkanVersionRequest() +{ + QVulkanInstance inst; + if (!inst.create()) + QSKIP("Vulkan init failed; skip"); + + // Now that we know Vulkan is functional, check the requested apiVersion is + // passed to vkCreateInstance as expected. + + inst.destroy(); + + inst.setApiVersion(QVersionNumber(10, 0, 0)); + QVERIFY(!inst.create()); + QCOMPARE(inst.errorCode(), VK_ERROR_INCOMPATIBLE_DRIVER); +} + +static void waitForUnexposed(QWindow *w) +{ + QElapsedTimer timer; + timer.start(); + while (w->isExposed()) { + int remaining = 5000 - int(timer.elapsed()); + if (remaining <= 0) + break; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + QCoreApplication::sendPostedEvents(Q_NULLPTR, QEvent::DeferredDelete); + QTest::qSleep(10); + } +} + +void tst_QVulkan::vulkanWindow() +{ + QVulkanInstance inst; + if (!inst.create()) + QSKIP("Vulkan init failed; skip"); + + // First let's forget to set the instance. + QVulkanWindow w; + QVERIFY(!w.isValid()); + w.resize(1024, 768); + w.show(); + QTest::qWaitForWindowExposed(&w); + QVERIFY(!w.isValid()); + + // Now set it. A simple hide - show should be enough to correct, this, no + // need for a full destroy - create. + w.hide(); + waitForUnexposed(&w); + w.setVulkanInstance(&inst); + QVector<VkPhysicalDeviceProperties> pdevs = w.availablePhysicalDevices(); + if (pdevs.isEmpty()) + QSKIP("No Vulkan physical devices; skip"); + w.show(); + QTest::qWaitForWindowExposed(&w); + QVERIFY(w.isValid()); + QCOMPARE(w.vulkanInstance(), &inst); + QVulkanInfoVector<QVulkanExtension> exts = w.supportedDeviceExtensions(); + + // Now destroy and recreate. + w.destroy(); + waitForUnexposed(&w); + QVERIFY(!w.isValid()); + // check that flags can be set between a destroy() - show() + w.setFlags(QVulkanWindow::PersistentResources); + // supported lists can be queried before expose too + QVERIFY(w.supportedDeviceExtensions() == exts); + w.show(); + QTest::qWaitForWindowExposed(&w); + QVERIFY(w.isValid()); + QVERIFY(w.flags().testFlag(QVulkanWindow::PersistentResources)); + + QVERIFY(w.physicalDevice() != VK_NULL_HANDLE); + QVERIFY(w.physicalDeviceProperties() != nullptr); + QVERIFY(w.device() != VK_NULL_HANDLE); + QVERIFY(w.graphicsQueue() != VK_NULL_HANDLE); + QVERIFY(w.graphicsCommandPool() != VK_NULL_HANDLE); + QVERIFY(w.defaultRenderPass() != VK_NULL_HANDLE); + + QVERIFY(w.concurrentFrameCount() > 0); + QVERIFY(w.concurrentFrameCount() <= QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT); +} + +class TestVulkanRenderer; + +class TestVulkanWindow : public QVulkanWindow +{ +public: + QVulkanWindowRenderer *createRenderer() override; + +private: + TestVulkanRenderer *m_renderer = nullptr; +}; + +struct TestVulkan { + int preInitResCount = 0; + int initResCount = 0; + int initSwcResCount = 0; + int releaseResCount = 0; + int releaseSwcResCount = 0; + int startNextFrameCount = 0; +} testVulkan; + +class TestVulkanRenderer : public QVulkanWindowRenderer +{ +public: + TestVulkanRenderer(QVulkanWindow *w) : m_window(w) { } + + void preInitResources() override; + void initResources() override; + void initSwapChainResources() override; + void releaseSwapChainResources() override; + void releaseResources() override; + + void startNextFrame() override; + +private: + QVulkanWindow *m_window; + QVulkanDeviceFunctions *m_devFuncs; +}; + +void TestVulkanRenderer::preInitResources() +{ + if (testVulkan.initResCount) { + qWarning("initResources called before preInitResources?!"); + testVulkan.preInitResCount = -1; + return; + } + + // Ensure the physical device and the surface are available at this stage. + VkPhysicalDevice physDev = m_window->physicalDevice(); + if (physDev == VK_NULL_HANDLE) { + qWarning("No physical device in preInitResources"); + testVulkan.preInitResCount = -1; + return; + } + VkSurfaceKHR surface = m_window->vulkanInstance()->surfaceForWindow(m_window); + if (surface == VK_NULL_HANDLE) { + qWarning("No surface in preInitResources"); + testVulkan.preInitResCount = -1; + return; + } + + ++testVulkan.preInitResCount; +} + +void TestVulkanRenderer::initResources() +{ + m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device()); + ++testVulkan.initResCount; +} + +void TestVulkanRenderer::initSwapChainResources() +{ + ++testVulkan.initSwcResCount; +} + +void TestVulkanRenderer::releaseSwapChainResources() +{ + ++testVulkan.releaseSwcResCount; +} + +void TestVulkanRenderer::releaseResources() +{ + ++testVulkan.releaseResCount; +} + +void TestVulkanRenderer::startNextFrame() +{ + ++testVulkan.startNextFrameCount; + + VkClearColorValue clearColor = { 0, 1, 0, 1 }; + VkClearDepthStencilValue clearDS = { 1, 0 }; + VkClearValue clearValues[2]; + memset(clearValues, 0, sizeof(clearValues)); + clearValues[0].color = clearColor; + clearValues[1].depthStencil = clearDS; + + VkRenderPassBeginInfo rpBeginInfo; + memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); + rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpBeginInfo.renderPass = m_window->defaultRenderPass(); + rpBeginInfo.framebuffer = m_window->currentFramebuffer(); + const QSize sz = m_window->swapChainImageSize(); + rpBeginInfo.renderArea.extent.width = sz.width(); + rpBeginInfo.renderArea.extent.height = sz.height(); + rpBeginInfo.clearValueCount = 2; + rpBeginInfo.pClearValues = clearValues; + VkCommandBuffer cmdBuf = m_window->currentCommandBuffer(); + m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + m_devFuncs->vkCmdEndRenderPass(cmdBuf); + + m_window->frameReady(); +} + +QVulkanWindowRenderer *TestVulkanWindow::createRenderer() +{ + Q_ASSERT(!m_renderer); + m_renderer = new TestVulkanRenderer(this); + return m_renderer; +} + +void tst_QVulkan::vulkanWindowRenderer() +{ + QVulkanInstance inst; + if (!inst.create()) + QSKIP("Vulkan init failed; skip"); + + testVulkan = TestVulkan(); + + TestVulkanWindow w; + w.setVulkanInstance(&inst); + w.resize(1024, 768); + w.show(); + QTest::qWaitForWindowExposed(&w); + + if (w.availablePhysicalDevices().isEmpty()) + QSKIP("No Vulkan physical devices; skip"); + + QVERIFY(testVulkan.preInitResCount == 1); + QVERIFY(testVulkan.initResCount == 1); + QVERIFY(testVulkan.initSwcResCount == 1); + // this has to be QTRY due to the async update in QVulkanWindowPrivate::ensureStarted() + QTRY_VERIFY(testVulkan.startNextFrameCount >= 1); + + QVERIFY(!w.swapChainImageSize().isEmpty()); + QVERIFY(w.colorFormat() != VK_FORMAT_UNDEFINED); + QVERIFY(w.depthStencilFormat() != VK_FORMAT_UNDEFINED); + + w.destroy(); + waitForUnexposed(&w); + QVERIFY(testVulkan.releaseSwcResCount == 1); + QVERIFY(testVulkan.releaseResCount == 1); +} + +void tst_QVulkan::vulkanWindowGrab() +{ + QVulkanInstance inst; + inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); + if (!inst.create()) + QSKIP("Vulkan init failed; skip"); + + testVulkan = TestVulkan(); + + TestVulkanWindow w; + w.setVulkanInstance(&inst); + w.resize(1024, 768); + w.show(); + QTest::qWaitForWindowExposed(&w); + + if (w.availablePhysicalDevices().isEmpty()) + QSKIP("No Vulkan physical devices; skip"); + + if (!w.supportsGrab()) + QSKIP("No grab support; skip"); + + QVERIFY(!w.swapChainImageSize().isEmpty()); + + QImage img1 = w.grab(); + QImage img2 = w.grab(); + QImage img3 = w.grab(); + + QVERIFY(!img1.isNull()); + QVERIFY(!img2.isNull()); + QVERIFY(!img3.isNull()); + + QCOMPARE(img1.size(), w.swapChainImageSize()); + QCOMPARE(img2.size(), w.swapChainImageSize()); + QCOMPARE(img3.size(), w.swapChainImageSize()); + + QRgb a = img1.pixel(10, 20); + QRgb b = img2.pixel(5, 5); + QRgb c = img3.pixel(50, 30); + + QCOMPARE(a, b); + QCOMPARE(b, c); + QRgb refPixel = qRgb(0, 255, 0); + + int redFuzz = qAbs(qRed(a) - qRed(refPixel)); + int greenFuzz = qAbs(qGreen(a) - qGreen(refPixel)); + int blueFuzz = qAbs(qBlue(a) - qBlue(refPixel)); + + QVERIFY(redFuzz <= 1); + QVERIFY(blueFuzz <= 1); + QVERIFY(greenFuzz <= 1); + + w.destroy(); +} + +QTEST_MAIN(tst_QVulkan) + +#include "tst_qvulkan.moc" diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index 764b99646b..2f3da2c196 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -187,6 +187,7 @@ private slots: void cssInheritance(); void lineHeightType(); + void cssLineHeightMultiplier(); private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void buildRegExpData(); @@ -3398,6 +3399,33 @@ void tst_QTextDocument::lineHeightType() { QTextDocument td; + td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: fixed; line-height: 10; -qt-line-height-type: fixed; }</style></head><body>Foobar</body></html>"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); + QCOMPARE(format.lineHeight(), 10.0); + } + + { + QTextDocument td; + td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: proportional; line-height: 3; }</style></head><body>Foobar</body></html>"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); + QCOMPARE(format.lineHeight(), 3.0); + } + + { + QTextDocument td; + td.setHtml("<html><head><style type=\"text/css\">body { line-height: 2.5; -qt-line-height-type: proportional; }</style></head><body>Foobar</body></html>"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); + QCOMPARE(format.lineHeight(), 2.5); + } + + { + QTextDocument td; td.setHtml("<html><head><style type=\"text/css\">body { line-height: 33; -qt-line-height-type: minimum; }</style></head><body>Foobar</body></html>"); QTextBlock block = td.begin(); QTextBlockFormat format = block.blockFormat(); @@ -3424,5 +3452,26 @@ void tst_QTextDocument::lineHeightType() } } +void tst_QTextDocument::cssLineHeightMultiplier() +{ + { + QTextDocument td; + td.setHtml("<html><head><style type=\"text/css\">body { line-height: 10; }</style></head><body>Foobar</body></html>"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); + QCOMPARE(format.lineHeight(), 1000.0); + } + + { + QTextDocument td; + td.setHtml("<html><head><style type=\"text/css\">body {line-height: 1.38; }</style></head><body>Foobar</body></html>"); + QTextBlock block = td.begin(); + QTextBlockFormat format = block.blockFormat(); + QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); + QCOMPARE(format.lineHeight(), 138.0); + } +} + QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc" diff --git a/tests/auto/gui/util/qshadergenerator/qshadergenerator.pro b/tests/auto/gui/util/qshadergenerator/qshadergenerator.pro new file mode 100644 index 0000000000..c1f610e029 --- /dev/null +++ b/tests/auto/gui/util/qshadergenerator/qshadergenerator.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +QT += testlib gui-private + +SOURCES += tst_qshadergenerator.cpp +TARGET = tst_qshadergenerator diff --git a/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp b/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp new file mode 100644 index 0000000000..655bc94f2c --- /dev/null +++ b/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** 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 <QtGui/private/qshadergenerator_p.h> + +namespace +{ + QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion) + { + auto format = QShaderFormat(); + format.setApi(api); + format.setVersion(QVersionNumber(majorVersion, minorVersion)); + return format; + } + + QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) + { + auto port = QShaderNodePort(); + port.direction = portDirection; + port.name = portName; + return port; + } + + QShaderNode createNode(const QVector<QShaderNodePort> &ports) + { + auto node = QShaderNode(); + node.setUuid(QUuid::createUuid()); + for (const auto &port : ports) + node.addPort(port); + return node; + } + + QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName, + const QUuid &targetUuid, const QString &targetName) + { + auto edge = QShaderGraph::Edge(); + edge.sourceNodeUuid = sourceUuid; + edge.sourcePortName = sourceName; + edge.targetNodeUuid = targetUuid; + edge.targetPortName = targetName; + return edge; + } + + QShaderGraph createGraph() + { + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + + auto graph = QShaderGraph(); + + auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "worldPosition") + }); + worldPosition.addRule(openGLES2, QShaderNode::Rule("highp vec3 $worldPosition = worldPosition;", + QByteArrayList() << "varying highp vec3 worldPosition;")); + worldPosition.addRule(openGL3, QShaderNode::Rule("vec3 $worldPosition = worldPosition;", + QByteArrayList() << "in vec3 worldPosition;")); + + auto texture = createNode({ + createPort(QShaderNodePort::Output, "texture") + }); + texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + + auto texCoord = createNode({ + createPort(QShaderNodePort::Output, "texCoord") + }); + texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;", + QByteArrayList() << "varying highp vec2 texCoord;")); + texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;", + QByteArrayList() << "in vec2 texCoord;")); + + auto lightIntensity = createNode({ + createPort(QShaderNodePort::Output, "lightIntensity") + }); + lightIntensity.addRule(openGLES2, QShaderNode::Rule("highp float $lightIntensity = lightIntensity;", + QByteArrayList() << "uniform highp float lightIntensity;")); + lightIntensity.addRule(openGL3, QShaderNode::Rule("float $lightIntensity = lightIntensity;", + QByteArrayList() << "uniform float lightIntensity;")); + + auto exposure = createNode({ + createPort(QShaderNodePort::Output, "exposure") + }); + exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;", + QByteArrayList() << "uniform highp float exposure;")); + exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;", + QByteArrayList() << "uniform float exposure;")); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;", + QByteArrayList() << "out vec4 fragColor;")); + + auto sampleTexture = createNode({ + createPort(QShaderNodePort::Input, "sampler"), + createPort(QShaderNodePort::Input, "coord"), + createPort(QShaderNodePort::Output, "color") + }); + sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);")); + sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);")); + + auto lightFunction = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + lightFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include es2/lightmodel.frag.inc")); + lightFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc")); + + auto exposureFunction = createNode({ + createPort(QShaderNodePort::Input, "inputColor"), + createPort(QShaderNodePort::Input, "exposure"), + createPort(QShaderNodePort::Output, "outputColor") + }); + exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + + graph.addNode(worldPosition); + graph.addNode(texture); + graph.addNode(texCoord); + graph.addNode(lightIntensity); + graph.addNode(exposure); + graph.addNode(fragColor); + graph.addNode(sampleTexture); + graph.addNode(lightFunction); + graph.addNode(exposureFunction); + + graph.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler")); + graph.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord")); + + graph.addEdge(createEdge(worldPosition.uuid(), "worldPosition", lightFunction.uuid(), "position")); + graph.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor")); + graph.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity")); + + graph.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor")); + graph.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure")); + + graph.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor")); + + return graph; + } +} + +class tst_QShaderGenerator : public QObject +{ + Q_OBJECT +private slots: + void shouldHaveDefaultState(); + void shouldGenerateShaderCode_data(); + void shouldGenerateShaderCode(); + void shouldGenerateVersionCommands_data(); + void shouldGenerateVersionCommands(); +}; + +void tst_QShaderGenerator::shouldHaveDefaultState() +{ + // GIVEN + auto generator = QShaderGenerator(); + + // THEN + QVERIFY(generator.graph.nodes().isEmpty()); + QVERIFY(generator.graph.edges().isEmpty()); + QVERIFY(!generator.format.isValid()); +} + +void tst_QShaderGenerator::shouldGenerateShaderCode_data() +{ + QTest::addColumn<QShaderGraph>("graph"); + QTest::addColumn<QShaderFormat>("format"); + QTest::addColumn<QByteArray>("expectedCode"); + + const auto graph = createGraph(); + + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + const auto openGL32 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2); + const auto openGL4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0); + + const auto versionGLES2 = QByteArrayList() << "#version 100 es" << ""; + const auto versionGL3 = QByteArrayList() << "#version 130" << ""; + const auto versionGL32 = QByteArrayList() << "#version 150 core" << ""; + const auto versionGL4 = QByteArrayList() << "#version 400 core" << ""; + + const auto es2Code = QByteArrayList() << "varying highp vec3 worldPosition;" + << "uniform sampler2D texture;" + << "varying highp vec2 texCoord;" + << "uniform highp float lightIntensity;" + << "uniform highp float exposure;" + << "#pragma include es2/lightmodel.frag.inc" + << "" + << "void main()" + << "{" + << " highp vec2 v2 = texCoord;" + << " sampler2D v1 = texture;" + << " highp float v3 = lightIntensity;" + << " highp vec4 v5 = texture2D(v1, v2);" + << " highp vec3 v0 = worldPosition;" + << " highp float v4 = exposure;" + << " highp vec4 v6 = lightModel(v5, v0, v3);" + << " highp vec4 v7 = v6 * pow(2.0, v4);" + << " gl_fragColor = v7;" + << "}" + << ""; + + const auto gl3Code = QByteArrayList() << "in vec3 worldPosition;" + << "uniform sampler2D texture;" + << "in vec2 texCoord;" + << "uniform float lightIntensity;" + << "uniform float exposure;" + << "out vec4 fragColor;" + << "#pragma include gl3/lightmodel.frag.inc" + << "" + << "void main()" + << "{" + << " vec2 v2 = texCoord;" + << " sampler2D v1 = texture;" + << " float v3 = lightIntensity;" + << " vec4 v5 = texture2D(v1, v2);" + << " vec3 v0 = worldPosition;" + << " float v4 = exposure;" + << " vec4 v6 = lightModel(v5, v0, v3);" + << " vec4 v7 = v6 * pow(2.0, v4);" + << " fragColor = v7;" + << "}" + << ""; + + QTest::newRow("EmptyGraphAndFormat") << QShaderGraph() << QShaderFormat() << QByteArrayLiteral("\nvoid main()\n{\n}\n"); + QTest::newRow("LightExposureGraphAndES2") << graph << openGLES2 << (versionGLES2 + es2Code).join('\n'); + QTest::newRow("LightExposureGraphAndGL3") << graph << openGL3 << (versionGL3 + gl3Code).join('\n'); + QTest::newRow("LightExposureGraphAndGL32") << graph << openGL32 << (versionGL32 + gl3Code).join('\n'); + QTest::newRow("LightExposureGraphAndGL4") << graph << openGL4 << (versionGL4 + gl3Code).join('\n'); +} + +void tst_QShaderGenerator::shouldGenerateShaderCode() +{ + // GIVEN + QFETCH(QShaderGraph, graph); + QFETCH(QShaderFormat, format); + + auto generator = QShaderGenerator(); + generator.graph = graph; + generator.format = format; + + // WHEN + const auto code = generator.createShaderCode(); + + // THEN + QFETCH(QByteArray, expectedCode); + QCOMPARE(code, expectedCode); +} + +void tst_QShaderGenerator::shouldGenerateVersionCommands_data() +{ + QTest::addColumn<QShaderFormat>("format"); + QTest::addColumn<QByteArray>("version"); + + QTest::newRow("GLES2") << createFormat(QShaderFormat::OpenGLES, 2, 0) << QByteArrayLiteral("#version 100 es"); + QTest::newRow("GLES3") << createFormat(QShaderFormat::OpenGLES, 3, 0) << QByteArrayLiteral("#version 300 es"); + + QTest::newRow("GL20") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) << QByteArrayLiteral("#version 110"); + QTest::newRow("GL21") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 1) << QByteArrayLiteral("#version 120"); + QTest::newRow("GL30") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 0) << QByteArrayLiteral("#version 130"); + QTest::newRow("GL31") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 1) << QByteArrayLiteral("#version 140"); + QTest::newRow("GL32") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 2) << QByteArrayLiteral("#version 150"); + QTest::newRow("GL33") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 3) << QByteArrayLiteral("#version 330"); + QTest::newRow("GL40") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 0) << QByteArrayLiteral("#version 400"); + QTest::newRow("GL41") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 1) << QByteArrayLiteral("#version 410"); + QTest::newRow("GL42") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 2) << QByteArrayLiteral("#version 420"); + QTest::newRow("GL43") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 3) << QByteArrayLiteral("#version 430"); + + QTest::newRow("GL20core") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) << QByteArrayLiteral("#version 110"); + QTest::newRow("GL21core") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 1) << QByteArrayLiteral("#version 120"); + QTest::newRow("GL30core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) << QByteArrayLiteral("#version 130"); + QTest::newRow("GL31core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1) << QByteArrayLiteral("#version 140"); + QTest::newRow("GL32core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2) << QByteArrayLiteral("#version 150 core"); + QTest::newRow("GL33core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 3) << QByteArrayLiteral("#version 330 core"); + QTest::newRow("GL40core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0) << QByteArrayLiteral("#version 400 core"); + QTest::newRow("GL41core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 1) << QByteArrayLiteral("#version 410 core"); + QTest::newRow("GL42core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 2) << QByteArrayLiteral("#version 420 core"); + QTest::newRow("GL43core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 3) << QByteArrayLiteral("#version 430 core"); + + QTest::newRow("GL20compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) << QByteArrayLiteral("#version 110"); + QTest::newRow("GL21compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 1) << QByteArrayLiteral("#version 120"); + QTest::newRow("GL30compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 0) << QByteArrayLiteral("#version 130"); + QTest::newRow("GL31compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 1) << QByteArrayLiteral("#version 140"); + QTest::newRow("GL32compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 2) << QByteArrayLiteral("#version 150 compatibility"); + QTest::newRow("GL33compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 3) << QByteArrayLiteral("#version 330 compatibility"); + QTest::newRow("GL40compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 0) << QByteArrayLiteral("#version 400 compatibility"); + QTest::newRow("GL41compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 1) << QByteArrayLiteral("#version 410 compatibility"); + QTest::newRow("GL42compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 2) << QByteArrayLiteral("#version 420 compatibility"); + QTest::newRow("GL43compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 3) << QByteArrayLiteral("#version 430 compatibility"); +} + +void tst_QShaderGenerator::shouldGenerateVersionCommands() +{ + // GIVEN + QFETCH(QShaderFormat, format); + + auto generator = QShaderGenerator(); + generator.format = format; + + // WHEN + const auto code = generator.createShaderCode(); + + // THEN + QFETCH(QByteArray, version); + const auto expectedCode = (QByteArrayList() << version + << "" + << "" + << "void main()" + << "{" + << "}" + << "").join('\n'); + QCOMPARE(code, expectedCode); +} + +QTEST_MAIN(tst_QShaderGenerator) + +#include "tst_qshadergenerator.moc" diff --git a/tests/auto/gui/util/qshadergraph/qshadergraph.pro b/tests/auto/gui/util/qshadergraph/qshadergraph.pro new file mode 100644 index 0000000000..ec54941c77 --- /dev/null +++ b/tests/auto/gui/util/qshadergraph/qshadergraph.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +QT += testlib gui-private + +SOURCES += tst_qshadergraph.cpp +TARGET = tst_qshadergraph diff --git a/tests/auto/gui/util/qshadergraph/tst_qshadergraph.cpp b/tests/auto/gui/util/qshadergraph/tst_qshadergraph.cpp new file mode 100644 index 0000000000..b2a9d99b19 --- /dev/null +++ b/tests/auto/gui/util/qshadergraph/tst_qshadergraph.cpp @@ -0,0 +1,659 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** 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 <QtGui/private/qshadergraph_p.h> + +namespace +{ + QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) + { + auto port = QShaderNodePort(); + port.direction = portDirection; + port.name = portName; + return port; + } + + QShaderNode createNode(const QVector<QShaderNodePort> &ports) + { + auto node = QShaderNode(); + node.setUuid(QUuid::createUuid()); + for (const auto &port : ports) + node.addPort(port); + return node; + } + + QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName, + const QUuid &targetUuid, const QString &targetName) + { + auto edge = QShaderGraph::Edge(); + edge.sourceNodeUuid = sourceUuid; + edge.sourcePortName = sourceName; + edge.targetNodeUuid = targetUuid; + edge.targetPortName = targetName; + return edge; + } + + QShaderGraph::Statement createStatement(const QShaderNode &node, + const QVector<int> &inputs = QVector<int>(), + const QVector<int> &outputs = QVector<int>()) + { + auto statement = QShaderGraph::Statement(); + statement.node = node; + statement.inputs = inputs; + statement.outputs = outputs; + return statement; + } + + void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement) + { + qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs; + } + + void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &expected) + { + if (statements != expected) { + for (int i = 0; i < qMax(statements.size(), expected.size()); i++) { + qDebug() << "----" << i << "----"; + if (i < statements.size()) + debugStatement("A:", statements.at(i)); + if (i < expected.size()) + debugStatement("E:", expected.at(i)); + qDebug() << "-----------"; + } + } + } +} + +class tst_QShaderGraph : public QObject +{ + Q_OBJECT +private slots: + void shouldHaveEdgeDefaultState(); + void shouldTestEdgesEquality_data(); + void shouldTestEdgesEquality(); + void shouldManipulateStatementMembers(); + void shouldTestStatementsEquality_data(); + void shouldTestStatementsEquality(); + void shouldFindIndexFromPortNameInStatements_data(); + void shouldFindIndexFromPortNameInStatements(); + void shouldManageNodeList(); + void shouldManageEdgeList(); + void shouldSerializeGraphForCodeGeneration(); + void shouldHandleUnboundPortsDuringGraphSerialization(); + void shouldSurviveCyclesDuringGraphSerialization(); + void shouldDealWithEdgesJumpingOverLayers(); +}; + +void tst_QShaderGraph::shouldHaveEdgeDefaultState() +{ + // GIVEN + auto edge = QShaderGraph::Edge(); + + // THEN + QVERIFY(edge.sourceNodeUuid.isNull()); + QVERIFY(edge.sourcePortName.isEmpty()); + QVERIFY(edge.targetNodeUuid.isNull()); + QVERIFY(edge.targetPortName.isEmpty()); +} + +void tst_QShaderGraph::shouldTestEdgesEquality_data() +{ + QTest::addColumn<QShaderGraph::Edge>("left"); + QTest::addColumn<QShaderGraph::Edge>("right"); + QTest::addColumn<bool>("expected"); + + const auto sourceUuid1 = QUuid::createUuid(); + const auto sourceUuid2 = QUuid::createUuid(); + const auto targetUuid1 = QUuid::createUuid(); + const auto targetUuid2 = QUuid::createUuid(); + + QTest::newRow("Equals") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") + << createEdge(sourceUuid1, "foo", targetUuid1, "bar") + << true; + QTest::newRow("SourceUuid") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") + << createEdge(sourceUuid2, "foo", targetUuid1, "bar") + << false; + QTest::newRow("SourceName") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") + << createEdge(sourceUuid1, "bleh", targetUuid1, "bar") + << false; + QTest::newRow("TargetUuid") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") + << createEdge(sourceUuid1, "foo", targetUuid2, "bar") + << false; + QTest::newRow("TargetName") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") + << createEdge(sourceUuid1, "foo", targetUuid1, "bleh") + << false; +} + +void tst_QShaderGraph::shouldTestEdgesEquality() +{ + // GIVEN + QFETCH(QShaderGraph::Edge, left); + QFETCH(QShaderGraph::Edge, right); + + // WHEN + const auto equal = (left == right); + const auto notEqual = (left != right); + + // THEN + QFETCH(bool, expected); + QCOMPARE(equal, expected); + QCOMPARE(notEqual, !expected); +} + +void tst_QShaderGraph::shouldManipulateStatementMembers() +{ + // GIVEN + auto statement = QShaderGraph::Statement(); + + // THEN (default state) + QVERIFY(statement.inputs.isEmpty()); + QVERIFY(statement.outputs.isEmpty()); + QVERIFY(statement.node.uuid().isNull()); + QVERIFY(statement.uuid().isNull()); + + // WHEN + const auto node = createNode({}); + statement.node = node; + + // THEN + QCOMPARE(statement.uuid(), node.uuid()); + + // WHEN + statement.node = QShaderNode(); + + // THEN + QVERIFY(statement.uuid().isNull()); +} + +void tst_QShaderGraph::shouldTestStatementsEquality_data() +{ + QTest::addColumn<QShaderGraph::Statement>("left"); + QTest::addColumn<QShaderGraph::Statement>("right"); + QTest::addColumn<bool>("expected"); + + const auto node1 = createNode({}); + const auto node2 = createNode({}); + + QTest::newRow("EqualNodes") << createStatement(node1, {1, 2}, {3, 4}) + << createStatement(node1, {1, 2}, {3, 4}) + << true; + QTest::newRow("EqualInvalids") << createStatement(QShaderNode(), {1, 2}, {3, 4}) + << createStatement(QShaderNode(), {1, 2}, {3, 4}) + << true; + QTest::newRow("Nodes") << createStatement(node1, {1, 2}, {3, 4}) + << createStatement(node2, {1, 2}, {3, 4}) + << false; + QTest::newRow("Inputs") << createStatement(node1, {1, 2}, {3, 4}) + << createStatement(node1, {1, 2, 0}, {3, 4}) + << false; + QTest::newRow("Outputs") << createStatement(node1, {1, 2}, {3, 4}) + << createStatement(node1, {1, 2}, {3, 0, 4}) + << false; +} + +void tst_QShaderGraph::shouldTestStatementsEquality() +{ + // GIVEN + QFETCH(QShaderGraph::Statement, left); + QFETCH(QShaderGraph::Statement, right); + + // WHEN + const auto equal = (left == right); + const auto notEqual = (left != right); + + // THEN + QFETCH(bool, expected); + QCOMPARE(equal, expected); + QCOMPARE(notEqual, !expected); +} + +void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements_data() +{ + QTest::addColumn<QShaderGraph::Statement>("statement"); + QTest::addColumn<QString>("portName"); + QTest::addColumn<int>("expectedInputIndex"); + QTest::addColumn<int>("expectedOutputIndex"); + + const auto inputNodeStatement = createStatement(createNode({ + createPort(QShaderNodePort::Output, "input") + })); + const auto outputNodeStatement = createStatement(createNode({ + createPort(QShaderNodePort::Input, "output") + })); + const auto functionNodeStatement = createStatement(createNode({ + createPort(QShaderNodePort::Input, "input1"), + createPort(QShaderNodePort::Output, "output1"), + createPort(QShaderNodePort::Input, "input2"), + createPort(QShaderNodePort::Output, "output2"), + createPort(QShaderNodePort::Output, "output3"), + createPort(QShaderNodePort::Input, "input3") + })); + + QTest::newRow("Invalid") << QShaderGraph::Statement() << "foo" << -1 << -1; + QTest::newRow("InputNodeWrongName") << inputNodeStatement << "foo" << -1 << -1; + QTest::newRow("InputNodeExistingName") << inputNodeStatement << "input" << -1 << 0; + QTest::newRow("OutputNodeWrongName") << outputNodeStatement << "foo" << -1 << -1; + QTest::newRow("OutputNodeExistingName") << outputNodeStatement << "output" << 0 << -1; + QTest::newRow("FunctionNodeWrongName") << functionNodeStatement << "foo" << -1 << -1; + QTest::newRow("FunctionNodeInput1") << functionNodeStatement << "input1" << 0 << -1; + QTest::newRow("FunctionNodeOutput1") << functionNodeStatement << "output1" << -1 << 0; + QTest::newRow("FunctionNodeInput2") << functionNodeStatement << "input2" << 1 << -1; + QTest::newRow("FunctionNodeOutput2") << functionNodeStatement << "output2" << -1 << 1; + QTest::newRow("FunctionNodeInput3") << functionNodeStatement << "input3" << 2 << -1; + QTest::newRow("FunctionNodeOutput3") << functionNodeStatement << "output3" << -1 << 2; +} + +void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements() +{ + // GIVEN + QFETCH(QShaderGraph::Statement, statement); + QFETCH(QString, portName); + QFETCH(int, expectedInputIndex); + QFETCH(int, expectedOutputIndex); + + // WHEN + const auto inputIndex = statement.portIndex(QShaderNodePort::Input, portName); + const auto outputIndex = statement.portIndex(QShaderNodePort::Output, portName); + + // THEN + QCOMPARE(inputIndex, expectedInputIndex); + QCOMPARE(outputIndex, expectedOutputIndex); +} + +void tst_QShaderGraph::shouldManageNodeList() +{ + // GIVEN + const auto node1 = createNode({createPort(QShaderNodePort::Output, "node1")}); + const auto node2 = createNode({createPort(QShaderNodePort::Output, "node2")}); + + auto graph = QShaderGraph(); + + // THEN (default state) + QVERIFY(graph.nodes().isEmpty()); + + // WHEN + graph.addNode(node1); + + // THEN + QCOMPARE(graph.nodes().size(), 1); + QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); + QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); + + // WHEN + graph.addNode(node2); + + // THEN + QCOMPARE(graph.nodes().size(), 2); + QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); + QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); + QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid()); + QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name); + + + // WHEN + graph.removeNode(node2); + + // THEN + QCOMPARE(graph.nodes().size(), 1); + QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); + QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); + + // WHEN + graph.addNode(node2); + + // THEN + QCOMPARE(graph.nodes().size(), 2); + QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); + QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); + QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid()); + QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name); + + // WHEN + const auto node1bis = [node1] { + auto res = node1; + auto port = res.ports().at(0); + port.name = QStringLiteral("node1bis"); + res.addPort(port); + return res; + }(); + graph.addNode(node1bis); + + // THEN + QCOMPARE(graph.nodes().size(), 2); + QCOMPARE(graph.nodes().at(0).uuid(), node2.uuid()); + QCOMPARE(graph.nodes().at(0).ports().at(0).name, node2.ports().at(0).name); + QCOMPARE(graph.nodes().at(1).uuid(), node1bis.uuid()); + QCOMPARE(graph.nodes().at(1).ports().at(0).name, node1bis.ports().at(0).name); +} + +void tst_QShaderGraph::shouldManageEdgeList() +{ + // GIVEN + const auto edge1 = createEdge(QUuid::createUuid(), "foo", QUuid::createUuid(), "bar"); + const auto edge2 = createEdge(QUuid::createUuid(), "baz", QUuid::createUuid(), "boo"); + + auto graph = QShaderGraph(); + + // THEN (default state) + QVERIFY(graph.edges().isEmpty()); + + // WHEN + graph.addEdge(edge1); + + // THEN + QCOMPARE(graph.edges().size(), 1); + QCOMPARE(graph.edges().at(0), edge1); + + // WHEN + graph.addEdge(edge2); + + // THEN + QCOMPARE(graph.edges().size(), 2); + QCOMPARE(graph.edges().at(0), edge1); + QCOMPARE(graph.edges().at(1), edge2); + + + // WHEN + graph.removeEdge(edge2); + + // THEN + QCOMPARE(graph.edges().size(), 1); + QCOMPARE(graph.edges().at(0), edge1); + + // WHEN + graph.addEdge(edge2); + + // THEN + QCOMPARE(graph.edges().size(), 2); + QCOMPARE(graph.edges().at(0), edge1); + QCOMPARE(graph.edges().at(1), edge2); + + // WHEN + graph.addEdge(edge1); + + // THEN + QCOMPARE(graph.edges().size(), 2); + QCOMPARE(graph.edges().at(0), edge1); + QCOMPARE(graph.edges().at(1), edge2); +} + +void tst_QShaderGraph::shouldSerializeGraphForCodeGeneration() +{ + // GIVEN + const auto input1 = createNode({ + createPort(QShaderNodePort::Output, "input1Value") + }); + const auto input2 = createNode({ + createPort(QShaderNodePort::Output, "input2Value") + }); + const auto output1 = createNode({ + createPort(QShaderNodePort::Input, "output1Value") + }); + const auto output2 = createNode({ + createPort(QShaderNodePort::Input, "output2Value") + }); + const auto function1 = createNode({ + createPort(QShaderNodePort::Input, "function1Input"), + createPort(QShaderNodePort::Output, "function1Output") + }); + const auto function2 = createNode({ + createPort(QShaderNodePort::Input, "function2Input1"), + createPort(QShaderNodePort::Input, "function2Input2"), + createPort(QShaderNodePort::Output, "function2Output") + }); + const auto function3 = createNode({ + createPort(QShaderNodePort::Input, "function3Input1"), + createPort(QShaderNodePort::Input, "function3Input2"), + createPort(QShaderNodePort::Output, "function3Output1"), + createPort(QShaderNodePort::Output, "function3Output2") + }); + + const auto graph = [=] { + auto res = QShaderGraph(); + res.addNode(input1); + res.addNode(input2); + res.addNode(output1); + res.addNode(output2); + res.addNode(function1); + res.addNode(function2); + res.addNode(function3); + res.addEdge(createEdge(input1.uuid(), "input1Value", function1.uuid(), "function1Input")); + res.addEdge(createEdge(input1.uuid(), "input1Value", function2.uuid(), "function2Input1")); + res.addEdge(createEdge(input2.uuid(), "input2Value", function2.uuid(), "function2Input2")); + res.addEdge(createEdge(function1.uuid(), "function1Output", function3.uuid(), "function3Input1")); + res.addEdge(createEdge(function2.uuid(), "function2Output", function3.uuid(), "function3Input2")); + res.addEdge(createEdge(function3.uuid(), "function3Output1", output1.uuid(), "output1Value")); + res.addEdge(createEdge(function3.uuid(), "function3Output2", output2.uuid(), "output2Value")); + return res; + }(); + + // WHEN + const auto statements = graph.createStatements(); + + // THEN + const auto expected = QVector<QShaderGraph::Statement>() + << createStatement(input2, {}, {1}) + << createStatement(input1, {}, {0}) + << createStatement(function2, {0, 1}, {3}) + << createStatement(function1, {0}, {2}) + << createStatement(function3, {2, 3}, {4, 5}) + << createStatement(output2, {5}, {}) + << createStatement(output1, {4}, {}); + dumpStatementsIfNeeded(statements, expected); + QCOMPARE(statements, expected); +} + +void tst_QShaderGraph::shouldHandleUnboundPortsDuringGraphSerialization() +{ + // GIVEN + const auto input = createNode({ + createPort(QShaderNodePort::Output, "input") + }); + const auto unboundInput = createNode({ + createPort(QShaderNodePort::Output, "unbound") + }); + const auto output = createNode({ + createPort(QShaderNodePort::Input, "output") + }); + const auto unboundOutput = createNode({ + createPort(QShaderNodePort::Input, "unbound") + }); + const auto function = createNode({ + createPort(QShaderNodePort::Input, "functionInput1"), + createPort(QShaderNodePort::Input, "functionInput2"), + createPort(QShaderNodePort::Input, "functionInput3"), + createPort(QShaderNodePort::Output, "functionOutput1"), + createPort(QShaderNodePort::Output, "functionOutput2"), + createPort(QShaderNodePort::Output, "functionOutput3") + }); + + const auto graph = [=] { + auto res = QShaderGraph(); + res.addNode(input); + res.addNode(unboundInput); + res.addNode(output); + res.addNode(unboundOutput); + res.addNode(function); + res.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput2")); + res.addEdge(createEdge(function.uuid(), "functionOutput2", output.uuid(), "output")); + return res; + }(); + + // WHEN + const auto statements = graph.createStatements(); + + // THEN + // Note that no edge leads to the unbound input + const auto expected = QVector<QShaderGraph::Statement>() + << createStatement(input, {}, {0}) + << createStatement(function, {-1, 0, -1}, {2, 3, 4}) + << createStatement(unboundOutput, {-1}, {}) + << createStatement(output, {3}, {}); + dumpStatementsIfNeeded(statements, expected); + QCOMPARE(statements, expected); +} + +void tst_QShaderGraph::shouldSurviveCyclesDuringGraphSerialization() +{ + // GIVEN + const auto input = createNode({ + createPort(QShaderNodePort::Output, "input") + }); + const auto output = createNode({ + createPort(QShaderNodePort::Input, "output") + }); + const auto function1 = createNode({ + createPort(QShaderNodePort::Input, "function1Input1"), + createPort(QShaderNodePort::Input, "function1Input2"), + createPort(QShaderNodePort::Output, "function1Output") + }); + const auto function2 = createNode({ + createPort(QShaderNodePort::Input, "function2Input"), + createPort(QShaderNodePort::Output, "function2Output") + }); + const auto function3 = createNode({ + createPort(QShaderNodePort::Input, "function3Input"), + createPort(QShaderNodePort::Output, "function3Output") + }); + + const auto graph = [=] { + auto res = QShaderGraph(); + res.addNode(input); + res.addNode(output); + res.addNode(function1); + res.addNode(function2); + res.addNode(function3); + res.addEdge(createEdge(input.uuid(), "input", function1.uuid(), "function1Input1")); + res.addEdge(createEdge(function1.uuid(), "function1Output", function2.uuid(), "function2Input")); + res.addEdge(createEdge(function2.uuid(), "function2Output", function3.uuid(), "function3Input")); + res.addEdge(createEdge(function3.uuid(), "function3Output", function1.uuid(), "function1Input2")); + res.addEdge(createEdge(function2.uuid(), "function2Output", output.uuid(), "output")); + return res; + }(); + + // WHEN + const auto statements = graph.createStatements(); + + // THEN + // Obviously will lead to a compile failure later on since it cuts everything beyond the cycle + const auto expected = QVector<QShaderGraph::Statement>() + << createStatement(output, {2}, {}); + dumpStatementsIfNeeded(statements, expected); + QCOMPARE(statements, expected); +} + +void tst_QShaderGraph::shouldDealWithEdgesJumpingOverLayers() +{ + // GIVEN + const auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "worldPosition") + }); + const auto texture = createNode({ + createPort(QShaderNodePort::Output, "texture") + }); + const auto texCoord = createNode({ + createPort(QShaderNodePort::Output, "texCoord") + }); + const auto lightIntensity = createNode({ + createPort(QShaderNodePort::Output, "lightIntensity") + }); + const auto exposure = createNode({ + createPort(QShaderNodePort::Output, "exposure") + }); + const auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + const auto sampleTexture = createNode({ + createPort(QShaderNodePort::Input, "sampler"), + createPort(QShaderNodePort::Input, "coord"), + createPort(QShaderNodePort::Output, "color") + }); + const auto lightFunction = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + const auto exposureFunction = createNode({ + createPort(QShaderNodePort::Input, "inputColor"), + createPort(QShaderNodePort::Input, "exposure"), + createPort(QShaderNodePort::Output, "outputColor") + }); + + const auto graph = [=] { + auto res = QShaderGraph(); + + res.addNode(worldPosition); + res.addNode(texture); + res.addNode(texCoord); + res.addNode(lightIntensity); + res.addNode(exposure); + res.addNode(fragColor); + res.addNode(sampleTexture); + res.addNode(lightFunction); + res.addNode(exposureFunction); + + res.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler")); + res.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord")); + + res.addEdge(createEdge(worldPosition.uuid(), "worldPosition", lightFunction.uuid(), "position")); + res.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor")); + res.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity")); + + res.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor")); + res.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure")); + + res.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor")); + + return res; + }(); + + // WHEN + const auto statements = graph.createStatements(); + + // THEN + const auto expected = QVector<QShaderGraph::Statement>() + << createStatement(texCoord, {}, {2}) + << createStatement(texture, {}, {1}) + << createStatement(lightIntensity, {}, {3}) + << createStatement(sampleTexture, {1, 2}, {5}) + << createStatement(worldPosition, {}, {0}) + << createStatement(exposure, {}, {4}) + << createStatement(lightFunction, {5, 0, 3}, {6}) + << createStatement(exposureFunction, {6, 4}, {7}) + << createStatement(fragColor, {7}, {}); + dumpStatementsIfNeeded(statements, expected); + QCOMPARE(statements, expected); +} + +QTEST_MAIN(tst_QShaderGraph) + +#include "tst_qshadergraph.moc" diff --git a/tests/auto/gui/util/qshadergraphloader/qshadergraphloader.pro b/tests/auto/gui/util/qshadergraphloader/qshadergraphloader.pro new file mode 100644 index 0000000000..e80a93f9e8 --- /dev/null +++ b/tests/auto/gui/util/qshadergraphloader/qshadergraphloader.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +QT += testlib gui-private + +SOURCES += tst_qshadergraphloader.cpp +TARGET = tst_qshadergraphloader diff --git a/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp b/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp new file mode 100644 index 0000000000..418370fa2a --- /dev/null +++ b/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp @@ -0,0 +1,552 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** 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 <QtCore/qbuffer.h> + +#include <QtGui/private/qshadergraphloader_p.h> + +using QBufferPointer = QSharedPointer<QBuffer>; +Q_DECLARE_METATYPE(QBufferPointer); + +using PrototypeHash = QHash<QString, QShaderNode>; +Q_DECLARE_METATYPE(PrototypeHash); + +namespace +{ + QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly) + { + auto buffer = QBufferPointer::create(); + buffer->setData(data); + if (openMode != QIODevice::NotOpen) + buffer->open(openMode); + return buffer; + } + + QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion) + { + auto format = QShaderFormat(); + format.setApi(api); + format.setVersion(QVersionNumber(majorVersion, minorVersion)); + return format; + } + + QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) + { + auto port = QShaderNodePort(); + port.direction = portDirection; + port.name = portName; + return port; + } + + QShaderNode createNode(const QVector<QShaderNodePort> &ports) + { + auto node = QShaderNode(); + node.setUuid(QUuid::createUuid()); + for (const auto &port : ports) + node.addPort(port); + return node; + } + + QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName, + const QUuid &targetUuid, const QString &targetName) + { + auto edge = QShaderGraph::Edge(); + edge.sourceNodeUuid = sourceUuid; + edge.sourcePortName = sourceName; + edge.targetNodeUuid = targetUuid; + edge.targetPortName = targetName; + return edge; + } + + QShaderGraph createGraph() + { + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + + auto graph = QShaderGraph(); + + auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "worldPosition") + }); + worldPosition.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}")); + worldPosition.addRule(openGLES2, QShaderNode::Rule("highp vec3 $worldPosition = worldPosition;", + QByteArrayList() << "varying highp vec3 worldPosition;")); + worldPosition.addRule(openGL3, QShaderNode::Rule("vec3 $worldPosition = worldPosition;", + QByteArrayList() << "in vec3 worldPosition;")); + + auto texture = createNode({ + createPort(QShaderNodePort::Output, "texture") + }); + texture.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}")); + texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + + auto texCoord = createNode({ + createPort(QShaderNodePort::Output, "texCoord") + }); + texCoord.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}")); + texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;", + QByteArrayList() << "varying highp vec2 texCoord;")); + texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;", + QByteArrayList() << "in vec2 texCoord;")); + + auto lightIntensity = createNode({ + createPort(QShaderNodePort::Output, "lightIntensity") + }); + lightIntensity.setUuid(QUuid("{00000000-0000-0000-0000-000000000004}")); + lightIntensity.addRule(openGLES2, QShaderNode::Rule("highp float $lightIntensity = lightIntensity;", + QByteArrayList() << "uniform highp float lightIntensity;")); + lightIntensity.addRule(openGL3, QShaderNode::Rule("float $lightIntensity = lightIntensity;", + QByteArrayList() << "uniform float lightIntensity;")); + + auto exposure = createNode({ + createPort(QShaderNodePort::Output, "exposure") + }); + exposure.setUuid(QUuid("{00000000-0000-0000-0000-000000000005}")); + exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;", + QByteArrayList() << "uniform highp float exposure;")); + exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;", + QByteArrayList() << "uniform float exposure;")); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + fragColor.setUuid(QUuid("{00000000-0000-0000-0000-000000000006}")); + fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;", + QByteArrayList() << "out vec4 fragColor;")); + + auto sampleTexture = createNode({ + createPort(QShaderNodePort::Input, "sampler"), + createPort(QShaderNodePort::Input, "coord"), + createPort(QShaderNodePort::Output, "color") + }); + sampleTexture.setUuid(QUuid("{00000000-0000-0000-0000-000000000007}")); + sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);")); + sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);")); + + auto lightFunction = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + lightFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000008}")); + lightFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include es2/lightmodel.frag.inc")); + lightFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc")); + + auto exposureFunction = createNode({ + createPort(QShaderNodePort::Input, "inputColor"), + createPort(QShaderNodePort::Input, "exposure"), + createPort(QShaderNodePort::Output, "outputColor") + }); + exposureFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000009}")); + exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + + graph.addNode(worldPosition); + graph.addNode(texture); + graph.addNode(texCoord); + graph.addNode(lightIntensity); + graph.addNode(exposure); + graph.addNode(fragColor); + graph.addNode(sampleTexture); + graph.addNode(lightFunction); + graph.addNode(exposureFunction); + + graph.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler")); + graph.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord")); + + graph.addEdge(createEdge(worldPosition.uuid(), "worldPosition", lightFunction.uuid(), "position")); + graph.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor")); + graph.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity")); + + graph.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor")); + graph.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure")); + + graph.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor")); + + return graph; + } + + void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement) + { + qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs; + } + + void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &expected) + { + if (statements != expected) { + for (int i = 0; i < qMax(statements.size(), expected.size()); i++) { + qDebug() << "----" << i << "----"; + if (i < statements.size()) + debugStatement("A:", statements.at(i)); + if (i < expected.size()) + debugStatement("E:", expected.at(i)); + qDebug() << "-----------"; + } + } + } +} + +class tst_QShaderGraphLoader : public QObject +{ + Q_OBJECT +private slots: + void shouldManipulateLoaderMembers(); + void shouldLoadFromJsonStream_data(); + void shouldLoadFromJsonStream(); +}; + +void tst_QShaderGraphLoader::shouldManipulateLoaderMembers() +{ + // GIVEN + auto loader = QShaderGraphLoader(); + + // THEN (default state) + QCOMPARE(loader.status(), QShaderGraphLoader::Null); + QVERIFY(!loader.device()); + QVERIFY(loader.graph().nodes().isEmpty()); + QVERIFY(loader.graph().edges().isEmpty()); + QVERIFY(loader.prototypes().isEmpty()); + + // WHEN + auto device1 = createBuffer(QByteArray("..........."), QIODevice::NotOpen); + loader.setDevice(device1.data()); + + // THEN + QCOMPARE(loader.status(), QShaderGraphLoader::Error); + QCOMPARE(loader.device(), device1.data()); + QVERIFY(loader.graph().nodes().isEmpty()); + QVERIFY(loader.graph().edges().isEmpty()); + + // WHEN + auto device2 = createBuffer(QByteArray("..........."), QIODevice::ReadOnly); + loader.setDevice(device2.data()); + + // THEN + QCOMPARE(loader.status(), QShaderGraphLoader::Waiting); + QCOMPARE(loader.device(), device2.data()); + QVERIFY(loader.graph().nodes().isEmpty()); + QVERIFY(loader.graph().edges().isEmpty()); + + + // WHEN + const auto prototypes = [this]{ + auto res = QHash<QString, QShaderNode>(); + res.insert("foo", createNode({})); + return res; + }(); + loader.setPrototypes(prototypes); + + // THEN + QCOMPARE(loader.prototypes().size(), prototypes.size()); + QVERIFY(loader.prototypes().contains("foo")); + QCOMPARE(loader.prototypes().value("foo").uuid(), prototypes.value("foo").uuid()); +} + +void tst_QShaderGraphLoader::shouldLoadFromJsonStream_data() +{ + QTest::addColumn<QBufferPointer>("device"); + QTest::addColumn<PrototypeHash>("prototypes"); + QTest::addColumn<QShaderGraph>("graph"); + QTest::addColumn<QShaderGraphLoader::Status>("status"); + + QTest::newRow("empty") << createBuffer("", QIODevice::ReadOnly) << PrototypeHash() + << QShaderGraph() << QShaderGraphLoader::Error; + + const auto smallJson = "{" + " \"nodes\": [" + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"type\": \"MyInput\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"type\": \"MyOutput\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"type\": \"MyFunction\"" + " }" + " ]," + " \"edges\": [" + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"sourcePort\": \"input\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"targetPort\": \"functionInput\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"sourcePort\": \"functionOutput\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"targetPort\": \"output\"" + " }" + " ]" + "}"; + + const auto smallProtos = [this]{ + auto protos = PrototypeHash(); + + auto input = createNode({ + createPort(QShaderNodePort::Output, "input") + }); + protos.insert("MyInput", input); + + auto output = createNode({ + createPort(QShaderNodePort::Input, "output") + }); + protos.insert("MyOutput", output); + + auto function = createNode({ + createPort(QShaderNodePort::Input, "functionInput"), + createPort(QShaderNodePort::Output, "functionOutput") + }); + protos.insert("MyFunction", function); + return protos; + }(); + + const auto smallGraph = [this]{ + auto graph = QShaderGraph(); + + auto input = createNode({ + createPort(QShaderNodePort::Output, "input") + }); + input.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}")); + auto output = createNode({ + createPort(QShaderNodePort::Input, "output") + }); + output.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}")); + auto function = createNode({ + createPort(QShaderNodePort::Input, "functionInput"), + createPort(QShaderNodePort::Output, "functionOutput") + }); + function.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}")); + + graph.addNode(input); + graph.addNode(output); + graph.addNode(function); + graph.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput")); + graph.addEdge(createEdge(function.uuid(), "functionOutput", output.uuid(), "output")); + + return graph; + }(); + + QTest::newRow("TwoNodesOneEdge") << createBuffer(smallJson) << smallProtos << smallGraph << QShaderGraphLoader::Ready; + QTest::newRow("NotOpen") << createBuffer(smallJson, QIODevice::NotOpen) << smallProtos << QShaderGraph() << QShaderGraphLoader::Error; + QTest::newRow("NoPrototype") << createBuffer(smallJson) << PrototypeHash() << QShaderGraph() << QShaderGraphLoader::Error; + + const auto complexJson = "{" + " \"nodes\": [" + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"type\": \"worldPosition\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"type\": \"texture\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"type\": \"texCoord\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000004}\"," + " \"type\": \"lightIntensity\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000005}\"," + " \"type\": \"exposure\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000006}\"," + " \"type\": \"fragColor\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"type\": \"sampleTexture\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"type\": \"lightModel\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"type\": \"exposureFunction\"" + " }" + " ]," + " \"edges\": [" + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"sourcePort\": \"texture\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"targetPort\": \"sampler\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"sourcePort\": \"texCoord\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"targetPort\": \"coord\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"sourcePort\": \"worldPosition\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"targetPort\": \"position\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"sourcePort\": \"color\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"targetPort\": \"baseColor\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000004}\"," + " \"sourcePort\": \"lightIntensity\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"targetPort\": \"lightIntensity\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"sourcePort\": \"outputColor\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"targetPort\": \"inputColor\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000005}\"," + " \"sourcePort\": \"exposure\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"targetPort\": \"exposure\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"sourcePort\": \"outputColor\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000006}\"," + " \"targetPort\": \"fragColor\"" + " }" + " ]" + "}"; + + const auto complexProtos = [this]{ + auto protos = PrototypeHash(); + + auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "worldPosition") + }); + protos.insert("worldPosition", worldPosition); + + auto texture = createNode({ + createPort(QShaderNodePort::Output, "texture") + }); + protos.insert("texture", texture); + + auto texCoord = createNode({ + createPort(QShaderNodePort::Output, "texCoord") + }); + protos.insert("texCoord", texCoord); + + auto lightIntensity = createNode({ + createPort(QShaderNodePort::Output, "lightIntensity") + }); + protos.insert("lightIntensity", lightIntensity); + + auto exposure = createNode({ + createPort(QShaderNodePort::Output, "exposure") + }); + protos.insert("exposure", exposure); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + protos.insert("fragColor", fragColor); + + auto sampleTexture = createNode({ + createPort(QShaderNodePort::Input, "sampler"), + createPort(QShaderNodePort::Input, "coord"), + createPort(QShaderNodePort::Output, "color") + }); + protos.insert("sampleTexture", sampleTexture); + + auto lightModel = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + protos.insert("lightModel", lightModel); + + auto exposureFunction = createNode({ + createPort(QShaderNodePort::Input, "inputColor"), + createPort(QShaderNodePort::Input, "exposure"), + createPort(QShaderNodePort::Output, "outputColor") + }); + protos.insert("exposureFunction", exposureFunction); + + return protos; + }(); + + const auto complexGraph = createGraph(); + + QTest::newRow("ComplexGraph") << createBuffer(complexJson) << complexProtos << complexGraph << QShaderGraphLoader::Ready; +} + +void tst_QShaderGraphLoader::shouldLoadFromJsonStream() +{ + // GIVEN + QFETCH(QBufferPointer, device); + QFETCH(PrototypeHash, prototypes); + + auto loader = QShaderGraphLoader(); + + // WHEN + loader.setPrototypes(prototypes); + loader.setDevice(device.data()); + loader.load(); + + // THEN + QFETCH(QShaderGraphLoader::Status, status); + QCOMPARE(loader.status(), status); + + QFETCH(QShaderGraph, graph); + const auto statements = loader.graph().createStatements(); + const auto expected = graph.createStatements(); + dumpStatementsIfNeeded(statements, expected); + QCOMPARE(statements, expected); +} + +QTEST_MAIN(tst_QShaderGraphLoader) + +#include "tst_qshadergraphloader.moc" diff --git a/tests/auto/gui/util/qshadernodes/qshadernodes.pro b/tests/auto/gui/util/qshadernodes/qshadernodes.pro new file mode 100644 index 0000000000..5ab8b73a51 --- /dev/null +++ b/tests/auto/gui/util/qshadernodes/qshadernodes.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +QT += testlib gui-private + +SOURCES += tst_qshadernodes.cpp +TARGET = tst_qshadernodes diff --git a/tests/auto/gui/util/qshadernodes/tst_qshadernodes.cpp b/tests/auto/gui/util/qshadernodes/tst_qshadernodes.cpp new file mode 100644 index 0000000000..9d7139c880 --- /dev/null +++ b/tests/auto/gui/util/qshadernodes/tst_qshadernodes.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** 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 <QtGui/private/qshaderformat_p.h> +#include <QtGui/private/qshadernode_p.h> +#include <QtGui/private/qshadernodeport_p.h> + +namespace +{ + QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion, + const QStringList &extensions = QStringList(), + const QString &vendor = QString()) + { + auto format = QShaderFormat(); + format.setApi(api); + format.setVersion(QVersionNumber(majorVersion, minorVersion)); + format.setExtensions(extensions); + format.setVendor(vendor); + return format; + } + + QShaderNodePort createPort(QShaderNodePort::Direction direction, const QString &name) + { + auto port = QShaderNodePort(); + port.direction = direction; + port.name = name; + return port; + } +} + +class tst_QShaderNodes : public QObject +{ + Q_OBJECT +private slots: + void shouldManipulateFormatMembers(); + void shouldVerifyFormatsEquality_data(); + void shouldVerifyFormatsEquality(); + void shouldVerifyFormatsCompatibilities_data(); + void shouldVerifyFormatsCompatibilities(); + + void shouldHaveDefaultPortState(); + void shouldVerifyPortsEquality_data(); + void shouldVerifyPortsEquality(); + + void shouldManipulateNodeUuidPortsAndRules(); + void shouldHandleNodeRulesSupportAndOrder(); +}; + +void tst_QShaderNodes::shouldManipulateFormatMembers() +{ + // GIVEN + auto format = QShaderFormat(); + + // THEN (default state) + QCOMPARE(format.api(), QShaderFormat::NoApi); + QCOMPARE(format.version().majorVersion(), 0); + QCOMPARE(format.version().minorVersion(), 0); + QCOMPARE(format.extensions(), QStringList()); + QCOMPARE(format.vendor(), QString()); + QVERIFY(!format.isValid()); + + // WHEN + format.setApi(QShaderFormat::OpenGLES); + + // THEN + QCOMPARE(format.api(), QShaderFormat::OpenGLES); + QCOMPARE(format.version().majorVersion(), 0); + QCOMPARE(format.version().minorVersion(), 0); + QCOMPARE(format.extensions(), QStringList()); + QCOMPARE(format.vendor(), QString()); + QVERIFY(!format.isValid()); + + // WHEN + format.setVersion(QVersionNumber(3)); + + // THEN + QCOMPARE(format.api(), QShaderFormat::OpenGLES); + QCOMPARE(format.version().majorVersion(), 3); + QCOMPARE(format.version().minorVersion(), 0); + QCOMPARE(format.extensions(), QStringList()); + QCOMPARE(format.vendor(), QString()); + QVERIFY(format.isValid()); + + // WHEN + format.setVersion(QVersionNumber(3, 2)); + + // THEN + QCOMPARE(format.api(), QShaderFormat::OpenGLES); + QCOMPARE(format.version().majorVersion(), 3); + QCOMPARE(format.version().minorVersion(), 2); + QCOMPARE(format.extensions(), QStringList()); + QCOMPARE(format.vendor(), QString()); + QVERIFY(format.isValid()); + + // WHEN + format.setExtensions({"foo", "bar"}); + + // THEN + QCOMPARE(format.api(), QShaderFormat::OpenGLES); + QCOMPARE(format.version().majorVersion(), 3); + QCOMPARE(format.version().minorVersion(), 2); + QCOMPARE(format.extensions(), QStringList({"bar", "foo"})); + QCOMPARE(format.vendor(), QString()); + QVERIFY(format.isValid()); + + // WHEN + format.setVendor(QStringLiteral("KDAB")); + + // THEN + QCOMPARE(format.api(), QShaderFormat::OpenGLES); + QCOMPARE(format.version().majorVersion(), 3); + QCOMPARE(format.version().minorVersion(), 2); + QCOMPARE(format.extensions(), QStringList({"bar", "foo"})); + QCOMPARE(format.vendor(), QStringLiteral("KDAB")); + QVERIFY(format.isValid()); +} + +void tst_QShaderNodes::shouldVerifyFormatsEquality_data() +{ + QTest::addColumn<QShaderFormat>("left"); + QTest::addColumn<QShaderFormat>("right"); + QTest::addColumn<bool>("expected"); + + QTest::newRow("Equals") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << true; + QTest::newRow("Apis") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << createFormat(QShaderFormat::OpenGLNoProfile, 3, 0, {"foo", "bar"}, "KDAB") + << false; + QTest::newRow("Major") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0, {"foo", "bar"}, "KDAB") + << false; + QTest::newRow("Minor") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1, {"foo", "bar"}, "KDAB") + << false; + QTest::newRow("Extensions") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo"}, "KDAB") + << false; + QTest::newRow("Vendor") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}) + << false; +} + +void tst_QShaderNodes::shouldVerifyFormatsEquality() +{ + // GIVEN + QFETCH(QShaderFormat, left); + QFETCH(QShaderFormat, right); + + // WHEN + const auto equal = (left == right); + const auto notEqual = (left != right); + + // THEN + QFETCH(bool, expected); + QCOMPARE(equal, expected); + QCOMPARE(notEqual, !expected); +} + +void tst_QShaderNodes::shouldVerifyFormatsCompatibilities_data() +{ + QTest::addColumn<QShaderFormat>("reference"); + QTest::addColumn<QShaderFormat>("tested"); + QTest::addColumn<bool>("expected"); + + QTest::newRow("NoProfileVsES") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLES, 2, 0) + << true; + QTest::newRow("CoreProfileVsES") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLES, 2, 0) + << false; + QTest::newRow("CompatProfileVsES") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLES, 2, 0) + << true; + + QTest::newRow("ESVsNoProfile") << createFormat(QShaderFormat::OpenGLES, 2, 0) + << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << false; + QTest::newRow("ESVsCoreProfile") << createFormat(QShaderFormat::OpenGLES, 2, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << false; + QTest::newRow("ESVsCompatProfile") << createFormat(QShaderFormat::OpenGLES, 2, 0) + << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << false; + + QTest::newRow("CoreVsNoProfile") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << false; + QTest::newRow("CoreVsCompat") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << false; + QTest::newRow("CoreVsCore") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << true; + + QTest::newRow("NoProfileVsCore") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << true; + QTest::newRow("NoProvileVsCompat") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << true; + QTest::newRow("NoProfileVsNoProfile") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << true; + + QTest::newRow("CompatVsCore") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << true; + QTest::newRow("CompatVsCompat") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << true; + QTest::newRow("CompatVsNoProfile") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) + << true; + + QTest::newRow("MajorForwardCompat_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << true; + QTest::newRow("MajorForwardCompat_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 4) + << true; + QTest::newRow("MajorForwardCompat_3") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) + << false; + QTest::newRow("MajorForwardCompat_4") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 4) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) + << false; + + QTest::newRow("MinorForwardCompat_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) + << true; + QTest::newRow("MinorForwardCompat_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1) + << false; + + QTest::newRow("Extensions_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo"}) + << true; + QTest::newRow("Extensions_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo"}) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}) + << false; + + QTest::newRow("Vendor_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}) + << true; + QTest::newRow("Vendor_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}) + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB") + << false; + QTest::newRow("Vendor_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB") + << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB") + << true; +} + +void tst_QShaderNodes::shouldVerifyFormatsCompatibilities() +{ + // GIVEN + QFETCH(QShaderFormat, reference); + QFETCH(QShaderFormat, tested); + + // WHEN + const auto supported = reference.supports(tested); + + // THEN + QFETCH(bool, expected); + QCOMPARE(supported, expected); +} + +void tst_QShaderNodes::shouldHaveDefaultPortState() +{ + // GIVEN + auto port = QShaderNodePort(); + + // THEN + QCOMPARE(port.direction, QShaderNodePort::Output); + QVERIFY(port.name.isEmpty()); +} + +void tst_QShaderNodes::shouldVerifyPortsEquality_data() +{ + QTest::addColumn<QShaderNodePort>("left"); + QTest::addColumn<QShaderNodePort>("right"); + QTest::addColumn<bool>("expected"); + + QTest::newRow("Equals") << createPort(QShaderNodePort::Input, "foo") + << createPort(QShaderNodePort::Input, "foo") + << true; + QTest::newRow("Direction") << createPort(QShaderNodePort::Input, "foo") + << createPort(QShaderNodePort::Output, "foo") + << false; + QTest::newRow("Name") << createPort(QShaderNodePort::Input, "foo") + << createPort(QShaderNodePort::Input, "bar") + << false; +} + +void tst_QShaderNodes::shouldVerifyPortsEquality() +{ + // GIVEN + QFETCH(QShaderNodePort, left); + QFETCH(QShaderNodePort, right); + + // WHEN + const auto equal = (left == right); + const auto notEqual = (left != right); + + // THEN + QFETCH(bool, expected); + QCOMPARE(equal, expected); + QCOMPARE(notEqual, !expected); +} + +void tst_QShaderNodes::shouldManipulateNodeUuidPortsAndRules() +{ + // GIVEN + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + + const auto es2Rule = QShaderNode::Rule(QByteArrayLiteral("gles2"), {"#pragma include es2/foo.inc", "#pragma include es2/bar.inc"}); + const auto gl3Rule = QShaderNode::Rule(QByteArrayLiteral("gl3"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"}); + const auto gl3bisRule = QShaderNode::Rule(QByteArrayLiteral("gl3bis"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"}); + + auto node = QShaderNode(); + + // THEN (default state) + QCOMPARE(node.type(), QShaderNode::Invalid); + QVERIFY(node.uuid().isNull()); + QVERIFY(node.ports().isEmpty()); + QVERIFY(node.availableFormats().isEmpty()); + + // WHEN + const auto uuid = QUuid::createUuid(); + node.setUuid(uuid); + + // THEN + QCOMPARE(node.uuid(), uuid); + + // WHEN + auto firstPort = QShaderNodePort(); + firstPort.direction = QShaderNodePort::Input; + firstPort.name = QStringLiteral("foo"); + node.addPort(firstPort); + + // THEN + QCOMPARE(node.type(), QShaderNode::Output); + QCOMPARE(node.ports().size(), 1); + QCOMPARE(node.ports().at(0), firstPort); + QVERIFY(node.availableFormats().isEmpty()); + + // WHEN + auto secondPort = QShaderNodePort(); + secondPort.direction = QShaderNodePort::Output; + secondPort.name = QStringLiteral("bar"); + node.addPort(secondPort); + + // THEN + QCOMPARE(node.type(), QShaderNode::Function); + QCOMPARE(node.ports().size(), 2); + QCOMPARE(node.ports().at(0), firstPort); + QCOMPARE(node.ports().at(1), secondPort); + QVERIFY(node.availableFormats().isEmpty()); + + // WHEN + node.removePort(firstPort); + + // THEN + QCOMPARE(node.type(), QShaderNode::Input); + QCOMPARE(node.ports().size(), 1); + QCOMPARE(node.ports().at(0), secondPort); + QVERIFY(node.availableFormats().isEmpty()); + + + // WHEN + node.addRule(openGLES2, es2Rule); + node.addRule(openGL3, gl3Rule); + + // THEN + QCOMPARE(node.availableFormats().size(), 2); + QCOMPARE(node.availableFormats().at(0), openGLES2); + QCOMPARE(node.availableFormats().at(1), openGL3); + QCOMPARE(node.rule(openGLES2), es2Rule); + QCOMPARE(node.rule(openGL3), gl3Rule); + + // WHEN + node.removeRule(openGLES2); + + // THEN + QCOMPARE(node.availableFormats().size(), 1); + QCOMPARE(node.availableFormats().at(0), openGL3); + QCOMPARE(node.rule(openGL3), gl3Rule); + + // WHEN + node.addRule(openGLES2, es2Rule); + + // THEN + QCOMPARE(node.availableFormats().size(), 2); + QCOMPARE(node.availableFormats().at(0), openGL3); + QCOMPARE(node.availableFormats().at(1), openGLES2); + QCOMPARE(node.rule(openGLES2), es2Rule); + QCOMPARE(node.rule(openGL3), gl3Rule); + + // WHEN + node.addRule(openGL3, gl3bisRule); + + // THEN + QCOMPARE(node.availableFormats().size(), 2); + QCOMPARE(node.availableFormats().at(0), openGLES2); + QCOMPARE(node.availableFormats().at(1), openGL3); + QCOMPARE(node.rule(openGLES2), es2Rule); + QCOMPARE(node.rule(openGL3), gl3bisRule); +} + +void tst_QShaderNodes::shouldHandleNodeRulesSupportAndOrder() +{ + // GIVEN + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + const auto openGL32 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2); + const auto openGL4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0); + + const auto es2Rule = QShaderNode::Rule(QByteArrayLiteral("gles2"), {"#pragma include es2/foo.inc", "#pragma include es2/bar.inc"}); + const auto gl3Rule = QShaderNode::Rule(QByteArrayLiteral("gl3"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"}); + const auto gl32Rule = QShaderNode::Rule(QByteArrayLiteral("gl32"), {"#pragma include gl32/foo.inc", "#pragma include gl32/bar.inc"}); + const auto gl3bisRule = QShaderNode::Rule(QByteArrayLiteral("gl3bis"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"}); + + auto node = QShaderNode(); + + // WHEN + node.addRule(openGLES2, es2Rule); + node.addRule(openGL3, gl3Rule); + + // THEN + QCOMPARE(node.availableFormats().size(), 2); + QCOMPARE(node.availableFormats().at(0), openGLES2); + QCOMPARE(node.availableFormats().at(1), openGL3); + QCOMPARE(node.rule(openGLES2), es2Rule); + QCOMPARE(node.rule(openGL3), gl3Rule); + QCOMPARE(node.rule(openGL32), gl3Rule); + QCOMPARE(node.rule(openGL4), gl3Rule); + + // WHEN + node.addRule(openGL32, gl32Rule); + + // THEN + QCOMPARE(node.availableFormats().size(), 3); + QCOMPARE(node.availableFormats().at(0), openGLES2); + QCOMPARE(node.availableFormats().at(1), openGL3); + QCOMPARE(node.availableFormats().at(2), openGL32); + QCOMPARE(node.rule(openGLES2), es2Rule); + QCOMPARE(node.rule(openGL3), gl3Rule); + QCOMPARE(node.rule(openGL32), gl32Rule); + QCOMPARE(node.rule(openGL4), gl32Rule); + + // WHEN + node.addRule(openGL3, gl3bisRule); + + // THEN + QCOMPARE(node.availableFormats().size(), 3); + QCOMPARE(node.availableFormats().at(0), openGLES2); + QCOMPARE(node.availableFormats().at(1), openGL32); + QCOMPARE(node.availableFormats().at(2), openGL3); + QCOMPARE(node.rule(openGLES2), es2Rule); + QCOMPARE(node.rule(openGL3), gl3bisRule); + QCOMPARE(node.rule(openGL32), gl3bisRule); + QCOMPARE(node.rule(openGL4), gl3bisRule); +} + +QTEST_MAIN(tst_QShaderNodes) + +#include "tst_qshadernodes.moc" diff --git a/tests/auto/gui/util/qshadernodesloader/qshadernodesloader.pro b/tests/auto/gui/util/qshadernodesloader/qshadernodesloader.pro new file mode 100644 index 0000000000..b9c26a2942 --- /dev/null +++ b/tests/auto/gui/util/qshadernodesloader/qshadernodesloader.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +QT += testlib gui-private + +SOURCES += tst_qshadernodesloader.cpp +TARGET = tst_qshadernodesloader diff --git a/tests/auto/gui/util/qshadernodesloader/tst_qshadernodesloader.cpp b/tests/auto/gui/util/qshadernodesloader/tst_qshadernodesloader.cpp new file mode 100644 index 0000000000..59c476acc3 --- /dev/null +++ b/tests/auto/gui/util/qshadernodesloader/tst_qshadernodesloader.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** 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 <QtCore/qbuffer.h> + +#include <QtGui/private/qshadernodesloader_p.h> + +using QBufferPointer = QSharedPointer<QBuffer>; +Q_DECLARE_METATYPE(QBufferPointer); + +using NodeHash = QHash<QString, QShaderNode>; +Q_DECLARE_METATYPE(NodeHash); + +namespace +{ + QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly) + { + auto buffer = QBufferPointer::create(); + buffer->setData(data); + if (openMode != QIODevice::NotOpen) + buffer->open(openMode); + return buffer; + } + + QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion, + const QStringList &extensions = QStringList(), + const QString &vendor = QString()) + { + auto format = QShaderFormat(); + format.setApi(api); + format.setVersion(QVersionNumber(majorVersion, minorVersion)); + format.setExtensions(extensions); + format.setVendor(vendor); + return format; + } + + QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) + { + auto port = QShaderNodePort(); + port.direction = portDirection; + port.name = portName; + return port; + } + + QShaderNode createNode(const QVector<QShaderNodePort> &ports) + { + auto node = QShaderNode(); + for (const auto &port : ports) + node.addPort(port); + return node; + } +} + +class tst_QShaderNodesLoader : public QObject +{ + Q_OBJECT +private slots: + void shouldManipulateLoaderMembers(); + void shouldLoadFromJsonStream_data(); + void shouldLoadFromJsonStream(); +}; + +void tst_QShaderNodesLoader::shouldManipulateLoaderMembers() +{ + // GIVEN + auto loader = QShaderNodesLoader(); + + // THEN (default state) + QCOMPARE(loader.status(), QShaderNodesLoader::Null); + QVERIFY(!loader.device()); + QVERIFY(loader.nodes().isEmpty()); + + // WHEN + auto device1 = createBuffer(QByteArray("..........."), QIODevice::NotOpen); + loader.setDevice(device1.data()); + + // THEN + QCOMPARE(loader.status(), QShaderNodesLoader::Error); + QCOMPARE(loader.device(), device1.data()); + QVERIFY(loader.nodes().isEmpty()); + + // WHEN + auto device2 = createBuffer(QByteArray("..........."), QIODevice::ReadOnly); + loader.setDevice(device2.data()); + + // THEN + QCOMPARE(loader.status(), QShaderNodesLoader::Waiting); + QCOMPARE(loader.device(), device2.data()); + QVERIFY(loader.nodes().isEmpty()); +} + +void tst_QShaderNodesLoader::shouldLoadFromJsonStream_data() +{ + QTest::addColumn<QBufferPointer>("device"); + QTest::addColumn<NodeHash>("nodes"); + QTest::addColumn<QShaderNodesLoader::Status>("status"); + + QTest::newRow("empty") << createBuffer("", QIODevice::ReadOnly) << NodeHash() << QShaderNodesLoader::Error; + + const auto smallJson = "{" + " \"worldPosition\": {" + " \"outputs\": [" + " \"worldPosition\"" + " ]," + " \"rules\": [" + " {" + " \"format\": {" + " \"api\": \"OpenGLES\"," + " \"major\": 2," + " \"minor\": 0" + " }," + " \"substitution\": \"highp vec3 $worldPosition = worldPosition;\"," + " \"headerSnippets\": [ \"varying highp vec3 worldPosition;\" ]" + " }," + " {" + " \"format\": {" + " \"api\": \"OpenGLCompatibilityProfile\"," + " \"major\": 2," + " \"minor\": 1" + " }," + " \"substitution\": \"vec3 $worldPosition = worldPosition;\"," + " \"headerSnippets\": [ \"in vec3 worldPosition;\" ]" + " }" + " ]" + " }," + " \"fragColor\": {" + " \"inputs\": [" + " \"fragColor\"" + " ]," + " \"rules\": [" + " {" + " \"format\": {" + " \"api\": \"OpenGLES\"," + " \"major\": 2," + " \"minor\": 0" + " }," + " \"substitution\": \"gl_fragColor = $fragColor;\"" + " }," + " {" + " \"format\": {" + " \"api\": \"OpenGLNoProfile\"," + " \"major\": 4," + " \"minor\": 0" + " }," + " \"substitution\": \"fragColor = $fragColor;\"," + " \"headerSnippets\": [ \"out vec4 fragColor;\" ]" + " }" + " ]" + " }," + " \"lightModel\": {" + " \"inputs\": [" + " \"baseColor\"," + " \"position\"," + " \"lightIntensity\"" + " ]," + " \"outputs\": [" + " \"outputColor\"" + " ]," + " \"rules\": [" + " {" + " \"format\": {" + " \"api\": \"OpenGLES\"," + " \"major\": 2," + " \"minor\": 0," + " \"extensions\": [ \"ext1\", \"ext2\" ]," + " \"vendor\": \"kdab\"" + " }," + " \"substitution\": \"highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\"," + " \"headerSnippets\": [ \"#pragma include es2/lightmodel.frag.inc\" ]" + " }," + " {" + " \"format\": {" + " \"api\": \"OpenGLCoreProfile\"," + " \"major\": 3," + " \"minor\": 3" + " }," + " \"substitution\": \"vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\"," + " \"headerSnippets\": [ \"#pragma include gl3/lightmodel.frag.inc\" ]" + " }" + " ]" + " }" + "}"; + + const auto smallProtos = [this]{ + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGLES2Extended = createFormat(QShaderFormat::OpenGLES, 2, 0, {"ext1", "ext2"}, "kdab"); + const auto openGL2 = createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 1); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 3); + const auto openGL4 = createFormat(QShaderFormat::OpenGLNoProfile, 4, 0); + + auto protos = NodeHash(); + + auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "worldPosition") + }); + worldPosition.addRule(openGLES2, QShaderNode::Rule("highp vec3 $worldPosition = worldPosition;", + QByteArrayList() << "varying highp vec3 worldPosition;")); + worldPosition.addRule(openGL2, QShaderNode::Rule("vec3 $worldPosition = worldPosition;", + QByteArrayList() << "in vec3 worldPosition;")); + protos.insert("worldPosition", worldPosition); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(openGL4, QShaderNode::Rule("fragColor = $fragColor;", + QByteArrayList() << "out vec4 fragColor;")); + protos.insert(QStringLiteral("fragColor"), fragColor); + + auto lightModel = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + lightModel.addRule(openGLES2Extended, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include es2/lightmodel.frag.inc")); + lightModel.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc")); + protos.insert("lightModel", lightModel); + + return protos; + }(); + + QTest::newRow("NotOpen") << createBuffer(smallJson, QIODevice::NotOpen) << NodeHash() << QShaderNodesLoader::Error; + QTest::newRow("CorrectJSON") << createBuffer(smallJson) << smallProtos << QShaderNodesLoader::Ready; +} + +void tst_QShaderNodesLoader::shouldLoadFromJsonStream() +{ + // GIVEN + QFETCH(QBufferPointer, device); + + auto loader = QShaderNodesLoader(); + + // WHEN + loader.setDevice(device.data()); + loader.load(); + + // THEN + QFETCH(QShaderNodesLoader::Status, status); + QCOMPARE(loader.status(), status); + + QFETCH(NodeHash, nodes); + const auto sortedKeys = [](const NodeHash &nodes) { + auto res = nodes.keys(); + res.sort(); + return res; + }; + QCOMPARE(sortedKeys(loader.nodes()), sortedKeys(nodes)); + for (const auto &key : nodes.keys()) { + const auto actual = loader.nodes().value(key); + const auto expected = nodes.value(key); + + QVERIFY(actual.uuid().isNull()); + QCOMPARE(actual.ports(), expected.ports()); + QCOMPARE(actual.availableFormats(), expected.availableFormats()); + for (const auto &format : expected.availableFormats()) { + QCOMPARE(actual.rule(format), expected.rule(format)); + } + } +} + +QTEST_MAIN(tst_QShaderNodesLoader) + +#include "tst_qshadernodesloader.moc" diff --git a/tests/auto/gui/util/util.pro b/tests/auto/gui/util/util.pro index f2c4515dc2..940e892e5f 100644 --- a/tests/auto/gui/util/util.pro +++ b/tests/auto/gui/util/util.pro @@ -5,4 +5,9 @@ SUBDIRS= \ qintvalidator \ qregexpvalidator \ qregularexpressionvalidator \ + qshadergenerator \ + qshadergraph \ + qshadergraphloader \ + qshadernodes \ + qshadernodesloader \ diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp index 2671c253cb..cb7e66bad4 100644 --- a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp @@ -86,6 +86,8 @@ class tst_QHostInfo : public QObject private slots: void init(); void initTestCase(); + void swapFunction(); + void moveOperator(); void getSetCheck(); void staticInformation(); void lookupIPv4_data(); @@ -129,6 +131,29 @@ private: #endif }; +void tst_QHostInfo::swapFunction() +{ + QHostInfo obj1, obj2; + obj1.setError(QHostInfo::HostInfoError(0)); + obj2.setError(QHostInfo::HostInfoError(1)); + obj1.swap(obj2); + QCOMPARE(QHostInfo::HostInfoError(0), obj2.error()); + QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); +} + +void tst_QHostInfo::moveOperator() +{ + QHostInfo obj1, obj2, obj3(1); + obj1.setError(QHostInfo::HostInfoError(0)); + obj2.setError(QHostInfo::HostInfoError(1)); + obj1 = std::move(obj2); + obj2 = obj3; + QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); + QCOMPARE(obj3.lookupId(), obj2.lookupId()); +} + + + // Testing get/set functions void tst_QHostInfo::getSetCheck() { diff --git a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp index 60ee4eb471..7760138067 100644 --- a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp +++ b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp @@ -306,9 +306,11 @@ void tst_QLocalSocket::listen() // already isListening QTest::ignoreMessage(QtWarningMsg, "QLocalServer::listen() called when already listening"); QVERIFY(!server.listen(name)); + QVERIFY(server.socketDescriptor() != -1); } else { QVERIFY(!server.errorString().isEmpty()); QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); + QCOMPARE(server.socketDescriptor(), -1); } QCOMPARE(server.maxPendingConnections(), 30); bool timedOut = false; diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 8a8522760c..90838e8831 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -1097,6 +1097,9 @@ public: QString m_interFile; QString ciphers; +signals: + void socketError(QAbstractSocket::SocketError); + protected: void incomingConnection(qintptr socketDescriptor) { @@ -1106,6 +1109,7 @@ protected: socket->setProtocol(protocol); if (ignoreSslErrors) connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(socketError(QAbstractSocket::SocketError))); QFile file(m_keyFile); QVERIFY(file.open(QIODevice::ReadOnly)); @@ -1241,6 +1245,37 @@ void tst_QSslSocket::protocolServerSide_data() #if !defined(OPENSSL_NO_SSL3) QTest::newRow("any-ssl3") << QSsl::AnyProtocol << QSsl::SslV3 << true; #endif + +#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT) + QTest::newRow("tls1.0orlater-ssl2") << QSsl::TlsV1_0OrLater << QSsl::SslV2 << false; +#endif +#if !defined(OPENSSL_NO_SSL3) + QTest::newRow("tls1.0orlater-ssl3") << QSsl::TlsV1_0OrLater << QSsl::SslV3 << false; +#endif + QTest::newRow("tls1.0orlater-tls1.0") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_0 << true; + QTest::newRow("tls1.0orlater-tls1.1") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_1 << true; + QTest::newRow("tls1.0orlater-tls1.2") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_2 << true; + +#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT) + QTest::newRow("tls1.1orlater-ssl2") << QSsl::TlsV1_1OrLater << QSsl::SslV2 << false; +#endif +#if !defined(OPENSSL_NO_SSL3) + QTest::newRow("tls1.1orlater-ssl3") << QSsl::TlsV1_1OrLater << QSsl::SslV3 << false; +#endif + QTest::newRow("tls1.1orlater-tls1.0") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_0 << false; + QTest::newRow("tls1.1orlater-tls1.1") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_1 << true; + QTest::newRow("tls1.1orlater-tls1.2") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_2 << true; + +#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT) + QTest::newRow("tls1.2orlater-ssl2") << QSsl::TlsV1_2OrLater << QSsl::SslV2 << false; +#endif +#if !defined(OPENSSL_NO_SSL3) + QTest::newRow("tls1.2orlater-ssl3") << QSsl::TlsV1_2OrLater << QSsl::SslV3 << false; +#endif + QTest::newRow("tls1.2orlater-tls1.0") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_0 << false; + QTest::newRow("tls1.2orlater-tls1.1") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_1 << false; + QTest::newRow("tls1.2orlater-tls1.2") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_2 << true; + QTest::newRow("any-tls1.0") << QSsl::AnyProtocol << QSsl::TlsV1_0 << true; QTest::newRow("any-tls1ssl3") << QSsl::AnyProtocol << QSsl::TlsV1SslV3 << true; QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true; @@ -1263,6 +1298,7 @@ void tst_QSslSocket::protocolServerSide() QVERIFY(server.listen()); QEventLoop loop; + connect(&server, SIGNAL(socketError(QAbstractSocket::SocketError)), &loop, SLOT(quit())); QTimer::singleShot(5000, &loop, SLOT(quit())); QSslSocket client; @@ -1280,7 +1316,15 @@ void tst_QSslSocket::protocolServerSide() QFETCH(bool, works); QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState; - QCOMPARE(int(client.state()), int(expectedState)); + // Determine whether the client or the server caused the event loop + // to quit due to a socket error, and investigate the culprit. + if (server.socket->error() != QAbstractSocket::UnknownSocketError) { + QVERIFY(client.error() == QAbstractSocket::UnknownSocketError); + QCOMPARE(int(server.socket->state()), int(expectedState)); + } else if (client.error() != QAbstractSocket::UnknownSocketError) { + QVERIFY(server.socket->error() == QAbstractSocket::UnknownSocketError); + QCOMPARE(int(client.state()), int(expectedState)); + } QCOMPARE(client.isEncrypted(), works); } @@ -2118,6 +2162,13 @@ void tst_QSslSocket::waitForMinusOne() // fifth verification: it should wait for 200 ms more QVERIFY(socket.waitForDisconnected(-1)); + + // sixth verification: reading from a disconnected socket returns -1 + // once we deplete the read buffer + QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); + socket.readAll(); + char aux; + QCOMPARE(socket.read(&aux, 1), -1); } class VerifyServer : public QTcpServer diff --git a/tests/auto/other/lancelot/scripts/gradientxform_device.qps b/tests/auto/other/lancelot/scripts/gradientxform_device.qps new file mode 100644 index 0000000000..82ebb536bb --- /dev/null +++ b/tests/auto/other/lancelot/scripts/gradientxform_device.qps @@ -0,0 +1,67 @@ +# Version: 1 +# CheckVsReference: 5% + +gradient_clearStops +gradient_appendStop 0 black +gradient_appendStop 0.4 yellow +gradient_appendStop 1 gray + +gradient_setSpread PadSpread + +gradient_setCoordinateMode StretchToDeviceMode + +# first run is dummy, make it offscreen +save +translate -500 -500 + +begin_block row +save + +setPen nopen +drawRect 50 0 100 100 + +setPen brush 30 +setBrush lightblue +drawRect 175 15 70 70 + +setFont "times" 110 99 +drawText 270 100 "X" + +restore +end_block row + +restore + +drawText 160 20 "PLAIN" +drawText 560 20 "BRUSH XFORM" +translate 0 20 + +begin_block block +save + +drawText 75 20 "Brush Fill" +drawText 176 20 "Pen Stroke" +drawText 277 20 "Text Stroke" +translate 0 30 +drawText 0 50 "Linear" +drawText 0 160 "Radial" +drawText 0 270 "Conical" + +gradient_setLinear 0.0 0.0 0.4 0.0 +repeat_block row + +translate 0 110 +gradient_setRadial 0.04 0.08 0.3 0.3 0.05 +repeat_block row + +translate 0 110 +gradient_setConical 0.25 0.1 45 +repeat_block row +restore +end_block block + +translate 400 0 +brushRotate 30.0 +brushScale 1.5 .5 +brushTranslate 0 -80 +repeat_block block diff --git a/tests/auto/other/lancelot/scripts/gradientxform_logical.qps b/tests/auto/other/lancelot/scripts/gradientxform_logical.qps new file mode 100644 index 0000000000..1bb157b635 --- /dev/null +++ b/tests/auto/other/lancelot/scripts/gradientxform_logical.qps @@ -0,0 +1,67 @@ +# Version: 1 +# CheckVsReference: 5% + +gradient_clearStops +gradient_appendStop 0 black +gradient_appendStop 0.4 yellow +gradient_appendStop 1 gray + +gradient_setSpread PadSpread + +gradient_setCoordinateMode LogicalMode + +# first run is dummy, make it offscreen +save +translate -500 -500 + +begin_block row +save + +setPen nopen +drawRect 50 0 100 100 + +setPen brush 30 +setBrush lightblue +drawRect 175 15 70 70 + +setFont "times" 110 99 +drawText 270 100 "X" + +restore +end_block row + +restore + +drawText 160 20 "PLAIN" +drawText 560 20 "BRUSH XFORM" +translate 0 20 + +begin_block block +save + +drawText 75 20 "Brush Fill" +drawText 176 20 "Pen Stroke" +drawText 277 20 "Text Stroke" +translate 0 30 +drawText 0 50 "Linear" +drawText 0 160 "Radial" +drawText 0 270 "Conical" + +gradient_setLinear 0 0 400 0 +repeat_block row + +translate 0 110 +gradient_setRadial 200 50 140 70 20 +repeat_block row + +translate 0 110 +gradient_setConical 220 60 45 +repeat_block row +restore +end_block block + +translate 400 0 +brushRotate 30.0 +brushScale 1.5 .5 +brushTranslate 0 -80 +repeat_block block diff --git a/tests/auto/other/lancelot/scripts/gradientxform_object.qps b/tests/auto/other/lancelot/scripts/gradientxform_object.qps new file mode 100644 index 0000000000..d785a008c0 --- /dev/null +++ b/tests/auto/other/lancelot/scripts/gradientxform_object.qps @@ -0,0 +1,68 @@ +# Version: 1 +# CheckVsReference: 5% + +gradient_clearStops +gradient_appendStop 0 black +gradient_appendStop 0.4 yellow +gradient_appendStop 1 gray + +gradient_setSpread PadSpread + +gradient_setCoordinateMode ObjectBoundingMode + +# first run is dummy, make it offscreen +save +translate -500 -500 + +begin_block row +save + +setPen nopen +drawRect 50 0 100 100 + +setPen brush 30 +setBrush lightblue +translate 110 0 +drawRect 65 15 70 70 + +translate 110 0 +setFont "times" 110 99 +drawText 50 100 "X" + +restore +end_block row + +restore + +drawText 160 20 "PLAIN" +drawText 560 20 "BRUSH XFORM" +translate 0 20 + +begin_block block +save + +drawText 75 20 "Brush Fill" +drawText 176 20 "Pen Stroke" +drawText 277 20 "Text Stroke" +translate 0 30 +drawText 0 50 "Linear" +drawText 0 160 "Radial" +drawText 0 270 "Conical" + +gradient_setLinear 0.1 0.0 0.5 0.0 +repeat_block row + +translate 0 110 +gradient_setRadial 0.3 0.2 0.5 0.4 0.5 +repeat_block row + +translate 0 110 +gradient_setConical 0.5 0.7 45 +repeat_block row +restore +end_block block + +translate 400 0 +brushRotate 30.0 +brushScale 1.5 .5 +repeat_block block diff --git a/tests/auto/other/macnativeevents/macnativeevents.pro b/tests/auto/other/macnativeevents/macnativeevents.pro index 48ad04bbff..0611377d0b 100644 --- a/tests/auto/other/macnativeevents/macnativeevents.pro +++ b/tests/auto/other/macnativeevents/macnativeevents.pro @@ -1,9 +1,8 @@ CONFIG += testcase TARGET = tst_macnativeevents -LIBS += -framework Carbon QT += widgets testlib HEADERS += qnativeevents.h nativeeventlist.h expectedeventlist.h -SOURCES += qnativeevents.cpp qnativeevents_mac.cpp +SOURCES += qnativeevents.cpp qnativeevents_mac.cpp SOURCES += expectedeventlist.cpp nativeeventlist.cpp SOURCES += tst_macnativeevents.cpp diff --git a/tests/auto/other/macnativeevents/qnativeevents.cpp b/tests/auto/other/macnativeevents/qnativeevents.cpp index 758c0a94b8..f04b33151a 100644 --- a/tests/auto/other/macnativeevents/qnativeevents.cpp +++ b/tests/auto/other/macnativeevents/qnativeevents.cpp @@ -72,7 +72,7 @@ void QNativeInput::nativeEvent(QNativeEvent *event) } } -Qt::Native::Status QNativeInput::sendNativeEvent(const QNativeEvent &event, int pid) +Qt::Native::Status QNativeInput::sendNativeEvent(const QNativeEvent &event) { switch (event.id()){ case QNativeMouseMoveEvent::eventId: @@ -84,7 +84,7 @@ Qt::Native::Status QNativeInput::sendNativeEvent(const QNativeEvent &event, int case QNativeMouseWheelEvent::eventId: return sendNativeMouseWheelEvent(static_cast<const QNativeMouseWheelEvent &>(event)); case QNativeKeyEvent::eventId: - return sendNativeKeyEvent(static_cast<const QNativeKeyEvent &>(event), pid); + return sendNativeKeyEvent(static_cast<const QNativeKeyEvent &>(event)); case QNativeModifierEvent::eventId: return sendNativeModifierEvent(static_cast<const QNativeModifierEvent &>(event)); case QNativeEvent::eventId: diff --git a/tests/auto/other/macnativeevents/qnativeevents.h b/tests/auto/other/macnativeevents/qnativeevents.h index 605d6d196e..2e30d849f2 100644 --- a/tests/auto/other/macnativeevents/qnativeevents.h +++ b/tests/auto/other/macnativeevents/qnativeevents.h @@ -204,10 +204,10 @@ class QNativeInput static Qt::Native::Status sendNativeMouseMoveEvent(const QNativeMouseMoveEvent &event); static Qt::Native::Status sendNativeMouseDragEvent(const QNativeMouseDragEvent &event); static Qt::Native::Status sendNativeMouseWheelEvent(const QNativeMouseWheelEvent &event); - static Qt::Native::Status sendNativeKeyEvent(const QNativeKeyEvent &event, int pid = 0); + static Qt::Native::Status sendNativeKeyEvent(const QNativeKeyEvent &event); static Qt::Native::Status sendNativeModifierEvent(const QNativeModifierEvent &event); // sendNativeEvent will NOT differ from OS to OS. - static Qt::Native::Status sendNativeEvent(const QNativeEvent &event, int pid = 0); + static Qt::Native::Status sendNativeEvent(const QNativeEvent &event); // The following methods will differ in implementation from OS to OS: Qt::Native::Status subscribeForNativeEvents(); diff --git a/tests/auto/other/macnativeevents/qnativeevents_mac.cpp b/tests/auto/other/macnativeevents/qnativeevents_mac.cpp index 6671813188..6d7fbbecc1 100644 --- a/tests/auto/other/macnativeevents/qnativeevents_mac.cpp +++ b/tests/auto/other/macnativeevents/qnativeevents_mac.cpp @@ -27,7 +27,7 @@ ****************************************************************************/ #include "qnativeevents.h" -#include <Carbon/Carbon.h> +#include <CoreGraphics/CoreGraphics.h> #include <QtCore> // ************************************************************ @@ -176,28 +176,18 @@ static CGEventRef EventHandler_Quartz(CGEventTapProxy proxy, CGEventType type, C return inEvent; } -Qt::Native::Status insertEventHandler_Quartz(QNativeInput *nativeInput, int pid = 0) +Qt::Native::Status insertEventHandler_Quartz(QNativeInput *nativeInput) { uid_t uid = geteuid(); if (uid != 0) qWarning("MacNativeEvents: You must be root to listen for key events!"); - CFMachPortRef port; - if (!pid){ - port = CGEventTapCreate(kCGHIDEventTap, - kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, - kCGEventMaskForAllEvents, EventHandler_Quartz, nativeInput); - } else { - ProcessSerialNumber psn; - GetProcessForPID(pid, &psn); - port = CGEventTapCreateForPSN(&psn, - kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, - kCGEventMaskForAllEvents, EventHandler_Quartz, nativeInput); - } + CFMachPortRef port = CGEventTapCreate(kCGHIDEventTap, + kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, + kCGEventMaskForAllEvents, EventHandler_Quartz, nativeInput); CFRunLoopSourceRef eventSrc = CFMachPortCreateRunLoopSource(NULL, port, 0); - CFRunLoopAddSource((CFRunLoopRef) GetCFRunLoopFromEventLoop(GetMainEventLoop()), - eventSrc, kCFRunLoopCommonModes); + CFRunLoopAddSource(CFRunLoopGetMain(), eventSrc, kCFRunLoopCommonModes); return Qt::Native::Success; } @@ -207,19 +197,6 @@ Qt::Native::Status removeEventHandler_Quartz() return Qt::Native::Success; // ToDo: } -Qt::Native::Status sendNativeKeyEventToProcess_Quartz(const QNativeKeyEvent &event, int pid) -{ - ProcessSerialNumber psn; - GetProcessForPID(pid, &psn); - - CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, event.press); - setModifiersFromQNativeEvent(e, event); - SetFrontProcess(&psn); - CGEventPostToPSN(&psn, e); - CFRelease(e); - return Qt::Native::Success; -} - Qt::Native::Status sendNativeKeyEvent_Quartz(const QNativeKeyEvent &event) { CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, event.press); @@ -344,12 +321,9 @@ Qt::Native::Status QNativeInput::sendNativeMouseWheelEvent(const QNativeMouseWhe return sendNativeMouseWheelEvent_Quartz(event); } -Qt::Native::Status QNativeInput::sendNativeKeyEvent(const QNativeKeyEvent &event, int pid) +Qt::Native::Status QNativeInput::sendNativeKeyEvent(const QNativeKeyEvent &event) { - if (!pid) - return sendNativeKeyEvent_Quartz(event); - else - return sendNativeKeyEventToProcess_Quartz(event, pid); + return sendNativeKeyEvent_Quartz(event); } Qt::Native::Status QNativeInput::sendNativeModifierEvent(const QNativeModifierEvent &event) diff --git a/tests/auto/other/macnativeevents/tst_macnativeevents.cpp b/tests/auto/other/macnativeevents/tst_macnativeevents.cpp index 5edff7aabe..e8970e6f24 100644 --- a/tests/auto/other/macnativeevents/tst_macnativeevents.cpp +++ b/tests/auto/other/macnativeevents/tst_macnativeevents.cpp @@ -35,10 +35,14 @@ #include "qnativeevents.h" #include "nativeeventlist.h" #include "expectedeventlist.h" -#include <Carbon/Carbon.h> QT_USE_NAMESPACE +// Unicode code points for the glyphs associated with these keys +// Defined by Carbon headers but not anywhere in Cocoa +static const int kControlUnicode = 0x2303; +static const int kCommandUnicode = 0x2318; + class tst_MacNativeEvents : public QObject { Q_OBJECT diff --git a/tests/auto/other/modeltest/dynamictreemodel.cpp b/tests/auto/other/modeltest/dynamictreemodel.cpp index 1f6463db8a..fc979bce2d 100644 --- a/tests/auto/other/modeltest/dynamictreemodel.cpp +++ b/tests/auto/other/modeltest/dynamictreemodel.cpp @@ -33,9 +33,8 @@ #include <QtCore/QTimer> #include <QtCore/QDebug> - -DynamicTreeModel::DynamicTreeModel(QObject *parent) - : QAbstractItemModel(parent), +DynamicTreeModel::DynamicTreeModel(QObject *parent) : + QAbstractItemModel(parent), nextId(1) { } @@ -45,186 +44,172 @@ QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &pare // if (column != 0) // return QModelIndex(); + if (column < 0 || row < 0) + return QModelIndex(); - if ( column < 0 || row < 0 ) - return QModelIndex(); - - QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId()); + QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId()); - const qint64 grandParent = findParentId(parent.internalId()); - if (grandParent >= 0) { - QList<QList<qint64> > parentTable = m_childItems.value(grandParent); - if (parent.column() >= parentTable.size()) - qFatal("%s: parent.column() must be less than parentTable.size()", Q_FUNC_INFO); - QList<qint64> parentSiblings = parentTable.at(parent.column()); - if (parent.row() >= parentSiblings.size()) - qFatal("%s: parent.row() must be less than parentSiblings.size()", Q_FUNC_INFO); - } - - if (childIdColumns.size() == 0) - return QModelIndex(); + const qint64 grandParent = findParentId(parent.internalId()); + if (grandParent >= 0) { + QList<QList<qint64> > parentTable = m_childItems.value(grandParent); + if (parent.column() >= parentTable.size()) + qFatal("%s: parent.column() must be less than parentTable.size()", Q_FUNC_INFO); + QList<qint64> parentSiblings = parentTable.at(parent.column()); + if (parent.row() >= parentSiblings.size()) + qFatal("%s: parent.row() must be less than parentSiblings.size()", Q_FUNC_INFO); + } - if (column >= childIdColumns.size()) - return QModelIndex(); + if (childIdColumns.size() == 0) + return QModelIndex(); - QList<qint64> rowIds = childIdColumns.at(column); + if (column >= childIdColumns.size()) + return QModelIndex(); - if ( row >= rowIds.size()) - return QModelIndex(); + QList<qint64> rowIds = childIdColumns.at(column); - qint64 id = rowIds.at(row); + if (row >= rowIds.size()) + return QModelIndex(); - return createIndex(row, column, reinterpret_cast<void *>(id)); + qint64 id = rowIds.at(row); + return createIndex(row, column, reinterpret_cast<void *>(id)); } qint64 DynamicTreeModel::findParentId(qint64 searchId) const { - if (searchId <= 0) - return -1; - - QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems); - while (i.hasNext()) - { - i.next(); - QListIterator<QList<qint64> > j(i.value()); - while (j.hasNext()) - { - QList<qint64> l = j.next(); - if (l.contains(searchId)) - { - return i.key(); - } + if (searchId <= 0) + return -1; + + QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems); + while (i.hasNext()) { + i.next(); + QListIterator<QList<qint64> > j(i.value()); + while (j.hasNext()) { + QList<qint64> l = j.next(); + if (l.contains(searchId)) + return i.key(); + } } - } - return -1; + return -1; } QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const { - if (!index.isValid()) - return QModelIndex(); + if (!index.isValid()) + return QModelIndex(); - qint64 searchId = index.internalId(); - qint64 parentId = findParentId(searchId); - // Will never happen for valid index, but what the hey... - if (parentId <= 0) - return QModelIndex(); + qint64 searchId = index.internalId(); + qint64 parentId = findParentId(searchId); + // Will never happen for valid index, but what the hey... + if (parentId <= 0) + return QModelIndex(); - qint64 grandParentId = findParentId(parentId); - if (grandParentId < 0) - grandParentId = 0; + qint64 grandParentId = findParentId(parentId); + if (grandParentId < 0) + grandParentId = 0; - int column = 0; - QList<qint64> childList = m_childItems.value(grandParentId).at(column); + int column = 0; + QList<qint64> childList = m_childItems.value(grandParentId).at(column); - int row = childList.indexOf(parentId); - - return createIndex(row, column, reinterpret_cast<void *>(parentId)); + int row = childList.indexOf(parentId); + return createIndex(row, column, reinterpret_cast<void *>(parentId)); } -int DynamicTreeModel::rowCount(const QModelIndex &index ) const +int DynamicTreeModel::rowCount(const QModelIndex &index) const { - QList<QList<qint64> > cols = m_childItems.value(index.internalId()); + QList<QList<qint64> > cols = m_childItems.value(index.internalId()); - if (cols.size() == 0 ) - return 0; + if (cols.size() == 0) + return 0; - if (index.column() > 0) - return 0; + if (index.column() > 0) + return 0; - return cols.at(0).size(); + return cols.at(0).size(); } -int DynamicTreeModel::columnCount(const QModelIndex &index ) const +int DynamicTreeModel::columnCount(const QModelIndex &index) const { // Q_UNUSED(index); - return m_childItems.value(index.internalId()).size(); + return m_childItems.value(index.internalId()).size(); } QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) - return QVariant(); + if (!index.isValid()) + return QVariant(); - if (Qt::DisplayRole == role) - { - return m_items.value(index.internalId()); - } - return QVariant(); + if (Qt::DisplayRole == role) + return m_items.value(index.internalId()); + return QVariant(); } void DynamicTreeModel::clear() { - beginResetModel(); - m_items.clear(); - m_childItems.clear(); - nextId = 1; - endResetModel(); + beginResetModel(); + m_items.clear(); + m_childItems.clear(); + nextId = 1; + endResetModel(); } - -ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent ) - : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) +ModelChangeCommand::ModelChangeCommand(DynamicTreeModel *model, QObject *parent) : + QObject(parent), + m_model(model), + m_numCols(1), + m_startRow(-1), + m_endRow(-1) { - } QModelIndex ModelChangeCommand::findIndex(QList<int> rows) { - const int col = 0; - QModelIndex parent = QModelIndex(); - QListIterator<int> i(rows); - while (i.hasNext()) - { - parent = m_model->index(i.next(), col, parent); - if (!parent.isValid()) - qFatal("%s: parent must be valid", Q_FUNC_INFO); - } - return parent; + const int col = 0; + QModelIndex parent = QModelIndex(); + QListIterator<int> i(rows); + while (i.hasNext()) { + parent = m_model->index(i.next(), col, parent); + if (!parent.isValid()) + qFatal("%s: parent must be valid", Q_FUNC_INFO); + } + return parent; } -ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent ) - : ModelChangeCommand(model, parent) +ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent) : + ModelChangeCommand(model, parent) { - } void ModelInsertCommand::doCommand() { - QModelIndex parent = findIndex(m_rowNumbers); - m_model->beginInsertRows(parent, m_startRow, m_endRow); - qint64 parentId = parent.internalId(); - for (int row = m_startRow; row <= m_endRow; row++) - { - for(int col = 0; col < m_numCols; col++ ) - { - if (m_model->m_childItems[parentId].size() <= col) - { - m_model->m_childItems[parentId].append(QList<qint64>()); - } + QModelIndex parent = findIndex(m_rowNumbers); + m_model->beginInsertRows(parent, m_startRow, m_endRow); + qint64 parentId = parent.internalId(); + for (int row = m_startRow; row <= m_endRow; row++) { + for (int col = 0; col < m_numCols; col++) { + if (m_model->m_childItems[parentId].size() <= col) + m_model->m_childItems[parentId].append(QList<qint64>()); // QString name = QUuid::createUuid().toString(); - qint64 id = m_model->newId(); - QString name = QString::number(id); - - m_model->m_items.insert(id, name); - m_model->m_childItems[parentId][col].insert(row, id); + qint64 id = m_model->newId(); + QString name = QString::number(id); + m_model->m_items.insert(id, name); + m_model->m_childItems[parentId][col].insert(row, id); + } } - } - m_model->endInsertRows(); + m_model->endInsertRows(); } - -ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) - : ModelChangeCommand(model, parent) +ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) : + ModelChangeCommand(model, parent) { - } -bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) + +bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, + const QModelIndex &destParent, int destRow) { - return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); + return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); } void ModelMoveCommand::doCommand() @@ -233,33 +218,26 @@ void ModelMoveCommand::doCommand() QModelIndex destParent = findIndex(m_destRowNumbers); if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) - { return; - } - for (int column = 0; column < m_numCols; ++column) - { - QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 ); + for (int column = 0; column < m_numCols; ++column) { + QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid( + m_startRow, m_endRow - m_startRow + 1); - for (int i = m_startRow; i <= m_endRow ; i++) - { + for (int i = m_startRow; i <= m_endRow; i++) m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); - } int d; - if (m_destRow < m_startRow) + if (m_destRow < m_startRow) { d = m_destRow; - else - { + } else { if (srcParent == destParent) d = m_destRow - (m_endRow - m_startRow + 1); else d = m_destRow - (m_endRow - m_startRow) + 1; } - foreach(const qint64 id, l) - { + foreach (const qint64 id, l) m_model->m_childItems[destParent.internalId()][column].insert(d++, id); - } } emitPostSignal(); @@ -270,18 +248,17 @@ void ModelMoveCommand::emitPostSignal() m_model->endMoveRows(); } -ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent) - : ModelMoveCommand(model, parent) +ModelResetCommand::ModelResetCommand(DynamicTreeModel *model, QObject *parent) : + ModelMoveCommand(model, parent) { - } ModelResetCommand::~ModelResetCommand() { - } -bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, + const QModelIndex &destParent, int destRow) { Q_UNUSED(srcParent); Q_UNUSED(srcStart); @@ -298,18 +275,17 @@ void ModelResetCommand::emitPostSignal() m_model->endResetModel(); } -ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent) - : ModelMoveCommand(model, parent) +ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel *model, QObject *parent) : + ModelMoveCommand(model, parent) { - } ModelResetCommandFixed::~ModelResetCommandFixed() { - } -bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, + const QModelIndex &destParent, int destRow) { Q_UNUSED(srcParent); Q_UNUSED(srcStart); @@ -326,10 +302,10 @@ void ModelResetCommandFixed::emitPostSignal() m_model->endResetModel(); } -ModelChangeChildrenLayoutsCommand::ModelChangeChildrenLayoutsCommand(DynamicTreeModel* model, QObject* parent) - : ModelChangeCommand(model, parent) +ModelChangeChildrenLayoutsCommand::ModelChangeChildrenLayoutsCommand(DynamicTreeModel *model, + QObject *parent) : + ModelChangeCommand(model, parent) { - } void ModelChangeChildrenLayoutsCommand::doCommand() @@ -346,17 +322,16 @@ void ModelChangeChildrenLayoutsCommand::doCommand() int rowSize1 = -1; int rowSize2 = -1; - for (int column = 0; column < m_numCols; ++column) - { + for (int column = 0; column < m_numCols; ++column) { { - QList<qint64> &l = m_model->m_childItems[parent1.internalId()][column]; - rowSize1 = l.size(); - l.prepend(l.takeLast()); + QList<qint64> &l = m_model->m_childItems[parent1.internalId()][column]; + rowSize1 = l.size(); + l.prepend(l.takeLast()); } { - QList<qint64> &l = m_model->m_childItems[parent2.internalId()][column]; - rowSize2 = l.size(); - l.append(l.takeFirst()); + QList<qint64> &l = m_model->m_childItems[parent2.internalId()][column]; + rowSize2 = l.size(); + l.append(l.takeFirst()); } } @@ -373,15 +348,23 @@ void ModelChangeChildrenLayoutsCommand::doCommand() foreach (const QModelIndex &idx, persistent) { if (idx.parent() == parent1) { if (idx.row() == rowSize1 - 1) { - m_model->changePersistentIndex(idx, m_model->createIndex(0, idx.column(), idx.internalPointer())); + m_model->changePersistentIndex(idx, + m_model->createIndex(0, idx.column(), + idx.internalPointer())); } else { - m_model->changePersistentIndex(idx, m_model->createIndex(idx.row() + 1, idx.column(), idx.internalPointer())); + m_model->changePersistentIndex(idx, + m_model->createIndex(idx.row() + 1, idx.column(), + idx.internalPointer())); } } else if (idx.parent() == parent2) { if (idx.row() == 0) { - m_model->changePersistentIndex(idx, m_model->createIndex(rowSize2 - 1, idx.column(), idx.internalPointer())); + m_model->changePersistentIndex(idx, + m_model->createIndex(rowSize2 - 1, idx.column(), + idx.internalPointer())); } else { - m_model->changePersistentIndex(idx, m_model->createIndex(idx.row() - 1, idx.column(), idx.internalPointer())); + m_model->changePersistentIndex(idx, + m_model->createIndex(idx.row() - 1, idx.column(), + idx.internalPointer())); } } } diff --git a/tests/auto/other/modeltest/dynamictreemodel.h b/tests/auto/other/modeltest/dynamictreemodel.h index e31c4569fd..709751dd27 100644 --- a/tests/auto/other/modeltest/dynamictreemodel.h +++ b/tests/auto/other/modeltest/dynamictreemodel.h @@ -34,119 +34,142 @@ #include <QtCore/QHash> #include <QtCore/QList> - class DynamicTreeModel : public QAbstractItemModel { - Q_OBJECT + Q_OBJECT public: - DynamicTreeModel(QObject *parent = 0); + DynamicTreeModel(QObject *parent = 0); - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index) const; - int rowCount(const QModelIndex &index = QModelIndex()) const; - int columnCount(const QModelIndex &index = QModelIndex()) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &index = QModelIndex()) const; + int columnCount(const QModelIndex &index = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - void clear(); + void clear(); protected slots: - /** - Finds the parent id of the string with id @p searchId. + /** + Finds the parent id of the string with id @p searchId. - Returns -1 if not found. - */ - qint64 findParentId(qint64 searchId) const; + Returns -1 if not found. + */ + qint64 findParentId(qint64 searchId) const; private: - QHash<qint64, QString> m_items; - QHash<qint64, QList<QList<qint64> > > m_childItems; - qint64 nextId; - qint64 newId() { return nextId++; }; - - QModelIndex m_nextParentIndex; - int m_nextRow; - - int m_depth; - int maxDepth; - - friend class ModelInsertCommand; - friend class ModelMoveCommand; - friend class ModelResetCommand; - friend class ModelResetCommandFixed; - friend class ModelChangeChildrenLayoutsCommand; - + QHash<qint64, QString> m_items; + QHash<qint64, QList<QList<qint64> > > m_childItems; + qint64 nextId; + qint64 newId() + { + return nextId++; + } + + QModelIndex m_nextParentIndex; + int m_nextRow; + + int m_depth; + int maxDepth; + + friend class ModelInsertCommand; + friend class ModelMoveCommand; + friend class ModelResetCommand; + friend class ModelResetCommandFixed; + friend class ModelChangeChildrenLayoutsCommand; }; - class ModelChangeCommand : public QObject { - Q_OBJECT + Q_OBJECT public: - ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 ); + ModelChangeCommand(DynamicTreeModel *model, QObject *parent = 0); - virtual ~ModelChangeCommand() {} + virtual ~ModelChangeCommand() + { + } - void setAncestorRowNumbers(QList<int> rowNumbers) { m_rowNumbers = rowNumbers; } + void setAncestorRowNumbers(QList<int> rowNumbers) + { + m_rowNumbers = rowNumbers; + } - QModelIndex findIndex(QList<int> rows); + QModelIndex findIndex(QList<int> rows); - void setStartRow(int row) { m_startRow = row; } + void setStartRow(int row) + { + m_startRow = row; + } - void setEndRow(int row) { m_endRow = row; } + void setEndRow(int row) + { + m_endRow = row; + } - void setNumCols(int cols) { m_numCols = cols; } + void setNumCols(int cols) + { + m_numCols = cols; + } - virtual void doCommand() = 0; + virtual void doCommand() = 0; protected: - DynamicTreeModel* m_model; - QList<int> m_rowNumbers; - int m_numCols; - int m_startRow; - int m_endRow; - + DynamicTreeModel *m_model; + QList<int> m_rowNumbers; + int m_numCols; + int m_startRow; + int m_endRow; }; -typedef QList<ModelChangeCommand*> ModelChangeCommandList; +typedef QList<ModelChangeCommand *> ModelChangeCommandList; class ModelInsertCommand : public ModelChangeCommand { - Q_OBJECT + Q_OBJECT public: - ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 ); - virtual ~ModelInsertCommand() {} + ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0); + virtual ~ModelInsertCommand() + { + } - virtual void doCommand(); + virtual void doCommand(); }; - class ModelMoveCommand : public ModelChangeCommand { - Q_OBJECT + Q_OBJECT public: - ModelMoveCommand(DynamicTreeModel *model, QObject *parent); + ModelMoveCommand(DynamicTreeModel *model, QObject *parent); - virtual ~ModelMoveCommand() {} + virtual ~ModelMoveCommand() + { + } - virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, + const QModelIndex &destParent, int destRow); - virtual void doCommand(); + virtual void doCommand(); - virtual void emitPostSignal(); + virtual void emitPostSignal(); - void setDestAncestors( QList<int> rows ) { m_destRowNumbers = rows; } + void setDestAncestors(QList<int> rows) + { + m_destRowNumbers = rows; + } - void setDestRow(int row) { m_destRow = row; } + void setDestRow(int row) + { + m_destRow = row; + } protected: - QList<int> m_destRowNumbers; - int m_destRow; + QList<int> m_destRowNumbers; + int m_destRow; }; /** @@ -154,15 +177,15 @@ protected: */ class ModelResetCommand : public ModelMoveCommand { - Q_OBJECT + Q_OBJECT public: - ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0); + ModelResetCommand(DynamicTreeModel *model, QObject *parent = 0); - virtual ~ModelResetCommand(); - - virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); - virtual void emitPostSignal(); + virtual ~ModelResetCommand(); + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, + const QModelIndex &destParent, int destRow); + virtual void emitPostSignal(); }; /** @@ -170,32 +193,37 @@ public: */ class ModelResetCommandFixed : public ModelMoveCommand { - Q_OBJECT + Q_OBJECT public: - ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0); - - virtual ~ModelResetCommandFixed(); + ModelResetCommandFixed(DynamicTreeModel *model, QObject *parent = 0); - virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); - virtual void emitPostSignal(); + virtual ~ModelResetCommandFixed(); + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, + const QModelIndex &destParent, int destRow); + virtual void emitPostSignal(); }; class ModelChangeChildrenLayoutsCommand : public ModelChangeCommand { - Q_OBJECT + Q_OBJECT public: - ModelChangeChildrenLayoutsCommand(DynamicTreeModel *model, QObject *parent); + ModelChangeChildrenLayoutsCommand(DynamicTreeModel *model, QObject *parent); - virtual ~ModelChangeChildrenLayoutsCommand() {} + virtual ~ModelChangeChildrenLayoutsCommand() + { + } - virtual void doCommand(); + virtual void doCommand(); - void setSecondAncestorRowNumbers( QList<int> rows ) { m_secondRowNumbers = rows; } + void setSecondAncestorRowNumbers(QList<int> rows) + { + m_secondRowNumbers = rows; + } protected: - QList<int> m_secondRowNumbers; - int m_destRow; + QList<int> m_secondRowNumbers; + int m_destRow; }; #endif diff --git a/tests/auto/other/modeltest/modeltest.cpp b/tests/auto/other/modeltest/modeltest.cpp index 4da00bda4d..611f9e904b 100644 --- a/tests/auto/other/modeltest/modeltest.cpp +++ b/tests/auto/other/modeltest/modeltest.cpp @@ -34,60 +34,62 @@ /*! Connect to all of the models signals. Whenever anything happens recheck everything. */ -ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), + model(_model), + fetchingMore(false) { if (!model) qFatal("%s: model must not be null", Q_FUNC_INFO); connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) ); - connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) ); - connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests())); + connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests())); + connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); + this, SLOT(runAllTests())); // Special checks for changes connect(model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(layoutAboutToBeChanged()) ); + this, SLOT(layoutAboutToBeChanged())); connect(model, SIGNAL(layoutChanged()), - this, SLOT(layoutChanged()) ); + this, SLOT(layoutChanged())); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) ); + this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); + this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(rowsInserted(QModelIndex,int,int)) ); + this, SLOT(rowsInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int)) ); + this, SLOT(rowsRemoved(QModelIndex,int,int))); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); + this, SLOT(dataChanged(QModelIndex,QModelIndex))); connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(headerDataChanged(Qt::Orientation,int,int)) ); + this, SLOT(headerDataChanged(Qt::Orientation,int,int))); runAllTests(); } void ModelTest::runAllTests() { - if ( fetchingMore ) + if (fetchingMore) return; nonDestructiveBasicTest(); rowCount(); @@ -105,31 +107,31 @@ void ModelTest::runAllTests() void ModelTest::nonDestructiveBasicTest() { QVERIFY(!model->buddy(QModelIndex()).isValid()); - model->canFetchMore ( QModelIndex() ); - QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); + model->canFetchMore(QModelIndex()); + QVERIFY(model->columnCount(QModelIndex()) >= 0); QCOMPARE(model->data(QModelIndex()), QVariant()); fetchingMore = true; - model->fetchMore ( QModelIndex() ); + model->fetchMore(QModelIndex()); fetchingMore = false; - Qt::ItemFlags flags = model->flags ( QModelIndex() ); - QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); - model->hasChildren ( QModelIndex() ); - model->hasIndex ( 0, 0 ); - model->headerData ( 0, Qt::Horizontal ); - model->index ( 0, 0 ); - model->itemData ( QModelIndex() ); + Qt::ItemFlags flags = model->flags(QModelIndex()); + QVERIFY(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + model->itemData(QModelIndex()); QVariant cache; - model->match ( QModelIndex(), -1, cache ); + model->match(QModelIndex(), -1, cache); model->mimeTypes(); QVERIFY(!model->parent(QModelIndex()).isValid()); - QVERIFY( model->rowCount() >= 0 ); + QVERIFY(model->rowCount() >= 0); QVariant variant; - model->setData ( QModelIndex(), variant, -1 ); - model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); - model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); QMap<int, QVariant> roles; - model->sibling ( 0, 0, QModelIndex() ); - model->span ( QModelIndex() ); + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); model->supportedDropActions(); } @@ -142,19 +144,19 @@ void ModelTest::rowCount() { // qDebug() << "rc"; // check top row - QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); - int rows = model->rowCount ( topIndex ); - QVERIFY( rows >= 0 ); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( topIndex ) ); - - QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); - if ( secondLevelIndex.isValid() ) { // not the top level + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + QVERIFY(rows >= 0); + if (rows > 0) + QVERIFY(model->hasChildren(topIndex)); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level // check a row count where parent is valid - rows = model->rowCount ( secondLevelIndex ); - QVERIFY( rows >= 0 ); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( secondLevelIndex ) ); + rows = model->rowCount(secondLevelIndex); + QVERIFY(rows >= 0); + if (rows > 0) + QVERIFY(model->hasChildren(secondLevelIndex)); } // The models rowCount() is tested more extensively in checkChildren(), @@ -167,13 +169,13 @@ void ModelTest::rowCount() void ModelTest::columnCount() { // check top row - QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); - QVERIFY( model->columnCount ( topIndex ) >= 0 ); + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + QVERIFY(model->columnCount(topIndex) >= 0); // check a column count where parent is valid - QModelIndex childIndex = model->index ( 0, 0, topIndex ); - if ( childIndex.isValid() ) - QVERIFY( model->columnCount ( childIndex ) >= 0 ); + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + QVERIFY(model->columnCount(childIndex) >= 0); // columnCount() is tested more extensively in checkChildren(), // but this catches the big mistakes @@ -186,19 +188,19 @@ void ModelTest::hasIndex() { // qDebug() << "hi"; // Make sure that invalid values returns an invalid index - QVERIFY( !model->hasIndex ( -2, -2 ) ); - QVERIFY( !model->hasIndex ( -2, 0 ) ); - QVERIFY( !model->hasIndex ( 0, -2 ) ); + QVERIFY(!model->hasIndex(-2, -2)); + QVERIFY(!model->hasIndex(-2, 0)); + QVERIFY(!model->hasIndex(0, -2)); int rows = model->rowCount(); int columns = model->columnCount(); // check out of bounds - QVERIFY( !model->hasIndex ( rows, columns ) ); - QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); + QVERIFY(!model->hasIndex(rows, columns)); + QVERIFY(!model->hasIndex(rows + 1, columns + 1)); - if ( rows > 0 ) - QVERIFY( model->hasIndex ( 0, 0 ) ); + if (rows > 0) + QVERIFY(model->hasIndex(0, 0)); // hasIndex() is tested more extensively in checkChildren(), // but this catches the big mistakes @@ -218,7 +220,7 @@ void ModelTest::index() int rows = model->rowCount(); int columns = model->columnCount(); - if ( rows == 0 ) + if (rows == 0) return; // Catch off by one errors @@ -226,8 +228,8 @@ void ModelTest::index() QVERIFY(model->index(0, 0).isValid()); // Make sure that the same index is *always* returned - QModelIndex a = model->index ( 0, 0 ); - QModelIndex b = model->index ( 0, 0 ); + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); QCOMPARE(a, b); // index() is tested more extensively in checkChildren(), @@ -244,7 +246,7 @@ void ModelTest::parent() // when asked for the parent of an invalid index. QVERIFY(!model->parent(QModelIndex()).isValid()); - if ( model->rowCount() == 0 ) + if (model->rowCount() == 0) return; // Column 0 | Column 1 | @@ -254,29 +256,29 @@ void ModelTest::parent() // Common error test #1, make sure that a top level index has a parent // that is a invalid QModelIndex. - QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QModelIndex topIndex = model->index(0, 0, QModelIndex()); QVERIFY(!model->parent(topIndex).isValid()); // Common error test #2, make sure that a second level index has a parent // that is the first level index. - if ( model->rowCount ( topIndex ) > 0 ) { - QModelIndex childIndex = model->index ( 0, 0, topIndex ); + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); QCOMPARE(model->parent(childIndex), topIndex); } // Common error test #3, the second column should NOT have the same children // as the first column in a row. // Usually the second column shouldn't have children. - QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); - if ( model->rowCount ( topIndex1 ) > 0 ) { - QModelIndex childIndex = model->index ( 0, 0, topIndex ); - QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); - QVERIFY( childIndex != childIndex1 ); + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + QVERIFY(childIndex != childIndex1); } // Full test, walk n levels deep through the model making sure that all // parent's children correctly specify their parent. - checkChildren ( QModelIndex() ); + checkChildren(QModelIndex()); } /*! @@ -293,73 +295,75 @@ void ModelTest::parent() found the basic bugs because it is easier to figure out the problem in those tests then this one. */ -void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) { // First just try walking back up the tree. QModelIndex p = parent; - while ( p.isValid() ) + while (p.isValid()) p = p.parent(); // For models that are dynamically populated - if ( model->canFetchMore ( parent ) ) { + if (model->canFetchMore(parent)) { fetchingMore = true; - model->fetchMore ( parent ); + model->fetchMore(parent); fetchingMore = false; } - int rows = model->rowCount ( parent ); - int columns = model->columnCount ( parent ); + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( parent ) ); + if (rows > 0) + QVERIFY(model->hasChildren(parent)); // Some further testing against rows(), columns(), and hasChildren() - QVERIFY( rows >= 0 ); - QVERIFY( columns >= 0 ); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( parent ) ); + QVERIFY(rows >= 0); + QVERIFY(columns >= 0); + if (rows > 0) + QVERIFY(model->hasChildren(parent)); //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows // << "columns:" << columns << "parent column:" << parent.column(); - const QModelIndex topLeftChild = model->index( 0, 0, parent ); + const QModelIndex topLeftChild = model->index(0, 0, parent); - QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); - for ( int r = 0; r < rows; ++r ) { - if ( model->canFetchMore ( parent ) ) { + QVERIFY(!model->hasIndex(rows + 1, 0, parent)); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { fetchingMore = true; - model->fetchMore ( parent ); + model->fetchMore(parent); fetchingMore = false; } - QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); - for ( int c = 0; c < columns; ++c ) { - QVERIFY( model->hasIndex ( r, c, parent ) ); - QModelIndex index = model->index ( r, c, parent ); + QVERIFY(!model->hasIndex(r, columns + 1, parent)); + for (int c = 0; c < columns; ++c) { + QVERIFY(model->hasIndex(r, c, parent)); + QModelIndex index = model->index(r, c, parent); // rowCount() and columnCount() said that it existed... + if (!index.isValid()) + qWarning() << "Got invalid index at row=" << r << "col=" << c << "parent=" << parent; QVERIFY(index.isValid()); // index() should always return the same index when called twice in a row - QModelIndex modifiedIndex = model->index ( r, c, parent ); + QModelIndex modifiedIndex = model->index(r, c, parent); QCOMPARE(index, modifiedIndex); // Make sure we get the same index if we request it twice in a row - QModelIndex a = model->index ( r, c, parent ); - QModelIndex b = model->index ( r, c, parent ); + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); QCOMPARE(a, b); { - const QModelIndex sibling = model->sibling( r, c, topLeftChild ); + const QModelIndex sibling = model->sibling(r, c, topLeftChild); QCOMPARE(index, sibling); } { - const QModelIndex sibling = topLeftChild.sibling( r, c ); + const QModelIndex sibling = topLeftChild.sibling(r, c); QCOMPARE(index, sibling); } // Some basic checking on the index that is returned QCOMPARE(index.model(), model); - QCOMPARE( index.row(), r ); - QCOMPARE( index.column(), c ); + QCOMPARE(index.row(), r); + QCOMPARE(index.column(), c); // While you can technically return a QVariant usually this is a sign // of a bug in data(). Disable if this really is ok in your model. // QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); @@ -377,16 +381,16 @@ void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) } // Check that we can get back our real parent. - QCOMPARE( model->parent ( index ), parent ); + QCOMPARE(model->parent(index), parent); // recursively go down the children - if ( model->hasChildren ( index ) && currentDepth < 10 ) { + if (model->hasChildren(index) && currentDepth < 10) { //qDebug() << r << c << "has children" << model->rowCount(index); - checkChildren ( index, ++currentDepth ); + checkChildren(index, ++currentDepth); }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ // make sure that after testing the children that the index doesn't change. - QModelIndex newerIndex = model->index ( r, c, parent ); + QModelIndex newerIndex = model->index(r, c, parent); QCOMPARE(index, newerIndex); } } @@ -398,68 +402,61 @@ void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) void ModelTest::data() { // Invalid index should return an invalid qvariant - QVERIFY( !model->data ( QModelIndex() ).isValid() ); + QVERIFY(!model->data(QModelIndex()).isValid()); - if ( model->rowCount() == 0 ) + if (model->rowCount() == 0) return; // A valid index should have a valid QVariant data - QVERIFY( model->index ( 0, 0 ).isValid() ); + QVERIFY(model->index(0, 0).isValid()); // shouldn't be able to set data on an invalid index - QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); + QVERIFY(!model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole)); // General Purpose roles that should return a QString - QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert<QString>() ); - } - variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert<QString>() ); - } - variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert<QString>() ); - } + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) + QVERIFY(variant.canConvert<QString>()); + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) + QVERIFY(variant.canConvert<QString>()); + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) + QVERIFY(variant.canConvert<QString>()); // General Purpose roles that should return a QSize - variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert<QSize>() ); - } + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) + QVERIFY(variant.canConvert<QSize>()); // General Purpose roles that should return a QFont - QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); - if ( fontVariant.isValid() ) { - QVERIFY( fontVariant.canConvert<QFont>() ); - } + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) + QVERIFY(fontVariant.canConvert<QFont>()); // Check that the alignment is one we know about - QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); - if ( textAlignmentVariant.isValid() ) { + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { Qt::Alignment alignment = textAlignmentVariant.value<Qt::Alignment>(); - QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); + QCOMPARE(alignment, (alignment & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask))); } // General Purpose roles that should return a QColor - QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); - if ( colorVariant.isValid() ) { - QVERIFY( colorVariant.canConvert<QColor>() ); - } + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) + QVERIFY(colorVariant.canConvert<QColor>()); - colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); - if ( colorVariant.isValid() ) { - QVERIFY( colorVariant.canConvert<QColor>() ); - } + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) + QVERIFY(colorVariant.canConvert<QColor>()); // Check that the "check state" is one we know about. - QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); - if ( checkStateVariant.isValid() ) { + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { int state = checkStateVariant.toInt(); - QVERIFY( state == Qt::Unchecked || - state == Qt::PartiallyChecked || - state == Qt::Checked ); + QVERIFY(state == Qt::Unchecked + || state == Qt::PartiallyChecked + || state == Qt::Checked); } } @@ -468,7 +465,7 @@ void ModelTest::data() \sa rowsInserted() */ -void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */) +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int /* end */) { // Q_UNUSED(end); // qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() @@ -476,10 +473,10 @@ void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, in // qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); Changing c; c.parent = parent; - c.oldSize = model->rowCount ( parent ); - c.last = model->data ( model->index ( start - 1, 0, parent ) ); - c.next = model->data ( model->index ( start, 0, parent ) ); - insert.push ( c ); + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); } /*! @@ -487,10 +484,10 @@ void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, in \sa rowsAboutToBeInserted() */ -void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) +void ModelTest::rowsInserted(const QModelIndex &parent, int start, int end) { Changing c = insert.pop(); - QCOMPARE(c.parent, parent); + QCOMPARE(parent, c.parent); // qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize // << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); @@ -500,30 +497,30 @@ void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) // } // qDebug(); - QCOMPARE(c.oldSize + (end - start + 1), model->rowCount(parent)); - QCOMPARE(c.last, model->data(model->index(start - 1, 0, c.parent))); + QCOMPARE(model->rowCount(parent), c.oldSize + (end - start + 1)); + QCOMPARE(model->data(model->index(start - 1, 0, c.parent)), c.last); if (c.next != model->data(model->index(end + 1, 0, c.parent))) { qDebug() << start << end; - for (int i=0; i < model->rowCount(); ++i) + for (int i = 0; i < model->rowCount(); ++i) qDebug() << model->index(i, 0).data().toString(); qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); } - QCOMPARE(c.next, model->data(model->index(end + 1, 0, c.parent))); + QCOMPARE(model->data(model->index(end + 1, 0, c.parent)), c.next); } void ModelTest::layoutAboutToBeChanged() { - for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) - changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); } void ModelTest::layoutChanged() { - for ( int i = 0; i < changing.count(); ++i ) { + for (int i = 0; i < changing.count(); ++i) { QPersistentModelIndex p = changing[i]; - QCOMPARE(QModelIndex(p), model->index(p.row(), p.column(), p.parent())); + QCOMPARE(model->index(p.row(), p.column(), p.parent()), QModelIndex(p)); } changing.clear(); } @@ -533,15 +530,15 @@ void ModelTest::layoutChanged() \sa rowsRemoved() */ -void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { -qDebug() << "ratbr" << parent << start << end; + qDebug() << "ratbr" << parent << start << end; Changing c; c.parent = parent; - c.oldSize = model->rowCount ( parent ); - c.last = model->data ( model->index ( start - 1, 0, parent ) ); - c.next = model->data ( model->index ( end + 1, 0, parent ) ); - remove.push ( c ); + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); } /*! @@ -549,14 +546,14 @@ qDebug() << "ratbr" << parent << start << end; \sa rowsAboutToBeRemoved() */ -void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) +void ModelTest::rowsRemoved(const QModelIndex &parent, int start, int end) { - qDebug() << "rr" << parent << start << end; + qDebug() << "rr" << parent << start << end; Changing c = remove.pop(); - QCOMPARE(c.parent, parent); - QCOMPARE(c.oldSize - (end - start + 1), model->rowCount(parent)); - QCOMPARE(c.last, model->data(model->index(start - 1, 0, c.parent))); - QCOMPARE(c.next, model->data(model->index(start, 0, c.parent))); + QCOMPARE(parent, c.parent); + QCOMPARE(model->rowCount(parent), c.oldSize - (end - start + 1)); + QCOMPARE(model->data(model->index(start - 1, 0, c.parent)), c.last); + QCOMPARE(model->data(model->index(start, 0, c.parent)), c.next); } void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) @@ -582,4 +579,3 @@ void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int en QVERIFY(start < itemCount); QVERIFY(end < itemCount); } - diff --git a/tests/auto/other/modeltest/modeltest.h b/tests/auto/other/modeltest/modeltest.h index 735a422729..4676bf4434 100644 --- a/tests/auto/other/modeltest/modeltest.h +++ b/tests/auto/other/modeltest/modeltest.h @@ -26,7 +26,6 @@ ** ****************************************************************************/ - #ifndef MODELTEST_H #define MODELTEST_H @@ -36,48 +35,48 @@ class ModelTest : public QObject { - Q_OBJECT + Q_OBJECT public: - ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); + ModelTest(QAbstractItemModel *model, QObject *parent = 0); private Q_SLOTS: - void nonDestructiveBasicTest(); - void rowCount(); - void columnCount(); - void hasIndex(); - void index(); - void parent(); - void data(); + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); protected Q_SLOTS: - void runAllTests(); - void layoutAboutToBeChanged(); - void layoutChanged(); - void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); - void rowsInserted( const QModelIndex & parent, int start, int end ); - void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); - void rowsRemoved( const QModelIndex & parent, int start, int end ); - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void headerDataChanged(Qt::Orientation orientation, int start, int end); + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex &parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex &parent, int start, int end); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void headerDataChanged(Qt::Orientation orientation, int start, int end); private: - void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); + void checkChildren(const QModelIndex &parent, int currentDepth = 0); - QAbstractItemModel *model; + QAbstractItemModel *model; - struct Changing { - QModelIndex parent; - int oldSize; - QVariant last; - QVariant next; - }; - QStack<Changing> insert; - QStack<Changing> remove; + struct Changing { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack<Changing> insert; + QStack<Changing> remove; - bool fetchingMore; + bool fetchingMore; - QList<QPersistentModelIndex> changing; + QList<QPersistentModelIndex> changing; }; #endif diff --git a/tests/auto/other/modeltest/tst_modeltest.cpp b/tests/auto/other/modeltest/tst_modeltest.cpp index f81fefe9d1..e2d002844b 100644 --- a/tests/auto/other/modeltest/tst_modeltest.cpp +++ b/tests/auto/other/modeltest/tst_modeltest.cpp @@ -26,7 +26,6 @@ ** ****************************************************************************/ - #include <QtTest/QtTest> #include <QtGui/QtGui> #include <QtWidgets/QtWidgets> @@ -34,7 +33,6 @@ #include "modeltest.h" #include "dynamictreemodel.h" - class tst_ModelTest : public QObject { Q_OBJECT @@ -63,7 +61,7 @@ void tst_ModelTest::stringListModel() proxy.setSourceModel(&model); model.setStringList(QStringList() << "2" << "3" << "1"); - model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" ); + model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c"); proxy.setDynamicSortFilter(true); proxy.setFilterRegExp(QRegExp("[^b]")); @@ -76,9 +74,8 @@ void tst_ModelTest::treeWidgetModel() ModelTest t1(widget.model()); QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root")); - for (int i = 0; i < 20; ++i) { + for (int i = 0; i < 20; ++i) new QTreeWidgetItem(root, QStringList(QString::number(i))); - } QTreeWidgetItem *remove = root->child(2); root->removeChild(remove); QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent")); @@ -90,10 +87,9 @@ void tst_ModelTest::treeWidgetModel() void tst_ModelTest::standardItemModel() { - QStandardItemModel model(10,10); + QStandardItemModel model(10, 10); QSortFilterProxyModel proxy; - ModelTest t1(&model); ModelTest t2(&proxy); @@ -105,8 +101,8 @@ void tst_ModelTest::standardItemModel() model.insertColumns(2, 5); model.removeColumns(4, 5); - model.insertRows(0,5, model.index(1,1)); - model.insertColumns(0,5, model.index(1,3)); + model.insertRows(0, 5, model.index(1, 1)); + model.insertColumns(0, 5, model.index(1, 3)); } void tst_ModelTest::testInsertThroughProxy() @@ -148,7 +144,9 @@ class AccessibleProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {} + AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) + { + } QModelIndexList persistent() { @@ -160,14 +158,16 @@ class ObservingObject : public QObject { Q_OBJECT public: - ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0) - : QObject(parent) - , m_proxy(proxy) - , storePersistentFailureCount(0) - , checkPersistentFailureCount(0) + ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0) : + QObject(parent), + m_proxy(proxy), + storePersistentFailureCount(0), + checkPersistentFailureCount(0) { - connect(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(storePersistent())); - connect(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(checkPersistent())); + connect(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), + SLOT(storePersistent())); + connect(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + SLOT(checkPersistent())); } public slots: @@ -195,7 +195,7 @@ public slots: void storePersistent() { // This method is called from rowsAboutToBeMoved. Persistent indexes should be valid - foreach(const QModelIndex &idx, m_persistentProxyIndexes) + foreach (const QModelIndex &idx, m_persistentProxyIndexes) if (!idx.isValid()) { qWarning("%s: persistentProxyIndexes contains invalid index", Q_FUNC_INFO); ++storePersistentFailureCount; @@ -233,7 +233,7 @@ public slots: } private: - AccessibleProxyModel *m_proxy; + AccessibleProxyModel *m_proxy; QList<QPersistentModelIndex> m_persistentSourceIndexes; QList<QPersistentModelIndex> m_persistentProxyIndexes; public: @@ -296,6 +296,5 @@ void tst_ModelTest::testResetThroughProxy() QCOMPARE(observer.checkPersistentFailureCount, 0); } - QTEST_MAIN(tst_ModelTest) #include "tst_modeltest.moc" diff --git a/tests/auto/other/qaccessibilitymac/qaccessibilitymac.pro b/tests/auto/other/qaccessibilitymac/qaccessibilitymac.pro index ceed81c914..e55757775e 100644 --- a/tests/auto/other/qaccessibilitymac/qaccessibilitymac.pro +++ b/tests/auto/other/qaccessibilitymac/qaccessibilitymac.pro @@ -1,6 +1,5 @@ CONFIG += testcase TARGET = tst_qaccessibilitymac -# LIBS += -framework Carbon QT += widgets testlib HEADERS += tst_qaccessibilitymac_helpers.h diff --git a/tests/auto/other/qfocusevent/tst_qfocusevent.cpp b/tests/auto/other/qfocusevent/tst_qfocusevent.cpp index 5bf39b3953..a56caf9844 100644 --- a/tests/auto/other/qfocusevent/tst_qfocusevent.cpp +++ b/tests/auto/other/qfocusevent/tst_qfocusevent.cpp @@ -286,7 +286,7 @@ void tst_QFocusEvent::checkReason_Shortcut() void tst_QFocusEvent::checkReason_focusWidget() { // This test checks that a widget doesn't loose - // its focuswidget just because the focuswidget looses focus. + // its focuswidget just because the focuswidget loses focus. QWidget window1; QWidget frame1; QWidget frame2; diff --git a/tests/auto/shared/platformclipboard.h b/tests/auto/shared/platformclipboard.h index c5f1a64dce..15801f6add 100644 --- a/tests/auto/shared/platformclipboard.h +++ b/tests/auto/shared/platformclipboard.h @@ -31,22 +31,12 @@ #include <qglobal.h> -#ifdef Q_OS_OSX -#include <Carbon/Carbon.h> -#endif - struct PlatformClipboard { static inline bool isAvailable() { #if defined(QT_NO_CLIPBOARD) return false; -#elif defined(Q_OS_OSX) - PasteboardRef pasteboard; - OSStatus status = PasteboardCreate(0, &pasteboard); - if (status == noErr) - CFRelease(pasteboard); - return status == noErr; #else return true; #endif diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp index d7772f5c34..e3f088e763 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp @@ -188,6 +188,9 @@ private slots: void sqlite_enable_cache_mode_data() { generic_data("QSQLITE"); } void sqlite_enable_cache_mode(); + void sqlite_enableRegexp_data() { generic_data("QSQLITE"); } + void sqlite_enableRegexp(); + private: void createTestTables(QSqlDatabase db); void dropTestTables(QSqlDatabase db); @@ -345,7 +348,8 @@ void tst_QSqlDatabase::dropTestTables(QSqlDatabase db) << qTableName("qtest_sqlguid", __FILE__, db) << qTableName("uint_table", __FILE__, db) << qTableName("uint_test", __FILE__, db) - << qTableName("bug_249059", __FILE__, db); + << qTableName("bug_249059", __FILE__, db) + << qTableName("regexp_test", __FILE__, db); QSqlQuery q(0, db); if (dbType == QSqlDriver::PostgreSQL) { @@ -2259,5 +2263,33 @@ void tst_QSqlDatabase::sqlite_enable_cache_mode() db2.close(); } +void tst_QSqlDatabase::sqlite_enableRegexp() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + if (db.driverName().startsWith("QSQLITE2")) + QSKIP("SQLite3 specific test"); + + db.close(); + db.setConnectOptions("QSQLITE_ENABLE_REGEXP"); + QVERIFY_SQL(db, open()); + + QSqlQuery q(db); + const QString tableName(qTableName("regexp_test", __FILE__, db)); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1(text TEXT)").arg(tableName))); + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES(?)").arg(tableName))); + q.addBindValue("a0"); + QVERIFY_SQL(q, exec()); + q.addBindValue("a1"); + QVERIFY_SQL(q, exec()); + + QVERIFY_SQL(q, exec(QString("SELECT text FROM %1 WHERE text REGEXP 'a[^0]' " + "ORDER BY text").arg(tableName))); + QVERIFY_SQL(q, next()); + QCOMPARE(q.value(0).toString(), QString("a1")); + QFAIL_SQL(q, next()); +} + QTEST_MAIN(tst_QSqlDatabase) #include "tst_qsqldatabase.moc" diff --git a/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp b/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp index 07a6c9c835..08c6039e37 100644 --- a/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp +++ b/tests/auto/sql/kernel/qsqlerror/tst_qsqlerror.cpp @@ -44,6 +44,7 @@ public: private slots: void getSetCheck(); void construction(); + void moveOperator(); void operators(); }; @@ -143,6 +144,26 @@ void tst_QSqlError::construction() QCOMPARE(obj7.number(), -1); QCOMPARE(obj7.nativeErrorCode(), QString()); + // Move constructor + QSqlError obj8(std::move(obj3)); + QCOMPARE(obj8.driverText(), obj2.driverText()); + QCOMPARE(obj8.databaseText(), obj2.databaseText()); + QCOMPARE(obj8.type(), obj2.type()); + QCOMPARE(obj8.number(), obj2.number()); + QCOMPARE(obj8.nativeErrorCode(), obj2.nativeErrorCode()); + QVERIFY(obj8.isValid()); +} + +void tst_QSqlError::moveOperator() +{ + QSqlError obj1("drivertext", "databasetext", QSqlError::UnknownError, 123), obj2; + obj2 = std::move(obj1); + QCOMPARE(obj2.driverText(), QString("drivertext")); + QCOMPARE(obj2.databaseText(), QString("databasetext")); + QCOMPARE(obj2.type(), QSqlError::UnknownError); + QCOMPARE(obj2.number(), 123); + QCOMPARE(obj2.nativeErrorCode(), QStringLiteral("123")); + QVERIFY(obj2.isValid()); } void tst_QSqlError::operators() diff --git a/tests/auto/testlib/selftests/expected_pairdiagnostics.lightxml b/tests/auto/testlib/selftests/expected_pairdiagnostics.lightxml new file mode 100644 index 0000000000..95b932e3c4 --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pairdiagnostics.lightxml @@ -0,0 +1,30 @@ +<Environment> + <QtVersion>@INSERT_QT_VERSION_HERE@</QtVersion> + <QtBuild/> + <QTestVersion>@INSERT_QT_VERSION_HERE@</QTestVersion> +</Environment> +<TestFunction name="initTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testQPair"> +<Incident type="fail" file="../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp" line="51"> + <Description><![CDATA[Compared values are not the same + Actual (pair1): "QPair(1,1)" + Expected (pair2): "QPair(1,2)"]]></Description> +</Incident> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testStdPair"> +<Incident type="fail" file="../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp" line="58"> + <Description><![CDATA[Compared values are not the same + Actual (pair1): "std::pair(1,1)" + Expected (pair2): "std::pair(1,2)"]]></Description> +</Incident> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="cleanupTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<Duration msecs="0"/> diff --git a/tests/auto/testlib/selftests/expected_pairdiagnostics.teamcity b/tests/auto/testlib/selftests/expected_pairdiagnostics.teamcity new file mode 100644 index 0000000000..133e3aac5f --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pairdiagnostics.teamcity @@ -0,0 +1,12 @@ +##teamcity[testSuiteStarted name='tst_PairDiagnostics'] +##teamcity[testStarted name='initTestCase()'] +##teamcity[testFinished name='initTestCase()'] +##teamcity[testStarted name='testQPair()'] +##teamcity[testFailed name='testQPair()' message='Failure! |[Loc: ../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp(51)|]' details='Compared values are not the same|n Actual (pair1): "QPair(1,1)"|n Expected (pair2): "QPair(1,2)"'] +##teamcity[testFinished name='testQPair()'] +##teamcity[testStarted name='testStdPair()'] +##teamcity[testFailed name='testStdPair()' message='Failure! |[Loc: ../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp(58)|]' details='Compared values are not the same|n Actual (pair1): "std::pair(1,1)"|n Expected (pair2): "std::pair(1,2)"'] +##teamcity[testFinished name='testStdPair()'] +##teamcity[testStarted name='cleanupTestCase()'] +##teamcity[testFinished name='cleanupTestCase()'] +##teamcity[testSuiteFinished name='tst_PairDiagnostics'] diff --git a/tests/auto/testlib/selftests/expected_pairdiagnostics.txt b/tests/auto/testlib/selftests/expected_pairdiagnostics.txt new file mode 100644 index 0000000000..e09b9a560f --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pairdiagnostics.txt @@ -0,0 +1,14 @@ +********* Start testing of tst_PairDiagnostics ********* +Config: Using QtTest library @INSERT_QT_VERSION_HERE@, Qt @INSERT_QT_VERSION_HERE@ +PASS : tst_PairDiagnostics::initTestCase() +FAIL! : tst_PairDiagnostics::testQPair() Compared values are not the same + Actual (pair1): "QPair(1,1)" + Expected (pair2): "QPair(1,2)" + Loc: [../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp(51)] +FAIL! : tst_PairDiagnostics::testStdPair() Compared values are not the same + Actual (pair1): "std::pair(1,1)" + Expected (pair2): "std::pair(1,2)" + Loc: [../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp(58)] +PASS : tst_PairDiagnostics::cleanupTestCase() +Totals: 2 passed, 2 failed, 0 skipped, 0 blacklisted, 1ms +********* Finished testing of tst_PairDiagnostics ********* diff --git a/tests/auto/testlib/selftests/expected_pairdiagnostics.xml b/tests/auto/testlib/selftests/expected_pairdiagnostics.xml new file mode 100644 index 0000000000..47921e0b8a --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pairdiagnostics.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<TestCase name="tst_PairDiagnostics"> +<Environment> + <QtVersion>@INSERT_QT_VERSION_HERE@</QtVersion> + <QtBuild/> + <QTestVersion>@INSERT_QT_VERSION_HERE@</QTestVersion> +</Environment> +<TestFunction name="initTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0.680795"/> +</TestFunction> +<TestFunction name="testQPair"> +<Incident type="fail" file="../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp" line="51"> + <Description><![CDATA[Compared values are not the same + Actual (pair1): "QPair(1,1)" + Expected (pair2): "QPair(1,2)"]]></Description> +</Incident> + <Duration msecs="0.085705"/> +</TestFunction> +<TestFunction name="testStdPair"> +<Incident type="fail" file="../../../qt5/qtbase_fixItemData/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp" line="58"> + <Description><![CDATA[Compared values are not the same + Actual (pair1): "std::pair(1,1)" + Expected (pair2): "std::pair(1,2)"]]></Description> +</Incident> + <Duration msecs="0.030780"/> +</TestFunction> +<TestFunction name="cleanupTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0.039052"/> +</TestFunction> +<Duration msecs="0.995227"/> +</TestCase> diff --git a/tests/auto/testlib/selftests/expected_pairdiagnostics.xunitxml b/tests/auto/testlib/selftests/expected_pairdiagnostics.xunitxml new file mode 100644 index 0000000000..cf2a30b84a --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pairdiagnostics.xunitxml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<testsuite errors="0" failures="2" tests="4" name="tst_PairDiagnostics"> + <properties> + <property value="@INSERT_QT_VERSION_HERE@" name="QTestVersion"/> + <property value="@INSERT_QT_VERSION_HERE@" name="QtVersion"/> + <property value="" name="QtBuild"/> + </properties> + <testcase result="pass" name="initTestCase"/> + <testcase result="fail" name="testQPair"> + <failure message="Compared values are not the same + Actual (pair1): "QPair(1,1)" + Expected (pair2): "QPair(1,2)"" result="fail"/> + </testcase> + <testcase result="fail" name="testStdPair"> + <failure message="Compared values are not the same + Actual (pair1): "std::pair(1,1)" + Expected (pair2): "std::pair(1,2)"" result="fail"/> + </testcase> + <testcase result="pass" name="cleanupTestCase"/> + <system-err/> +</testsuite> diff --git a/tests/auto/testlib/selftests/generate_expected_output.py b/tests/auto/testlib/selftests/generate_expected_output.py index 202c4cc426..a3a66da98b 100755 --- a/tests/auto/testlib/selftests/generate_expected_output.py +++ b/tests/auto/testlib/selftests/generate_expected_output.py @@ -32,9 +32,18 @@ # Usage: cd to the build directory corresponding to this script's # location; invoke this script; optionally pass the names of sub-dirs # to limit which tests to regenerate expected_* files for. +# +# The saved test output is used by ./tst_selftests.cpp, which compares +# it to the output of each test, ignoring various boring changes. +# This script canonicalises the parts that would exhibit those boring +# changes, so as to avoid noise in git (and conflicts in merges) for +# the saved copies of the output. If you add or remove any files, be +# sure to update selftests.qrc to match; the selftest only sees files +# listed there. import os import subprocess +import re class Fail (Exception): pass @@ -55,17 +64,18 @@ class Cleaner (object): def __init__(self, here, command): """Set up the details we need for later cleaning. - Takes two parameters: here is $PWD and command is how this - script was invoked, from which we'll work out where it is; in - a shadow build, the former is the build tree's location - corresponding to this last. Checks $PWD does look as expected - in a build tree - raising Fail() if not - then invokes qmake - to discover Qt version (saved as .version for the benefit of - clients) and prepares the sequence of (regex, replace) pairs - that .clean() needs to do its job.""" - self.version, self.__replace = self.__getPatterns(here, command) - - import re + Takes two parameters: here is os.getcwd() and command is how + this script was invoked, from which we'll work out where it + is; in a shadow build, the former is the build tree's location + corresponding to this last. Saves the directory of this + script as self.sourceDir, so client can find tst_selftests.cpp + there. Checks here does look as expected in a build tree - + raising Fail() if not - then invokes qmake to discover Qt + version (saved as .version for the benefit of clients) and + prepares the sequence of (regex, replace) pairs that .clean() + needs to do its job.""" + self.version, self.sourceDir, self.__replace = self.__getPatterns(here, command) + @staticmethod def __getPatterns(here, command, patterns = ( @@ -80,9 +90,10 @@ class Cleaner (object): (r'( *<QtBuild)>[^<]+</QtBuild>', r'\1/>'), # xml, lightxml (r'(<property value=")[^"]+(" name="QtBuild"/>)', r'\1\2'), # xunitxml # Line numbers in source files: + (r'(ASSERT: ".*" in file .*, line) \d+', r'\1 0'), # lightxml (r'(Loc: \[[^[\]()]+)\(\d+\)', r'\1(0)'), # txt (r'(\[Loc: [^[\]()]+)\(\d+\)', r'\1(0)'), # teamcity - (r'(<Incident.*\bfile=.*\bline=)"\d+"', r'\1"0"'), # lightxml, xml + (r'(<(?:Incident|Message)\b.*\bfile=.*\bline=)"\d+"', r'\1"0"'), # lightxml, xml ), precook = re.compile): """Private implementation details of __init__().""" @@ -131,8 +142,7 @@ class Cleaner (object): patterns += tuple((root.replace('-', '�*2D;'), r'') for root in roots if '-' in root) - return qtver, tuple((precook(p), r) for p, r in patterns) - del re + return qtver, scriptPath, tuple((precook(p), r) for p, r in patterns) def clean(self, data): """Remove volatile details from test output. @@ -145,6 +155,62 @@ class Cleaner (object): line = searchRe.sub(replaceExp, line) yield line +class Scanner (object): + """Knows which subdirectories to generate output for. + + Tell its constructor the name of this source directory (see + Cleaner's .sourceDir) and it'll scan tst_selftests.cpp for the + list. Its .subdirs() can then filter a user-supplied list of + subdirs or generate the full list, when the user supplied + none.""" + def __init__(self, srcDir): + self.__tested = tuple(self.__scan_cpp(os.path.join(srcDir, 'tst_selftests.cpp'))) + + @staticmethod + def __scan_cpp(name, + trimc = re.compile(r'/\*.*?\*/').sub, + trimcpp = re.compile(r'//.*$').sub, + first = re.compile(r'(QStringList|auto)\s+tests\s*=\s*QStringList\(\)').match, + match = re.compile(r'(?:tests\s*)?<<\s*"(\w+)"').match, + last = re.compile(r'\bfor.*\b(LoggerSet|auto)\b.*\ballLoggerSets\(\)').search): + """Scans tst_selftests.cpp to find which subdirs matter. + + There's a list, tests, to which all subdir names get added, if + they're to be tested. Other sub-dirs aren't tested, so + there's no sense in generating output for them.""" + scan = False + with open(name) as src: + for line in src: + line = trimcpp('', trimc('', line.strip())).strip() + if not scan: + got = first(line) + if got: + scan, line = True, line[len(got.group()):] + if scan: + if last(line): break + got = match(line) + while got: + yield got.group(1) + line = line[len(got.group()):].strip() + got = match(line) + + def subdirs(self, given): + if given: + for d in given: + if not os.path.isdir(d): + print('No such directory:', d, '- skipped') + elif d in self.__tested: + yield d + else: + print('Directory', d, 'is not tested by tst_selftests.cpp') + else: + for d in self.__tested: + if os.path.isdir(d): + yield d + else: + print('tst_selftests.cpp names', d, "as a test, but it doesn't exist") +del re + def generateTestData(testname, clean, formats = ('xml', 'txt', 'xunitxml', 'lightxml', 'teamcity'), extraArgs = { @@ -190,7 +256,7 @@ def main(name, *args): herePath = os.getcwd() cleaner = Cleaner(herePath, name) - tests = args if args else [d for d in os.listdir('.') if os.path.isdir(d)] + tests = tuple(Scanner(cleaner.sourceDir).subdirs(args)) print("Generating", len(tests), "test results for", cleaner.version, "in:", herePath) for path in tests: generateTestData(path, cleaner.clean) diff --git a/tests/auto/testlib/selftests/pairdiagnostics/pairdiagnostics.pro b/tests/auto/testlib/selftests/pairdiagnostics/pairdiagnostics.pro new file mode 100644 index 0000000000..1c07c93e9d --- /dev/null +++ b/tests/auto/testlib/selftests/pairdiagnostics/pairdiagnostics.pro @@ -0,0 +1,6 @@ +SOURCES += tst_pairdiagnostics.cpp +QT = core testlib + +CONFIG -= app_bundle debug_and_release_target + +TARGET = pairdiagnostics diff --git a/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp b/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp new file mode 100644 index 0000000000..bbee5334fe --- /dev/null +++ b/tests/auto/testlib/selftests/pairdiagnostics/tst_pairdiagnostics.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// Make sure we get a real Q_ASSERT even in release builds +#ifdef QT_NO_DEBUG +# undef QT_NO_DEBUG +#endif + +#include <QtCore/QCoreApplication> +#include <QtCore/QPair> +#include <QtTest/QtTest> + +class tst_PairDiagnostics: public QObject +{ + Q_OBJECT + +private slots: + void testQPair() const; + void testStdPair() const; +}; + +void tst_PairDiagnostics::testQPair() const +{ + QPair<int, int> pair1 = qMakePair(1, 1); + QPair<int, int> pair2 = qMakePair(1, 2); + QCOMPARE(pair1, pair2); +} + +void tst_PairDiagnostics::testStdPair() const +{ + std::pair<int, int> pair1 = std::make_pair(1, 1); + std::pair<int, int> pair2 = std::make_pair(1, 2); + QCOMPARE(pair1, pair2); +} + +QTEST_MAIN(tst_PairDiagnostics) + +#include "tst_pairdiagnostics.moc" diff --git a/tests/auto/testlib/selftests/selftests.pri b/tests/auto/testlib/selftests/selftests.pri index 66c7e06760..0287e35447 100644 --- a/tests/auto/testlib/selftests/selftests.pri +++ b/tests/auto/testlib/selftests/selftests.pri @@ -28,6 +28,7 @@ SUBPROGRAMS = \ longstring \ maxwarnings \ multiexec \ + pairdiagnostics \ printdatatags \ printdatatagswithglobaltags \ qexecstringlist \ diff --git a/tests/auto/testlib/selftests/selftests.qrc b/tests/auto/testlib/selftests/selftests.qrc index 3c3fef28d9..02e8adb6b4 100644 --- a/tests/auto/testlib/selftests/selftests.qrc +++ b/tests/auto/testlib/selftests/selftests.qrc @@ -115,6 +115,11 @@ <file>expected_maxwarnings.xml</file> <file>expected_maxwarnings.xunitxml</file> <file>expected_multiexec.txt</file> + <file>expected_pairdiagnostics.lightxml</file> + <file>expected_pairdiagnostics.teamcity</file> + <file>expected_pairdiagnostics.txt</file> + <file>expected_pairdiagnostics.xml</file> + <file>expected_pairdiagnostics.xunitxml</file> <file>expected_printdatatags.txt</file> <file>expected_printdatatagswithglobaltags.txt</file> <file>expected_qexecstringlist.txt</file> diff --git a/tests/auto/testlib/selftests/tst_selftests.cpp b/tests/auto/testlib/selftests/tst_selftests.cpp index e7123fc059..64f324e26c 100644 --- a/tests/auto/testlib/selftests/tst_selftests.cpp +++ b/tests/auto/testlib/selftests/tst_selftests.cpp @@ -395,6 +395,7 @@ void tst_Selftests::runSubTest_data() << "longstring" << "maxwarnings" << "multiexec" + << "pairdiagnostics" << "printdatatags" << "printdatatagswithglobaltags" << "qexecstringlist" diff --git a/tests/auto/tools/moc/error-on-wrong-notify.h b/tests/auto/tools/moc/error-on-wrong-notify.h index 11f5e954db..9e6b27bff3 100644 --- a/tests/auto/tools/moc/error-on-wrong-notify.h +++ b/tests/auto/tools/moc/error-on-wrong-notify.h @@ -28,7 +28,7 @@ #ifndef ERROR_ON_WRONG_NOTIFY_H #define ERROR_ON_WRONG_NOTIFY_H -#include <QObject> +#include <QtCore/QObject> class ClassWithWrongNOTIFY : public QObject { diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 8189227cbe..55e8054a70 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -1830,13 +1830,25 @@ void tst_Moc::notifyError() const QString header = m_sourceDirectory + QStringLiteral("/error-on-wrong-notify.h"); proc.start(m_moc, QStringList(header)); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 1); + QCOMPARE(proc.exitCode(), 0); QCOMPARE(proc.exitStatus(), QProcess::NormalExit); QByteArray mocOut = proc.readAllStandardOutput(); - QVERIFY(mocOut.isEmpty()); - QString mocError = QString::fromLocal8Bit(proc.readAllStandardError()); - QCOMPARE(mocError, header + - QString(":42: Error: NOTIFY signal 'fooChanged' of property 'foo' does not exist in class ClassWithWrongNOTIFY.\n")); + QVERIFY(!mocOut.isEmpty()); + QCOMPARE(proc.readAllStandardError(), QByteArray()); + + QStringList args; + args << "-c" << "-x" << "c++" << "-I" << "." + << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; + proc.start("gcc", args); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); + proc.closeWriteChannel(); + + QVERIFY(proc.waitForFinished()); + QCOMPARE(proc.exitCode(), 1); + const QString gccOutput = QString::fromLocal8Bit(proc.readAllStandardError()); + QVERIFY(gccOutput.contains(QLatin1String("error"))); + QVERIFY(gccOutput.contains(QLatin1String("fooChanged"))); #else QSKIP("Only tested on linux/gcc"); #endif diff --git a/tests/auto/tools/qmakelib/evaltest.cpp b/tests/auto/tools/qmakelib/evaltest.cpp index 4e215b8570..10a5d7c0c3 100644 --- a/tests/auto/tools/qmakelib/evaltest.cpp +++ b/tests/auto/tools/qmakelib/evaltest.cpp @@ -2191,6 +2191,44 @@ void tst_qmakelib::addTestFunctions(const QString &qindir) << "" << true; + QTest::newRow("versionAtLeast(): true") + << "VAR = 1.2.3\nversionAtLeast(VAR, 1.2.3): OK = 1" + << "OK = 1" + << "" + << true; + + QTest::newRow("versionAtLeast(): false") + << "VAR = 1.2.2\nversionAtLeast(VAR, 1.2.3): OK = 1" + << "OK = UNDEF" + << "" + << true; + + QTest::newRow("versionAtLeast(): bad number of arguments") + << "versionAtLeast(1): OK = 1\nversionAtLeast(1, 2, 3): OK = 1" + << "OK = UNDEF" + << "##:1: versionAtLeast(variable, versionNumber) requires two arguments.\n" + "##:2: versionAtLeast(variable, versionNumber) requires two arguments." + << true; + + QTest::newRow("versionAtMost(): true") + << "VAR = 1.2.3\nversionAtMost(VAR, 1.2.3): OK = 1" + << "OK = 1" + << "" + << true; + + QTest::newRow("versionAtMost(): false") + << "VAR = 1.2.3\nversionAtMost(VAR, 1.2.2): OK = 1" + << "OK = UNDEF" + << "" + << true; + + QTest::newRow("versionAtMost(): bad number of arguments") + << "versionAtMost(1): OK = 1\nversionAtMost(1, 2, 3): OK = 1" + << "OK = UNDEF" + << "##:1: versionAtMost(variable, versionNumber) requires two arguments.\n" + "##:2: versionAtMost(variable, versionNumber) requires two arguments." + << true; + QTest::newRow("clear(): top-level") << "VAR = there\nclear(VAR): OK = 1" << "OK = 1\nVAR =" diff --git a/tests/auto/tools/uic/tst_uic.cpp b/tests/auto/tools/uic/tst_uic.cpp index cf43cb02d3..85668c96d4 100644 --- a/tests/auto/tools/uic/tst_uic.cpp +++ b/tests/auto/tools/uic/tst_uic.cpp @@ -34,6 +34,7 @@ #include <QtCore/QByteArray> #include <QtCore/QLibraryInfo> #include <QtCore/QTemporaryDir> +#include <QtCore/QRegularExpression> #include <QtCore/QStandardPaths> class tst_uic : public QObject @@ -63,12 +64,12 @@ private: const QString m_command; QString m_baseline; QTemporaryDir m_generated; - QRegExp m_versionRegexp; + QRegularExpression m_versionRegexp; }; tst_uic::tst_uic() : m_command(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/uic")) - , m_versionRegexp(QLatin1String("Created by: Qt User Interface Compiler version [.\\d]{5,5}")) + , m_versionRegexp(QLatin1String("\\*\\* Created by: Qt User Interface Compiler version \\d{1,2}\\.\\d{1,2}\\.\\d{1,2}")) { } diff --git a/tests/auto/widgets/gestures/gestures.pro b/tests/auto/widgets/gestures/gestures.pro deleted file mode 100644 index 5a87a01ee2..0000000000 --- a/tests/auto/widgets/gestures/gestures.pro +++ /dev/null @@ -1,6 +0,0 @@ -TEMPLATE=subdirs -SUBDIRS=\ - qgesturerecognizer \ - -mac: SUBDIRS -= \ # Uses native recognizers - qgesturerecognizer \ diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index 121836234d..1430911cf0 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -2903,7 +2903,7 @@ void tst_QGraphicsView::scrollBarRanges() QFETCH(ExpectedValueDescription, vmax); QFETCH(bool, useStyledPanel); - if (useStyledPanel && style == "Macintosh" && platformName == QStringLiteral("cocoa")) + if (useStyledPanel && style == "macintosh" && platformName == QStringLiteral("cocoa")) QSKIP("Insignificant on OSX"); QScopedPointer<QStyle> stylePtr; diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index f2cf78e8ac..4a27d63cba 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -626,7 +626,9 @@ void tst_QAbstractItemView::rowDelegate() QVERIFY(QTest::qWaitForWindowExposed(&view)); QModelIndex index = model.index(3, 0); + QVERIFY(!view.isPersistentEditorOpen(index)); view.openPersistentEditor(index); + QVERIFY(view.isPersistentEditorOpen(index)); QWidget *w = view.indexWidget(index); QVERIFY(w); QCOMPARE(w->metaObject()->className(), "QWidget"); @@ -646,7 +648,9 @@ void tst_QAbstractItemView::columnDelegate() QVERIFY(QTest::qWaitForWindowExposed(&view)); QModelIndex index = model.index(0, 3); + QVERIFY(!view.isPersistentEditorOpen(index)); view.openPersistentEditor(index); + QVERIFY(view.isPersistentEditorOpen(index)); QWidget *w = view.indexWidget(index); QVERIFY(w); QCOMPARE(w->metaObject()->className(), "QWidget"); diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index e2886cfcfe..45d33df356 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -169,6 +169,9 @@ private slots: void styleOptionViewItem(); void keyboardNavigationWithDisabled(); + void statusTip_data(); + void statusTip(); + // task-specific tests: void task174627_moveLeftToRoot(); void task171902_expandWith1stColHidden(); @@ -213,6 +216,7 @@ public: void init() { decorationsEnabled = false; + statusTipsEnabled = false; } inline qint32 level(const QModelIndex &index) const { @@ -294,6 +298,19 @@ public: pm.fill(QColor::fromHsv((idx.column() % 16)*8 + 64, 254, (idx.row() % 16)*8 + 32)); return pm; } + if (statusTipsEnabled && role == Qt::StatusTipRole) + return QString("[%1,%2,%3] -- Status").arg(idx.row()).arg(idx.column()).arg(level(idx)); + return QVariant(); + } + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override + { + Q_UNUSED(orientation); + if (section < 0 || section >= columnCount()) + return QVariant(); + if (statusTipsEnabled && role == Qt::StatusTipRole) + return QString("Header %1 -- Status").arg(section); return QVariant(); } @@ -339,6 +356,7 @@ public: mutable bool fetched; bool decorationsEnabled; + bool statusTipsEnabled; int rows, cols; int levels; mutable bool wrongIndex; @@ -4405,5 +4423,52 @@ void tst_QTreeView::taskQTBUG_7232_AllowUserToControlSingleStep() QCOMPARE(hStep1, t.horizontalScrollBar()->singleStep()); } +void tst_QTreeView::statusTip_data() +{ + QTest::addColumn<bool>("intermediateParent"); + QTest::newRow("noIntermediate") << false; + QTest::newRow("intermediate") << true; +} + +void tst_QTreeView::statusTip() +{ + QFETCH(bool, intermediateParent); + QMainWindow mw; + QtTestModel model; + model.statusTipsEnabled = true; + model.rows = model.cols = 5; + QTreeView *view = new QTreeView; + view->setModel(&model); + view->viewport()->setMouseTracking(true); + view->header()->viewport()->setMouseTracking(true); + if (intermediateParent) { + QWidget *inter = new QWidget; + QVBoxLayout *vbox = new QVBoxLayout; + inter->setLayout(vbox); + vbox->addWidget(view); + mw.setCentralWidget(inter); + } else { + mw.setCentralWidget(view); + } + mw.statusBar(); + mw.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)), + QSize(500, 500))); + mw.show(); + qApp->setActiveWindow(&mw); + QTest::qWaitForWindowActive(&mw); + // Ensure it is moved away first and then moved to the relevant section + QTest::mouseMove(mw.windowHandle(), view->mapTo(&mw, view->rect().bottomLeft() + QPoint(20, 20))); + QPoint centerPoint = view->viewport()->mapTo(&mw, view->visualRect(model.index(0, 0)).center()); + QTest::mouseMove(mw.windowHandle(), centerPoint); + QTRY_COMPARE(mw.statusBar()->currentMessage(), QLatin1String("[0,0,0] -- Status")); + centerPoint = view->viewport()->mapTo(&mw, view->visualRect(model.index(0, 1)).center()); + QTest::mouseMove(mw.windowHandle(), centerPoint); + QTRY_COMPARE(mw.statusBar()->currentMessage(), QLatin1String("[0,1,0] -- Status")); + centerPoint = view->header()->viewport()->mapTo(&mw, + QPoint(view->header()->sectionViewportPosition(0) + view->header()->sectionSize(0) / 2, + view->header()->height() / 2)); + QTest::mouseMove(mw.windowHandle(), centerPoint); + QTRY_COMPARE(mw.statusBar()->currentMessage(), QLatin1String("Header 0 -- Status")); +} QTEST_MAIN(tst_QTreeView) #include "tst_qtreeview.moc" diff --git a/tests/auto/widgets/kernel/kernel.pro b/tests/auto/widgets/kernel/kernel.pro index 20720dc928..73fd934502 100644 --- a/tests/auto/widgets/kernel/kernel.pro +++ b/tests/auto/widgets/kernel/kernel.pro @@ -6,6 +6,7 @@ SUBDIRS=\ qboxlayout \ qdesktopwidget \ qformlayout \ + qgesturerecognizer \ qgridlayout \ qlayout \ qstackedlayout \ @@ -19,4 +20,7 @@ SUBDIRS=\ qshortcut \ qsizepolicy +darwin:SUBDIRS -= \ # Uses native recognizers + qgesturerecognizer \ + SUBDIRS -= qsound diff --git a/tests/auto/widgets/kernel/qaction/tst_qaction.cpp b/tests/auto/widgets/kernel/qaction/tst_qaction.cpp index 83e1850524..ac6362168e 100644 --- a/tests/auto/widgets/kernel/qaction/tst_qaction.cpp +++ b/tests/auto/widgets/kernel/qaction/tst_qaction.cpp @@ -62,6 +62,7 @@ private slots: void task229128TriggeredSignalWithoutActiongroup(); void task229128TriggeredSignalWhenInActiongroup(); void repeat(); + void keysequence(); // QTBUG-53381 private: int m_lastEventType; @@ -276,6 +277,40 @@ void tst_QAction::alternateShortcuts() QTest::keyClick(&testWidget, Qt::Key_A, Qt::ControlModifier); } +void tst_QAction::keysequence() +{ + MyWidget testWidget(this); + testWidget.show(); + QApplication::setActiveWindow(&testWidget); + + { + QAction act(&testWidget); + testWidget.addAction(&act); + + QKeySequence ks(QKeySequence::SelectAll); + + act.setShortcut(ks); + + QSignalSpy spy(&act, &QAction::triggered); + + act.setAutoRepeat(true); + QTest::keySequence(&testWidget, ks); + QCoreApplication::processEvents(); + QCOMPARE(spy.count(), 1); // act should have been triggered + + act.setAutoRepeat(false); + QTest::keySequence(&testWidget, ks); + QCoreApplication::processEvents(); + QCOMPARE(spy.count(), 2); //act should have been triggered a 2nd time + + // end of the scope of the action, it will be destroyed and removed from widget + // This action should also unregister its shortcuts + } + + // this tests a crash (if the action did not unregister its alternate shortcuts) + QTest::keyClick(&testWidget, Qt::Key_A, Qt::ControlModifier); +} + void tst_QAction::enabledVisibleInteraction() { MyWidget testWidget(this); diff --git a/tests/auto/widgets/gestures/qgesturerecognizer/BLACKLIST b/tests/auto/widgets/kernel/qgesturerecognizer/BLACKLIST index 7f55c2dae0..7f55c2dae0 100644 --- a/tests/auto/widgets/gestures/qgesturerecognizer/BLACKLIST +++ b/tests/auto/widgets/kernel/qgesturerecognizer/BLACKLIST diff --git a/tests/auto/widgets/gestures/qgesturerecognizer/qgesturerecognizer.pro b/tests/auto/widgets/kernel/qgesturerecognizer/qgesturerecognizer.pro index 7c9ddcfb03..7c9ddcfb03 100644 --- a/tests/auto/widgets/gestures/qgesturerecognizer/qgesturerecognizer.pro +++ b/tests/auto/widgets/kernel/qgesturerecognizer/qgesturerecognizer.pro diff --git a/tests/auto/widgets/gestures/qgesturerecognizer/tst_qgesturerecognizer.cpp b/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp index c3ebb838bb..c3ebb838bb 100644 --- a/tests/auto/widgets/gestures/qgesturerecognizer/tst_qgesturerecognizer.cpp +++ b/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp diff --git a/tests/auto/widgets/kernel/qwidget/qwidget.pro b/tests/auto/widgets/kernel/qwidget/qwidget.pro index 499ca65516..0e95d454cf 100644 --- a/tests/auto/widgets/kernel/qwidget/qwidget.pro +++ b/tests/auto/widgets/kernel/qwidget/qwidget.pro @@ -12,7 +12,7 @@ aix-g++*:QMAKE_CXXFLAGS+=-fpermissive CONFIG += x11inc mac { - LIBS += -framework Security -framework AppKit -framework Carbon + LIBS += -framework Security -framework AppKit OBJECTIVE_SOURCES += tst_qwidget_mac_helpers.mm } diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 2b6dc5f7e1..d9bb3b28d7 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -1809,9 +1809,11 @@ void tst_QWidget::windowState() QCOMPARE(widget1.pos(), pos); QCOMPARE(widget1.size(), size); -#define VERIFY_STATE(s) QCOMPARE(int(widget1.windowState() & stateMask), int(s)) +#define VERIFY_STATE(s) \ + QCOMPARE(int(widget1.windowState() & stateMask), int(s)); \ + QCOMPARE(int(widget1.windowHandle()->windowStates() & stateMask), int(s)) - const int stateMask = Qt::WindowMaximized|Qt::WindowMinimized|Qt::WindowFullScreen; + const auto stateMask = Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen; widget1.setWindowState(Qt::WindowMaximized); QTest::qWait(100); diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index 6aaac6d135..a6855a417e 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -99,6 +99,11 @@ private slots: void tst_eventfilter_on_toplevel(); void QTBUG_50561_QCocoaBackingStore_paintDevice_crash(); + + void setWindowState_data(); + void setWindowState(); + + void nativeShow(); }; void tst_QWidget_window::initTestCase() @@ -861,5 +866,70 @@ void tst_QWidget_window::QTBUG_50561_QCocoaBackingStore_paintDevice_crash() w.close(); } +void tst_QWidget_window::setWindowState_data() +{ + QString platformName = QGuiApplication::platformName().toLower(); + + QTest::addColumn<Qt::WindowStates>("state"); + QTest::newRow("0") << Qt::WindowStates(); + QTest::newRow("Qt::WindowMaximized") << Qt::WindowStates(Qt::WindowMaximized); + QTest::newRow("Qt::WindowMinimized") << Qt::WindowStates(Qt::WindowMinimized); + QTest::newRow("Qt::WindowFullScreen") << Qt::WindowStates(Qt::WindowFullScreen); + + if (platformName != "xcb" && platformName != "windows" && !platformName.startsWith("wayland") + && platformName != "offscreen") + return; // Combination of states is not preserved on all platforms. + if (platformName == "xcb" && qgetenv("XDG_CURRENT_DESKTOP") != "KDE" + && qgetenv("XDG_CURRENT_DESKTOP") != "Unity") + return; // Not all window managers support state combinations. + + QTest::newRow("Qt::WindowMaximized|Qt::WindowMinimized") + << (Qt::WindowMaximized | Qt::WindowMinimized); + QTest::newRow("Qt::WindowFullScreen|Qt::WindowMinimized") + << (Qt::WindowFullScreen | Qt::WindowMinimized); + QTest::newRow("Qt::WindowMaximized|Qt::WindowFullScreen") + << (Qt::WindowMaximized | Qt::WindowFullScreen); + QTest::newRow("Qt::WindowMaximized|Qt::WindowFullScreen|Qt::WindowMinimized") + << (Qt::WindowMaximized | Qt::WindowFullScreen | Qt::WindowMinimized); +} + +void tst_QWidget_window::setWindowState() +{ + QFETCH(Qt::WindowStates, state); + + // This tests make sure that the states are preserved when the window is shown. + + QWidget w; + w.setWindowState(state); + QCOMPARE(w.windowState(), state); + w.show(); + QCOMPARE(w.windowState(), state); + QCOMPARE(w.windowHandle()->windowStates(), state); + QTest::qWaitForWindowExposed(&w); + QTRY_COMPARE(w.windowState(), state); + QCOMPARE(w.windowHandle()->windowStates(), state); + + // Minimizing keeps other states + w.showMinimized(); + QCOMPARE(w.windowState(), state | Qt::WindowMinimized); + QTest::qWait(100); + QCOMPARE(w.windowState(), state | Qt::WindowMinimized); + QCOMPARE(w.windowHandle()->windowStates(), state | Qt::WindowMinimized); +} + +void tst_QWidget_window::nativeShow() +{ + // Verify that a native widget can be shown using the QWindow::setVisible() API + QWidget w; + w.winId(); + w.windowHandle()->setVisible(true); + QTest::qWaitForWindowExposed(&w); + QVERIFY(w.isVisible()); + + // ... and that we can hide it + w.windowHandle()->setVisible(false); + QTRY_VERIFY(!w.isVisible()); +} + QTEST_MAIN(tst_QWidget_window) #include "tst_qwidget_window.moc" diff --git a/tests/auto/widgets/styles/qmacstyle/qmacstyle.pro b/tests/auto/widgets/styles/qmacstyle/qmacstyle.pro index 4cd85ab7f2..90bf134ef1 100644 --- a/tests/auto/widgets/styles/qmacstyle/qmacstyle.pro +++ b/tests/auto/widgets/styles/qmacstyle/qmacstyle.pro @@ -1,4 +1,4 @@ CONFIG += testcase TARGET = tst_qmacstyle -QT += widgets testlib +QT += widgets-private testlib SOURCES += tst_qmacstyle.cpp diff --git a/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp b/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp index fbd1505b30..2ab2fa4c86 100644 --- a/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp +++ b/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp @@ -29,12 +29,11 @@ #include <QtTest/QtTest> #include <QtWidgets> +#include <private/qstylehelper_p.h> const int N = 1; -enum Size { Normal, Small, Mini }; - -Q_DECLARE_METATYPE(Size); +Q_DECLARE_METATYPE(QStyleHelper::WidgetSizePolicy); #define CT(E) \ static const ControlType E = QSizePolicy::E; @@ -63,7 +62,7 @@ class tst_QMacStyle : public QObject Q_OBJECT public: - tst_QMacStyle() { qRegisterMetaType<Size>("Size"); } + tst_QMacStyle() { qRegisterMetaType<QStyleHelper::WidgetSizePolicy>("WidgetSizePolicy"); } private slots: void sizeHints_data(); @@ -85,27 +84,27 @@ private: static QSize gap(QWidget *widget1, QWidget *widget2); static int hgap(QWidget *widget1, QWidget *widget2) { return gap(widget1, widget2).width(); } static int vgap(QWidget *widget1, QWidget *widget2) { return gap(widget1, widget2).height(); } - static void setSize(QWidget *widget, Size size); + static void setSize(QWidget *widget, QStyleHelper::WidgetSizePolicy size); static int spacing(ControlType control1, ControlType control2, Qt::Orientation orientation, QStyleOption *option = 0, QWidget *widget = 0); - static int hspacing(ControlType control1, ControlType control2, Size size = Normal); - static int vspacing(ControlType control1, ControlType control2, Size size = Normal); + static int hspacing(ControlType control1, ControlType control2, QStyleHelper::WidgetSizePolicy size = QStyleHelper::SizeLarge); + static int vspacing(ControlType control1, ControlType control2, QStyleHelper::WidgetSizePolicy size = QStyleHelper::SizeLarge); }; #define SIZE(x, y, z) \ - ((size == Normal) ? (x) : (size == Small) ? (y) : (z)) + ((size == QStyleHelper::SizeLarge) ? (x) : (size == QStyleHelper::SizeSmall) ? (y) : (z)) void tst_QMacStyle::sizeHints_data() { - QTest::addColumn<Size>("size"); - QTest::newRow("normal") << Normal; -// QTest::newRow("small") << Small; -// QTest::newRow("mini") << Mini; + QTest::addColumn<QStyleHelper::WidgetSizePolicy>("size"); + QTest::newRow("normal") << QStyleHelper::SizeLarge; +// QTest::newRow("small") << QStyleHelper::SizeSmall; +// QTest::newRow("mini") << QStyleHelper::SizeMini; } void tst_QMacStyle::sizeHints() { - QFETCH(Size, size); + QFETCH(QStyleHelper::WidgetSizePolicy, size); QDialog w; setSize(&w, size); @@ -160,7 +159,7 @@ void tst_QMacStyle::sizeHints() QPushButton cancel1("Cancel", &w); QSize s1 = sh(&ok1); - if (size == Normal) { + if (size == QStyleHelper::SizeLarge) { // AHIG says 68, Builder does 70, and Qt seems to do 69 QVERIFY(s1.width() >= 68 && s1.width() <= 70); } @@ -222,7 +221,7 @@ void tst_QMacStyle::layoutMargins_data() void tst_QMacStyle::layoutMargins() { - QFETCH(Size, size); + QFETCH(QStyleHelper::WidgetSizePolicy, size); QWidget w; setSize(&w, size); @@ -235,7 +234,7 @@ void tst_QMacStyle::layoutSpacings_data() void tst_QMacStyle::layoutSpacings() { - QFETCH(Size, size); + QFETCH(QStyleHelper::WidgetSizePolicy, size); /* Constraints specified by AHIG. @@ -304,16 +303,16 @@ QSize tst_QMacStyle::gap(QWidget *widget1, QWidget *widget2) return s + QSize(d.x(), d.y()); } -void tst_QMacStyle::setSize(QWidget *widget, Size size) +void tst_QMacStyle::setSize(QWidget *widget, QStyleHelper::WidgetSizePolicy size) { switch (size) { - case Normal: + case QStyleHelper::SizeLarge: widget->setAttribute(Qt::WA_MacNormalSize, true); break; - case Small: + case QStyleHelper::SizeSmall: widget->setAttribute(Qt::WA_MacSmallSize, true); break; - case Mini: + case QStyleHelper::SizeMini: widget->setAttribute(Qt::WA_MacMiniSize, true); } } @@ -324,7 +323,7 @@ int tst_QMacStyle::spacing(ControlType control1, ControlType control2, Qt::Orien return QApplication::style()->layoutSpacing(control1, control2, orientation, option, widget); } -int tst_QMacStyle::hspacing(ControlType control1, ControlType control2, Size size) +int tst_QMacStyle::hspacing(ControlType control1, ControlType control2, QStyleHelper::WidgetSizePolicy size) { QWidget w; setSize(&w, size); @@ -335,7 +334,7 @@ int tst_QMacStyle::hspacing(ControlType control1, ControlType control2, Size siz return spacing(control1, control2, Qt::Horizontal, &opt); } -int tst_QMacStyle::vspacing(ControlType control1, ControlType control2, Size size) +int tst_QMacStyle::vspacing(ControlType control1, ControlType control2, QStyleHelper::WidgetSizePolicy size) { QWidget w; setSize(&w, size); diff --git a/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp b/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp index 5925b764dd..8422fe2439 100644 --- a/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp +++ b/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp @@ -86,9 +86,6 @@ private slots: void testFusionStyle(); #endif void testWindowsStyle(); -#if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSXP) - void testWindowsXPStyle(); -#endif #if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSVISTA) void testWindowsVistaStyle(); #endif @@ -146,14 +143,6 @@ void tst_QStyle::testStyleFactory() #ifndef QT_NO_STYLE_WINDOWS QVERIFY(keys.contains("Windows")); #endif -#ifdef Q_OS_WIN - if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && - (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) - QVERIFY(keys.contains("WindowsXP")); - if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && - (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) - QVERIFY(keys.contains("WindowsVista")); -#endif foreach (QString styleName , keys) { QStyle *style = QStyleFactory::create(styleName); @@ -340,17 +329,6 @@ void tst_QStyle::testWindowsStyle() delete wstyle; } -#if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSXP) -// WindowsXP style -void tst_QStyle::testWindowsXPStyle() -{ - QStyle *xpstyle = QStyleFactory::create("WindowsXP"); - QVERIFY(testAllFunctions(xpstyle)); - lineUpLayoutTest(xpstyle); - delete xpstyle; -} -#endif - void writeImage(const QString &fileName, QImage image) { QImageWriter imageWriter(fileName); @@ -373,8 +351,6 @@ void tst_QStyle::testWindowsVistaStyle() if (QSysInfo::WindowsVersion == QSysInfo::WV_VISTA) testPainting(vistastyle, "vista"); - else if (QSysInfo::WindowsVersion == QSysInfo::WV_XP) - testPainting(vistastyle, "xp"); delete vistastyle; } #endif diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index efcc47171d..8b6c4722be 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -8,4 +8,3 @@ SUBDIRS=\ styles \ util \ widgets \ - gestures \ diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index bf190cf60a..2f4e1a32f3 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -168,6 +168,7 @@ private slots: void task_QTBUG_49831_scrollerNotActivated(); void task_QTBUG_56693_itemFontFromModel(); void inputMethodUpdate(); + void task_QTBUG_52027_mapCompleterIndex(); private: PlatformInputContext m_platformInputContext; @@ -3399,5 +3400,62 @@ void tst_QComboBox::inputMethodUpdate() QVERIFY(m_platformInputContext.m_updateCallCount >= 1); } +void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() +{ + QStringList words; + words << "" << "foobar1" << "foobar2"; + + QStringList altWords; + altWords << "foobar2" << "hello" << "," << "world" << "" << "foobar0" << "foobar1"; + + QComboBox cbox; + setFrameless(&cbox); + cbox.setEditable(true); + cbox.setInsertPolicy(QComboBox::NoInsert); + cbox.addItems(words); + + QCompleter *completer = new QCompleter(altWords); + completer->setCaseSensitivity(Qt::CaseInsensitive); + cbox.setCompleter(completer); + + QSignalSpy spy(&cbox, SIGNAL(activated(int))); + QCOMPARE(spy.count(), 0); + cbox.move(200, 200); + cbox.show(); + QApplication::setActiveWindow(&cbox); + QVERIFY(QTest::qWaitForWindowActive(&cbox)); + + QTest::keyClicks(&cbox, "foobar2"); + QApplication::processEvents(); + QTRY_VERIFY(completer->popup()); + QTest::keyClick(completer->popup(), Qt::Key_Down); + QApplication::processEvents(); + QTest::keyClick(completer->popup(), Qt::Key_Return); + QApplication::processEvents(); + QList<QVariant> arguments = spy.takeLast(); + QCOMPARE(arguments.at(0).toInt(), 2); + + cbox.lineEdit()->selectAll(); + cbox.lineEdit()->del(); + + QSortFilterProxyModel* model = new QSortFilterProxyModel(); + model->setSourceModel(cbox.model()); + model->setFilterFixedString("foobar1"); + completer->setModel(model); + + QApplication::setActiveWindow(&cbox); + QVERIFY(QTest::qWaitForWindowActive(&cbox)); + + QTest::keyClicks(&cbox, "foobar1"); + QApplication::processEvents(); + QTRY_VERIFY(completer->popup()); + QTest::keyClick(completer->popup(), Qt::Key_Down); + QApplication::processEvents(); + QTest::keyClick(completer->popup(), Qt::Key_Return); + QApplication::processEvents(); + arguments = spy.takeLast(); + QCOMPARE(arguments.at(0).toInt(), 1); +} + QTEST_MAIN(tst_QComboBox) #include "tst_qcombobox.moc" diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_0.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_0.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_1.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_1.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_2.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_2.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_0.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_0.png Binary files differindex 0cc9090e3b..84be445138 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_1.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_1.png Binary files differindex 0cc9090e3b..84be445138 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_2.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_2.png Binary files differindex 0cc9090e3b..84be445138 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_0.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_1.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_2.png b/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_noshadow_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_0_0.png b/tests/auto/widgets/widgets/qframe/images/box_plain_0_0.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_0_1.png b/tests/auto/widgets/widgets/qframe/images/box_plain_0_1.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_0_2.png b/tests/auto/widgets/widgets/qframe/images/box_plain_0_2.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_1_0.png b/tests/auto/widgets/widgets/qframe/images/box_plain_1_0.png Binary files differindex 0cc9090e3b..84be445138 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_1_1.png b/tests/auto/widgets/widgets/qframe/images/box_plain_1_1.png Binary files differindex 0cc9090e3b..84be445138 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_1_2.png b/tests/auto/widgets/widgets/qframe/images/box_plain_1_2.png Binary files differindex 0cc9090e3b..84be445138 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_2_0.png b/tests/auto/widgets/widgets/qframe/images/box_plain_2_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_2_1.png b/tests/auto/widgets/widgets/qframe/images/box_plain_2_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_plain_2_2.png b/tests/auto/widgets/widgets/qframe/images/box_plain_2_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_plain_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_plain_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_0_0.png b/tests/auto/widgets/widgets/qframe/images/box_raised_0_0.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_0_1.png b/tests/auto/widgets/widgets/qframe/images/box_raised_0_1.png Binary files differindex b2ab941d30..e35498674c 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_0_2.png b/tests/auto/widgets/widgets/qframe/images/box_raised_0_2.png Binary files differindex 21ebe53eff..b958b62259 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_1_0.png b/tests/auto/widgets/widgets/qframe/images/box_raised_1_0.png Binary files differindex 4f097cee48..879f6ddc31 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_1_1.png b/tests/auto/widgets/widgets/qframe/images/box_raised_1_1.png Binary files differindex 86f9ed6186..343caa06e4 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_1_2.png b/tests/auto/widgets/widgets/qframe/images/box_raised_1_2.png Binary files differindex 51a7899d05..377e1377f2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_2_0.png b/tests/auto/widgets/widgets/qframe/images/box_raised_2_0.png Binary files differindex 8c3769c32d..d59309bb3e 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_2_1.png b/tests/auto/widgets/widgets/qframe/images/box_raised_2_1.png Binary files differindex 81e0c2a152..36f6c0a9f3 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_raised_2_2.png b/tests/auto/widgets/widgets/qframe/images/box_raised_2_2.png Binary files differindex 107272d0b9..c6bdab7eb4 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_raised_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_raised_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_0_0.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_0_0.png Binary files differindex f1d6392779..2647ac98b2 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_0_1.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_0_1.png Binary files differindex b2ab941d30..e35498674c 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_0_2.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_0_2.png Binary files differindex 21ebe53eff..b958b62259 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_1_0.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_1_0.png Binary files differindex 6905baa1fd..7e57872d19 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_1_1.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_1_1.png Binary files differindex 9d5440b3aa..8bf9170f89 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_1_2.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_1_2.png Binary files differindex 17eb7f59c0..bb34a4db29 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_2_0.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_2_0.png Binary files differindex a6ccd54a39..4aa344ad54 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_2_1.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_2_1.png Binary files differindex 3736bf6c99..da5f2ba6b9 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/box_sunken_2_2.png b/tests/auto/widgets/widgets/qframe/images/box_sunken_2_2.png Binary files differindex 5750751c58..80ce6d846e 100644 --- a/tests/auto/widgets/widgets/qframe/images/box_sunken_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/box_sunken_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_noshadow_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_0.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_1.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_2.png Binary files differindex 61f683df52..8c29cd9965 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_plain_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_0.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_1.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_2.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_0.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_1.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_2.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_0.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_1.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_2.png Binary files differindex 00447760ec..a28358ec55 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_raised_2_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_0.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_1.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_2.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_0_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_0.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_1.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_2.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_1_2.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_0.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_0.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_0.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_0.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_1.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_1.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_1.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_1.png diff --git a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_2.png b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_2.png Binary files differindex 4c809a2c80..b5ea56f422 100644 --- a/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_2.png +++ b/tests/auto/widgets/widgets/qframe/images/winpanel_sunken_2_2.png diff --git a/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp b/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp index 62224217d6..420ef56106 100644 --- a/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +++ b/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp @@ -67,7 +67,7 @@ private slots: void toggledVsClicked(); void childrenAreDisabled(); void propagateFocus(); - void task_QTBUG_19170_ignoreMouseReleseEvent(); + void task_QTBUG_19170_ignoreMouseReleaseEvent(); void task_QTBUG_15519_propagateMouseEvents(); private: @@ -477,7 +477,7 @@ void tst_QGroupBox::propagateFocus() QTRY_COMPARE(qApp->focusWidget(), static_cast<QWidget*>(&lineEdit)); } -void tst_QGroupBox::task_QTBUG_19170_ignoreMouseReleseEvent() +void tst_QGroupBox::task_QTBUG_19170_ignoreMouseReleaseEvent() { QGroupBox box; box.setCheckable(true); diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index f0fb7bc367..da37a9a968 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -126,6 +126,8 @@ private slots: #ifdef Q_OS_MAC void QTBUG_37933_ampersands_data(); void QTBUG_37933_ampersands(); +#else + void click_while_dismissing_submenu(); #endif void QTBUG_56917_wideMenuSize(); void QTBUG_56917_wideMenuScreenNumber(); @@ -1171,6 +1173,39 @@ void tst_QMenu::QTBUG20403_nested_popup_on_shortcut_trigger() QVERIFY(!subsub1.isVisible()); } +#ifndef Q_OS_MACOS +void tst_QMenu::click_while_dismissing_submenu() +{ + QMenu menu("Test Menu"); + QAction *action = menu.addAction("action"); + QMenu sub("&sub"); + sub.addAction("subaction"); + menu.addMenu(&sub); + centerOnScreen(&menu, QSize(120, 100)); + menu.show(); + QSignalSpy spy(action, SIGNAL(triggered())); + QSignalSpy menuShownSpy(&sub, SIGNAL(aboutToShow())); + QSignalSpy menuHiddenSpy(&sub, SIGNAL(aboutToHide())); + QVERIFY(QTest::qWaitForWindowExposed(&menu)); + //go over the submenu, press, move and release over the top level action + //this opens the submenu, move two times to emulate user interaction (d->motions > 0 in QMenu) + QTest::mouseMove(&menu, menu.rect().center() + QPoint(0,2)); + QTest::mouseMove(&menu, menu.rect().center() + QPoint(1,3), 60); + QVERIFY(menuShownSpy.wait()); + QVERIFY(sub.isVisible()); + QVERIFY(QTest::qWaitForWindowExposed(&sub)); + //press over the submenu entry + QTest::mousePress(&menu, Qt::LeftButton, 0, menu.rect().center() + QPoint(0,2), 300); + //move over the main action + QTest::mouseMove(&menu, menu.rect().center() - QPoint(0,2)); + QVERIFY(menuHiddenSpy.wait()); + //the submenu must have been hidden for the bug to be triggered + QVERIFY(!sub.isVisible()); + QTest::mouseRelease(&menu, Qt::LeftButton, 0, menu.rect().center() - QPoint(0,2), 300); + QCOMPARE(spy.count(), 1); +} +#endif + class MyWidget : public QWidget { public: diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 2a05900adc..e10c7591d5 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -62,6 +62,8 @@ private slots: void showHide(); void nativeWindow(); void stackWidgetOpaqueChildIsVisible(); + void offscreen(); + void offscreenThenOnscreen(); }; void tst_QOpenGLWidget::initTestCase() @@ -75,6 +77,7 @@ void tst_QOpenGLWidget::create() { QScopedPointer<QOpenGLWidget> w(new QOpenGLWidget); QVERIFY(!w->isValid()); + QVERIFY(w->textureFormat() == 0); QSignalSpy frameSwappedSpy(w.data(), SIGNAL(frameSwapped())); w->show(); QTest::qWaitForWindowExposed(w.data()); @@ -84,6 +87,7 @@ void tst_QOpenGLWidget::create() QVERIFY(w->context()); QCOMPARE(w->context()->format(), w->format()); QVERIFY(w->defaultFramebufferObject() != 0); + QVERIFY(w->textureFormat() != 0); } class ClearWidget : public QOpenGLWidget, protected QOpenGLFunctions @@ -603,6 +607,74 @@ void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() #undef VERIFY_COLOR } +void tst_QOpenGLWidget::offscreen() +{ + { + QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QImage image = w->grabFramebuffer(); + + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + // QWidget::grab() should eventually end up in grabFramebuffer() as well + { + QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QPixmap pm = w->grab(); + QImage image = pm.toImage(); + + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + // ditto for QWidget::render() + { + QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QImage image(800, 600, QImage::Format_ARGB32); + w->render(&image); + + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } +} + +void tst_QOpenGLWidget::offscreenThenOnscreen() +{ + QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QImage image = w->grabFramebuffer(); + + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + + // now let's make things more challenging: show. Internally this needs + // recreating the context. + w->show(); + QTest::qWaitForWindowExposed(w.data()); + + image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); +} + QTEST_MAIN(tst_QOpenGLWidget) #include "tst_qopenglwidget.moc" diff --git a/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp b/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp index 205ef34958..9f64335930 100644 --- a/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp +++ b/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp @@ -45,6 +45,7 @@ private slots: void destroyIndeterminate(); void text(); void format(); + void setValueRepaint_data(); void setValueRepaint(); #ifndef Q_OS_MAC void setMinMaxRepaint(); @@ -191,21 +192,49 @@ void tst_QProgressBar::format() QCOMPARE(bar.text(), QString()); } +void tst_QProgressBar::setValueRepaint_data() +{ + QTest::addColumn<int>("min"); + QTest::addColumn<int>("max"); + QTest::addColumn<int>("increment"); + + auto add = [](int min, int max, int increment) { + QTest::addRow("%d-%d@%d", min, max, increment) << min << max << increment; + }; + + add(0, 10, 1); + + auto add_set = [=](int min, int max, int increment) { + // check corner cases, and adjacent values (to circumvent explicit checks for corner cases) + add(min, max, increment); + add(min + 1, max, increment); + add(min, max - 1, increment); + add(min + 1, max - 1, increment); + }; + + add_set(INT_MIN, INT_MAX, INT_MAX / 50 + 1); + add_set(0, INT_MAX, INT_MAX / 100 + 1); + add_set(INT_MIN, 0, INT_MAX / 100 + 1); +} + void tst_QProgressBar::setValueRepaint() { + QFETCH(int, min); + QFETCH(int, max); + QFETCH(int, increment); + ProgressBar pbar; - pbar.setMinimum(0); - pbar.setMaximum(10); + pbar.setMinimum(min); + pbar.setMaximum(max); pbar.setFormat("%v"); pbar.move(300, 300); pbar.show(); QVERIFY(QTest::qWaitForWindowExposed(&pbar)); QApplication::processEvents(); - for (int i = pbar.minimum(); i < pbar.maximum(); ++i) { + for (qint64 i = min; i < max; i += increment) { pbar.repainted = false; - pbar.setValue(i); - QTest::qWait(50); + pbar.setValue(int(i)); QTRY_VERIFY(pbar.repainted); } } diff --git a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp index 7bbbc46b5a..bacab78601 100644 --- a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +++ b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp @@ -520,9 +520,6 @@ void tst_QPushButton::sizeHint_data() #if !defined(QT_NO_STYLE_FUSION) QTest::newRow("fusion") << QString::fromLatin1("fusion"); #endif -#if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSXP) - QTest::newRow("windowsxp") << QString::fromLatin1("windowsxp"); -#endif #if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSVISTA) QTest::newRow("windowsvista") << QString::fromLatin1("windowsvista"); #endif diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index a9d27fa488..ab00a5ef60 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -67,3 +67,5 @@ win32: SUBDIRS -= network_remote_stresstest network_stresstest lessThan(QT_MAJOR_VERSION, 5): SUBDIRS -= bearerex lance qnetworkaccessmanager/qget qmimedatabase qnetworkreply \ qpainfo qscreen socketengine xembed-raster xembed-widgets windowtransparency \ embeddedintoforeignwindow foreignwindows + +qtConfig(vulkan): SUBDIRS += qvulkaninstance diff --git a/tests/manual/qvulkaninstance/main.cpp b/tests/manual/qvulkaninstance/main.cpp new file mode 100644 index 0000000000..9b5f0ad072 --- /dev/null +++ b/tests/manual/qvulkaninstance/main.cpp @@ -0,0 +1,713 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 <QGuiApplication> +#include <QVulkanInstance> +#include <QVulkanFunctions> +#include <QWindow> +#include <QLoggingCategory> +#include <qevent.h> + +static const int SWAPCHAIN_BUFFER_COUNT = 2; +static const int FRAME_LAG = 2; + +class VWindow : public QWindow +{ +public: + VWindow() { setSurfaceType(VulkanSurface); } + ~VWindow() { releaseResources(); } + +private: + void exposeEvent(QExposeEvent *) override; + void resizeEvent(QResizeEvent *) override; + bool event(QEvent *) override; + + void init(); + void releaseResources(); + void recreateSwapChain(); + void createDefaultRenderPass(); + void releaseSwapChain(); + void render(); + void buildDrawCalls(); + + bool m_inited = false; + VkSurfaceKHR m_vkSurface; + VkPhysicalDevice m_vkPhysDev; + VkPhysicalDeviceProperties m_physDevProps; + VkDevice m_vkDev = 0; + QVulkanDeviceFunctions *m_devFuncs; + VkQueue m_vkGfxQueue; + VkQueue m_vkPresQueue; + VkCommandPool m_vkCmdPool = 0; + + PFN_vkCreateSwapchainKHR m_vkCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR m_vkDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR m_vkGetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR m_vkAcquireNextImageKHR; + PFN_vkQueuePresentKHR m_vkQueuePresentKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR m_vkGetPhysicalDeviceSurfaceFormatsKHR; + + QSize m_swapChainImageSize; + VkFormat m_colorFormat; + VkSwapchainKHR m_swapChain = 0; + uint32_t m_swapChainBufferCount = 0; + + struct ImageResources { + VkImage image = 0; + VkImageView imageView = 0; + VkCommandBuffer cmdBuf = 0; + VkFence cmdFence = 0; + bool cmdFenceWaitable = false; + VkFramebuffer fb = 0; + } m_imageRes[SWAPCHAIN_BUFFER_COUNT]; + + uint32_t m_currentImage; + + struct FrameResources { + VkFence fence = 0; + bool fenceWaitable = false; + VkSemaphore imageSem = 0; + VkSemaphore drawSem = 0; + } m_frameRes[FRAME_LAG]; + + uint32_t m_currentFrame; + + VkRenderPass m_defaultRenderPass = 0; +}; + +void VWindow::exposeEvent(QExposeEvent *) +{ + if (isExposed() && !m_inited) { + qDebug("initializing"); + m_inited = true; + init(); + recreateSwapChain(); + render(); + } + + // Release everything when unexposed - the meaning of which is platform specific. + // Can be essential on mobile, to release resources while in background. +#if 1 + if (!isExposed() && m_inited) { + m_inited = false; + releaseSwapChain(); + releaseResources(); + } +#endif +} + +void VWindow::resizeEvent(QResizeEvent *) +{ + // Nothing to do here - recreating the swapchain is handled in render(), + // in fact calling recreateSwapChain() from here leads to problems. +} + +bool VWindow::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::UpdateRequest: + render(); + break; + + // Now the fun part: the swapchain must be destroyed before the surface as per + // spec. This is not ideal for us because the surface is managed by the + // QPlatformWindow which may be gone already when the unexpose comes, making the + // validation layer scream. The solution is to listen to the PlatformSurface events. + case QEvent::PlatformSurface: + if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) + releaseSwapChain(); + break; + + default: + break; + } + + return QWindow::event(e); +} + +void VWindow::init() +{ + m_vkSurface = QVulkanInstance::surfaceForWindow(this); + if (!m_vkSurface) + qFatal("Failed to get surface for window"); + + QVulkanInstance *inst = vulkanInstance(); + QVulkanFunctions *f = inst->functions(); + uint32_t devCount = 0; + f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, nullptr); + qDebug("%d physical devices", devCount); + if (!devCount) + qFatal("No physical devices"); + + // Just pick the first physical device for now. + devCount = 1; + VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, &m_vkPhysDev); + if (err != VK_SUCCESS) + qFatal("Failed to enumerate physical devices: %d", err); + + f->vkGetPhysicalDeviceProperties(m_vkPhysDev, &m_physDevProps); + qDebug("Device name: %s Driver version: %d.%d.%d", m_physDevProps.deviceName, + VK_VERSION_MAJOR(m_physDevProps.driverVersion), VK_VERSION_MINOR(m_physDevProps.driverVersion), + VK_VERSION_PATCH(m_physDevProps.driverVersion)); + + uint32_t queueCount = 0; + f->vkGetPhysicalDeviceQueueFamilyProperties(m_vkPhysDev, &queueCount, nullptr); + QVector<VkQueueFamilyProperties> queueFamilyProps(queueCount); + f->vkGetPhysicalDeviceQueueFamilyProperties(m_vkPhysDev, &queueCount, queueFamilyProps.data()); + int gfxQueueFamilyIdx = -1; + int presQueueFamilyIdx = -1; + // First look for a queue that supports both. + for (int i = 0; i < queueFamilyProps.count(); ++i) { + qDebug("queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount); + if (gfxQueueFamilyIdx == -1 + && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + && inst->supportsPresent(m_vkPhysDev, i, this)) + gfxQueueFamilyIdx = i; + } + if (gfxQueueFamilyIdx != -1) { + presQueueFamilyIdx = gfxQueueFamilyIdx; + } else { + // Separate queues then. + qDebug("No queue with graphics+present; trying separate queues"); + for (int i = 0; i < queueFamilyProps.count(); ++i) { + if (gfxQueueFamilyIdx == -1 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) + gfxQueueFamilyIdx = i; + if (presQueueFamilyIdx == -1 && inst->supportsPresent(m_vkPhysDev, i, this)) + presQueueFamilyIdx = i; + } + } + if (gfxQueueFamilyIdx == -1) + qFatal("No graphics queue family found"); + if (presQueueFamilyIdx == -1) + qFatal("No present queue family found"); + + VkDeviceQueueCreateInfo queueInfo[2]; + const float prio[] = { 0 }; + memset(queueInfo, 0, sizeof(queueInfo)); + queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx; + queueInfo[0].queueCount = 1; + queueInfo[0].pQueuePriorities = prio; + if (gfxQueueFamilyIdx != presQueueFamilyIdx) { + queueInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo[1].queueFamilyIndex = presQueueFamilyIdx; + queueInfo[1].queueCount = 1; + queueInfo[1].pQueuePriorities = prio; + } + + QVector<const char *> devLayers; + if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation")) + devLayers.append("VK_LAYER_LUNARG_standard_validation"); + + QVector<const char *> devExts; + devExts.append("VK_KHR_swapchain"); + + VkDeviceCreateInfo devInfo; + memset(&devInfo, 0, sizeof(devInfo)); + devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + devInfo.queueCreateInfoCount = gfxQueueFamilyIdx == presQueueFamilyIdx ? 1 : 2; + devInfo.pQueueCreateInfos = queueInfo; + devInfo.enabledLayerCount = devLayers.count(); + devInfo.ppEnabledLayerNames = devLayers.constData(); + devInfo.enabledExtensionCount = devExts.count(); + devInfo.ppEnabledExtensionNames = devExts.constData(); + + err = f->vkCreateDevice(m_vkPhysDev, &devInfo, nullptr, &m_vkDev); + if (err != VK_SUCCESS) + qFatal("Failed to create device: %d", err); + + m_devFuncs = inst->deviceFunctions(m_vkDev); + + m_devFuncs->vkGetDeviceQueue(m_vkDev, gfxQueueFamilyIdx, 0, &m_vkGfxQueue); + if (gfxQueueFamilyIdx == presQueueFamilyIdx) + m_vkPresQueue = m_vkGfxQueue; + else + m_devFuncs->vkGetDeviceQueue(m_vkDev, presQueueFamilyIdx, 0, &m_vkPresQueue); + + VkCommandPoolCreateInfo poolInfo; + memset(&poolInfo, 0, sizeof(poolInfo)); + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = gfxQueueFamilyIdx; + err = m_devFuncs->vkCreateCommandPool(m_vkDev, &poolInfo, nullptr, &m_vkCmdPool); + if (err != VK_SUCCESS) + qFatal("Failed to create command pool: %d", err); + + m_colorFormat = VK_FORMAT_B8G8R8A8_UNORM; // may get changed later when setting up the swapchain +} + +void VWindow::releaseResources() +{ + if (!m_vkDev) + return; + + m_devFuncs->vkDeviceWaitIdle(m_vkDev); + + if (m_vkCmdPool) { + m_devFuncs->vkDestroyCommandPool(m_vkDev, m_vkCmdPool, nullptr); + m_vkCmdPool = 0; + } + + if (m_vkDev) { + m_devFuncs->vkDestroyDevice(m_vkDev, nullptr); + + // Play nice and notify QVulkanInstance that the QVulkanDeviceFunctions + // for m_vkDev needs to be invalidated. + vulkanInstance()->resetDeviceFunctions(m_vkDev); + + m_vkDev = 0; + } + + m_vkSurface = 0; +} + +void VWindow::recreateSwapChain() +{ + m_swapChainImageSize = size(); + + if (m_swapChainImageSize.isEmpty()) + return; + + QVulkanInstance *inst = vulkanInstance(); + QVulkanFunctions *f = inst->functions(); + m_devFuncs->vkDeviceWaitIdle(m_vkDev); + + if (!m_vkCreateSwapchainKHR) { + m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>( + inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); + m_vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>( + inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR")); + // note: device-specific functions + m_vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkCreateSwapchainKHR")); + m_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkDestroySwapchainKHR")); + m_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkGetSwapchainImagesKHR")); + m_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkAcquireNextImageKHR")); + m_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(m_vkDev, "vkQueuePresentKHR")); + } + + VkColorSpaceKHR colorSpace = VkColorSpaceKHR(0); + uint32_t formatCount = 0; + m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_vkPhysDev, m_vkSurface, &formatCount, nullptr); + if (formatCount) { + QVector<VkSurfaceFormatKHR> formats(formatCount); + m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_vkPhysDev, m_vkSurface, &formatCount, formats.data()); + if (formats[0].format != VK_FORMAT_UNDEFINED) { + m_colorFormat = formats[0].format; + colorSpace = formats[0].colorSpace; + } + } + + VkSurfaceCapabilitiesKHR surfaceCaps; + m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_vkPhysDev, m_vkSurface, &surfaceCaps); + uint32_t reqBufferCount = SWAPCHAIN_BUFFER_COUNT; + if (surfaceCaps.maxImageCount) + reqBufferCount = qBound(surfaceCaps.minImageCount, reqBufferCount, surfaceCaps.maxImageCount); + + VkExtent2D bufferSize = surfaceCaps.currentExtent; + if (bufferSize.width == uint32_t(-1)) + bufferSize.width = m_swapChainImageSize.width(); + if (bufferSize.height == uint32_t(-1)) + bufferSize.height = m_swapChainImageSize.height(); + + VkSurfaceTransformFlagBitsKHR preTransform = + (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR + : surfaceCaps.currentTransform; + + VkCompositeAlphaFlagBitsKHR compositeAlpha = + (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) + ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR + : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + + VkSwapchainKHR oldSwapChain = m_swapChain; + VkSwapchainCreateInfoKHR swapChainInfo; + memset(&swapChainInfo, 0, sizeof(swapChainInfo)); + swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapChainInfo.surface = m_vkSurface; + swapChainInfo.minImageCount = reqBufferCount; + swapChainInfo.imageFormat = m_colorFormat; + swapChainInfo.imageColorSpace = colorSpace; + swapChainInfo.imageExtent = bufferSize; + swapChainInfo.imageArrayLayers = 1; + swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapChainInfo.preTransform = preTransform; + swapChainInfo.compositeAlpha = compositeAlpha; + swapChainInfo.presentMode = presentMode; + swapChainInfo.clipped = true; + swapChainInfo.oldSwapchain = oldSwapChain; + + qDebug("creating new swap chain of %d buffers, size %dx%d", reqBufferCount, bufferSize.width, bufferSize.height); + + VkSwapchainKHR newSwapChain; + VkResult err = m_vkCreateSwapchainKHR(m_vkDev, &swapChainInfo, nullptr, &newSwapChain); + if (err != VK_SUCCESS) + qFatal("Failed to create swap chain: %d", err); + + if (oldSwapChain) + releaseSwapChain(); + + m_swapChain = newSwapChain; + + m_swapChainBufferCount = 0; + err = m_vkGetSwapchainImagesKHR(m_vkDev, m_swapChain, &m_swapChainBufferCount, nullptr); + if (err != VK_SUCCESS || m_swapChainBufferCount < 2) + qFatal("Failed to get swapchain images: %d (count=%d)", err, m_swapChainBufferCount); + + qDebug("actual swap chain buffer count: %d", m_swapChainBufferCount); + Q_ASSERT(m_swapChainBufferCount <= SWAPCHAIN_BUFFER_COUNT); + + VkImage swapChainImages[SWAPCHAIN_BUFFER_COUNT]; + err = m_vkGetSwapchainImagesKHR(m_vkDev, m_swapChain, &m_swapChainBufferCount, swapChainImages); + if (err != VK_SUCCESS) + qFatal("Failed to get swapchain images: %d", err); + + // Now that we know m_colorFormat, create the default renderpass, the framebuffers will need it. + createDefaultRenderPass(); + + VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT }; + + for (uint32_t i = 0; i < m_swapChainBufferCount; ++i) { + ImageResources &image(m_imageRes[i]); + image.image = swapChainImages[i]; + + VkImageViewCreateInfo imgViewInfo; + memset(&imgViewInfo, 0, sizeof(imgViewInfo)); + imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imgViewInfo.image = swapChainImages[i]; + imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imgViewInfo.format = m_colorFormat; + imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1; + err = m_devFuncs->vkCreateImageView(m_vkDev, &imgViewInfo, nullptr, &image.imageView); + if (err != VK_SUCCESS) + qFatal("Failed to create swapchain image view %d: %d", i, err); + + err = m_devFuncs->vkCreateFence(m_vkDev, &fenceInfo, nullptr, &image.cmdFence); + if (err != VK_SUCCESS) + qFatal("Failed to create command buffer fence: %d", err); + image.cmdFenceWaitable = true; + + VkImageView views[1] = { image.imageView }; + VkFramebufferCreateInfo fbInfo; + memset(&fbInfo, 0, sizeof(fbInfo)); + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = m_defaultRenderPass; + fbInfo.attachmentCount = 1; + fbInfo.pAttachments = views; + fbInfo.width = m_swapChainImageSize.width(); + fbInfo.height = m_swapChainImageSize.height(); + fbInfo.layers = 1; + VkResult err = m_devFuncs->vkCreateFramebuffer(m_vkDev, &fbInfo, nullptr, &image.fb); + if (err != VK_SUCCESS) + qFatal("Failed to create framebuffer: %d", err); + } + + m_currentImage = 0; + + VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; + for (uint32_t i = 0; i < FRAME_LAG; ++i) { + FrameResources &frame(m_frameRes[i]); + m_devFuncs->vkCreateFence(m_vkDev, &fenceInfo, nullptr, &frame.fence); + frame.fenceWaitable = true; + m_devFuncs->vkCreateSemaphore(m_vkDev, &semInfo, nullptr, &frame.imageSem); + m_devFuncs->vkCreateSemaphore(m_vkDev, &semInfo, nullptr, &frame.drawSem); + } + + m_currentFrame = 0; +} + +void VWindow::createDefaultRenderPass() +{ + VkAttachmentDescription attDesc[1]; + memset(attDesc, 0, sizeof(attDesc)); + attDesc[0].format = m_colorFormat; + attDesc[0].samples = VK_SAMPLE_COUNT_1_BIT; + attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subPassDesc; + memset(&subPassDesc, 0, sizeof(subPassDesc)); + subPassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subPassDesc.colorAttachmentCount = 1; + subPassDesc.pColorAttachments = &colorRef; + + VkRenderPassCreateInfo rpInfo; + memset(&rpInfo, 0, sizeof(rpInfo)); + rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpInfo.attachmentCount = 1; + rpInfo.pAttachments = attDesc; + rpInfo.subpassCount = 1; + rpInfo.pSubpasses = &subPassDesc; + VkResult err = m_devFuncs->vkCreateRenderPass(m_vkDev, &rpInfo, nullptr, &m_defaultRenderPass); + if (err != VK_SUCCESS) + qFatal("Failed to create renderpass: %d", err); +} + +void VWindow::releaseSwapChain() +{ + if (!m_vkDev) + return; + + m_devFuncs->vkDeviceWaitIdle(m_vkDev); + + if (m_defaultRenderPass) { + m_devFuncs->vkDestroyRenderPass(m_vkDev, m_defaultRenderPass, nullptr); + m_defaultRenderPass = 0; + } + + for (uint32_t i = 0; i < FRAME_LAG; ++i) { + FrameResources &frame(m_frameRes[i]); + if (frame.fence) { + if (frame.fenceWaitable) + m_devFuncs->vkWaitForFences(m_vkDev, 1, &frame.fence, VK_TRUE, UINT64_MAX); + m_devFuncs->vkDestroyFence(m_vkDev, frame.fence, nullptr); + frame.fence = 0; + frame.fenceWaitable = false; + } + if (frame.imageSem) { + m_devFuncs->vkDestroySemaphore(m_vkDev, frame.imageSem, nullptr); + frame.imageSem = 0; + } + if (frame.drawSem) { + m_devFuncs->vkDestroySemaphore(m_vkDev, frame.drawSem, nullptr); + frame.drawSem = 0; + } + } + + for (uint32_t i = 0; i < m_swapChainBufferCount; ++i) { + ImageResources &image(m_imageRes[i]); + if (image.cmdFence) { + if (image.cmdFenceWaitable) + m_devFuncs->vkWaitForFences(m_vkDev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX); + m_devFuncs->vkDestroyFence(m_vkDev, image.cmdFence, nullptr); + image.cmdFence = 0; + image.cmdFenceWaitable = false; + } + if (image.fb) { + m_devFuncs->vkDestroyFramebuffer(m_vkDev, image.fb, nullptr); + image.fb = 0; + } + if (image.imageView) { + m_devFuncs->vkDestroyImageView(m_vkDev, image.imageView, nullptr); + image.imageView = 0; + } + if (image.cmdBuf) { + m_devFuncs->vkFreeCommandBuffers(m_vkDev, m_vkCmdPool, 1, &image.cmdBuf); + image.cmdBuf = 0; + } + } + + if (m_swapChain) { + m_vkDestroySwapchainKHR(m_vkDev, m_swapChain, nullptr); + m_swapChain = 0; + } +} + +void VWindow::render() +{ + if (!m_swapChain) + return; + + if (size() != m_swapChainImageSize) { + recreateSwapChain(); + if (!m_swapChain) + return; + } + + FrameResources &frame(m_frameRes[m_currentFrame]); + + // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate + // (note that we are using FIFO mode -> vsync) + if (frame.fenceWaitable) { + m_devFuncs->vkWaitForFences(m_vkDev, 1, &frame.fence, VK_TRUE, UINT64_MAX); + m_devFuncs->vkResetFences(m_vkDev, 1, &frame.fence); + } + + // move on to next swapchain image + VkResult err = m_vkAcquireNextImageKHR(m_vkDev, m_swapChain, UINT64_MAX, + frame.imageSem, frame.fence, &m_currentImage); + if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) { + frame.fenceWaitable = true; + } else if (err == VK_ERROR_OUT_OF_DATE_KHR) { + frame.fenceWaitable = false; + recreateSwapChain(); + requestUpdate(); + return; + } else { + qWarning("Failed to acquire next swapchain image: %d", err); + frame.fenceWaitable = false; + requestUpdate(); + return; + } + + // make sure the previous draw for the same image has finished + ImageResources &image(m_imageRes[m_currentImage]); + if (image.cmdFenceWaitable) { + m_devFuncs->vkWaitForFences(m_vkDev, 1, &image.cmdFence, VK_TRUE, UINT64_MAX); + m_devFuncs->vkResetFences(m_vkDev, 1, &image.cmdFence); + } + + // build new draw command buffer + buildDrawCalls(); + + // submit draw calls + VkSubmitInfo submitInfo; + memset(&submitInfo, 0, sizeof(submitInfo)); + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &image.cmdBuf; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &frame.imageSem; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &frame.drawSem; + VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + submitInfo.pWaitDstStageMask = &psf; + + err = m_devFuncs->vkQueueSubmit(m_vkGfxQueue, 1, &submitInfo, image.cmdFence); + if (err == VK_SUCCESS) { + image.cmdFenceWaitable = true; + } else { + qWarning("Failed to submit to command queue: %d", err); + image.cmdFenceWaitable = false; + } + + // queue present + VkPresentInfoKHR presInfo; + memset(&presInfo, 0, sizeof(presInfo)); + presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presInfo.swapchainCount = 1; + presInfo.pSwapchains = &m_swapChain; + presInfo.pImageIndices = &m_currentImage; + presInfo.waitSemaphoreCount = 1; + presInfo.pWaitSemaphores = &frame.drawSem; + + // we do not currently handle the case when the present queue is separate + Q_ASSERT(m_vkGfxQueue == m_vkPresQueue); + + err = m_vkQueuePresentKHR(m_vkGfxQueue, &presInfo); + if (err != VK_SUCCESS) { + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + requestUpdate(); + return; + } else if (err != VK_SUBOPTIMAL_KHR) { + qWarning("Failed to present: %d", err); + } + } + + vulkanInstance()->presentQueued(this); + + m_currentFrame = (m_currentFrame + 1) % FRAME_LAG; + requestUpdate(); +} + +void VWindow::buildDrawCalls() +{ + ImageResources &image(m_imageRes[m_currentImage]); + + if (image.cmdBuf) { + m_devFuncs->vkFreeCommandBuffers(m_vkDev, m_vkCmdPool, 1, &image.cmdBuf); + image.cmdBuf = 0; + } + + VkCommandBufferAllocateInfo cmdBufInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, nullptr, m_vkCmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 }; + VkResult err = m_devFuncs->vkAllocateCommandBuffers(m_vkDev, &cmdBufInfo, &image.cmdBuf); + if (err != VK_SUCCESS) + qFatal("Failed to allocate frame command buffer: %d", err); + + VkCommandBufferBeginInfo cmdBufBeginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, 0, nullptr }; + err = m_devFuncs->vkBeginCommandBuffer(image.cmdBuf, &cmdBufBeginInfo); + if (err != VK_SUCCESS) + qFatal("Failed to begin frame command buffer: %d", err); + + static float g = 0; + g += 0.005f; + if (g > 1.0f) + g = 0.0f; + VkClearColorValue clearColor = { 0.0f, g, 0.0f, 1.0f }; + VkClearValue clearValues[1]; + clearValues[0].color = clearColor; + + VkRenderPassBeginInfo rpBeginInfo; + memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); + rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpBeginInfo.renderPass = m_defaultRenderPass; + rpBeginInfo.framebuffer = image.fb; + rpBeginInfo.renderArea.extent.width = m_swapChainImageSize.width(); + rpBeginInfo.renderArea.extent.height = m_swapChainImageSize.height(); + rpBeginInfo.clearValueCount = 1; + rpBeginInfo.pClearValues = clearValues; + m_devFuncs->vkCmdBeginRenderPass(image.cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + m_devFuncs->vkCmdEndRenderPass(image.cmdBuf); + + err = m_devFuncs->vkEndCommandBuffer(image.cmdBuf); + if (err != VK_SUCCESS) + qFatal("Failed to end frame command buffer: %d", err); +} + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true")); + + QVulkanInstance inst; + // Test the early queries for supported layers/exts. + qDebug() << inst.supportedLayers() << inst.supportedExtensions(); + + // Enable validation layer, if supported. + inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); + + bool ok = inst.create(); + qDebug("QVulkanInstance::create() returned %d", ok); + if (!ok) + return 1; + + VWindow w; + w.setVulkanInstance(&inst); + w.resize(1024, 768); + w.show(); + + return app.exec(); +} diff --git a/tests/manual/qvulkaninstance/qvulkaninstance.pro b/tests/manual/qvulkaninstance/qvulkaninstance.pro new file mode 100644 index 0000000000..7305da53c1 --- /dev/null +++ b/tests/manual/qvulkaninstance/qvulkaninstance.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +TARGET = qvulkaninstance + +QT += gui-private + +SOURCES += main.cpp diff --git a/tests/manual/windowflags/controllerwindow.cpp b/tests/manual/windowflags/controllerwindow.cpp index 9a12c8b2c9..4b380d5355 100644 --- a/tests/manual/windowflags/controllerwindow.cpp +++ b/tests/manual/windowflags/controllerwindow.cpp @@ -59,24 +59,26 @@ ControllerWidget::ControllerWidget(QWidget *parent) QLabel *label = new QLabel(tr("Parent window")); parentWindow->setCentralWidget(label); - previewWindow = new PreviewWindow; + previewWindow = new QWindow; previewWindow->installEventFilter(this); + previewWidget = new PreviewWidget; + previewWidget->installEventFilter(this); previewDialog = new PreviewDialog; previewDialog->installEventFilter(this); createTypeGroupBox(); hintsControl = new HintControl; - hintsControl->setHints(previewWindow->windowFlags()); + hintsControl->setHints(previewWidget->windowFlags()); connect(hintsControl, SIGNAL(changed(Qt::WindowFlags)), this, SLOT(updatePreview())); statesControl = new WindowStatesControl; - statesControl->setStates(previewWindow->windowState()); + statesControl->setStates(previewWidget->windowState()); statesControl->setVisibleValue(true); connect(statesControl, SIGNAL(changed()), this, SLOT(updatePreview())); typeControl = new TypeControl; - typeControl->setType(previewWindow->windowFlags()); + typeControl->setType(previewWidget->windowFlags()); connect(typeControl, SIGNAL(changed(Qt::WindowFlags)), this, SLOT(updatePreview())); QVBoxLayout *mainLayout = new QVBoxLayout(this); @@ -98,61 +100,99 @@ bool ControllerWidget::eventFilter(QObject *, QEvent *e) void ControllerWidget::updateStateControl() { - if (previewWidget) - statesControl->setStates(previewWidget->windowState()); + if (activePreview) + statesControl->setStates(activePreview->windowStates()); } -void ControllerWidget::updatePreview() +void ControllerWidget::updatePreview(QWindow *preview) { - const Qt::WindowFlags flags = typeControl->type() | hintsControl->hints(); + activePreview = preview; - if (previewWidgetButton->isChecked()) { - previewWidget = previewWindow; - previewDialog->hide(); - } else { - previewWidget = previewDialog; - previewWindow->hide(); - } + const Qt::WindowFlags flags = typeControl->type() | hintsControl->hints(); if (modalWindowCheckBox->isChecked()) { parentWindow->show(); - previewWidget->setWindowModality(Qt::WindowModal); - previewWidget->setParent(parentWindow); + preview->setModality(Qt::WindowModal); + preview->setParent(parentWindow->windowHandle()); } else { - previewWidget->setWindowModality(Qt::NonModal); - previewWidget->setParent(0); + preview->setModality(Qt::NonModal); + preview->setParent(0); parentWindow->hide(); } - if (previewWidgetButton->isChecked()) - previewWindow->setWindowFlags(flags); - else - previewDialog->setWindowFlags(flags); + preview->setFlags(flags); if (fixedSizeWindowCheckBox->isChecked()) { - previewWidget->setFixedSize(300, 300); + preview->setMinimumSize(QSize(300, 300)); + preview->setMaximumSize(QSize(300, 300)); } else { - previewWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + preview->setMinimumSize(QSize(0, 0)); + preview->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); } - QPoint pos = previewWidget->pos(); + preview->setWindowStates(statesControl->states()); + preview->setVisible(statesControl->visibleValue()); +} + +void ControllerWidget::updatePreview(QWidget *preview) +{ + activePreview = preview->windowHandle(); + + const Qt::WindowFlags flags = typeControl->type() | hintsControl->hints(); + + if (modalWindowCheckBox->isChecked()) { + parentWindow->show(); + preview->setWindowModality(Qt::WindowModal); + preview->setParent(parentWindow); + } else { + preview->setWindowModality(Qt::NonModal); + preview->setParent(0); + parentWindow->hide(); + } + + preview->setWindowFlags(flags); + + QSize fixedSize = fixedSizeWindowCheckBox->isChecked() ? + QSize(300, 300) : QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + preview->setFixedSize(fixedSize); + + QPoint pos = preview->pos(); if (pos.x() < 0) pos.setX(0); if (pos.y() < 0) pos.setY(0); - previewWidget->move(pos); + preview->move(pos); + + preview->setWindowState(statesControl->states()); + preview->setVisible(statesControl->visibleValue()); +} - previewWidget->setWindowState(statesControl->states()); - previewWidget->setVisible(statesControl->visibleValue()); +void ControllerWidget::updatePreview() +{ + if (previewWindowButton->isChecked()) { + previewDialog->hide(); + previewWidget->close(); + updatePreview(previewWindow); + } else if (previewWidgetButton->isChecked()) { + previewWindow->hide(); + previewDialog->hide(); + updatePreview(previewWidget); + } else { + previewWindow->hide(); + previewWidget->close(); + updatePreview(previewDialog); + } } void ControllerWidget::createTypeGroupBox() { - widgetTypeGroupBox = new QGroupBox(tr("Widget Type")); + widgetTypeGroupBox = new QGroupBox(tr("Window Type")); + previewWindowButton = createRadioButton(tr("QWindow")); previewWidgetButton = createRadioButton(tr("QWidget")); - previewWidgetButton->setChecked(true); previewDialogButton = createRadioButton(tr("QDialog")); + previewWindowButton->setChecked(true); QHBoxLayout *l = new QHBoxLayout; + l->addWidget(previewWindowButton); l->addWidget(previewWidgetButton); l->addWidget(previewDialogButton); widgetTypeGroupBox->setLayout(l); diff --git a/tests/manual/windowflags/controllerwindow.h b/tests/manual/windowflags/controllerwindow.h index 43a125a9ae..c623256112 100644 --- a/tests/manual/windowflags/controllerwindow.h +++ b/tests/manual/windowflags/controllerwindow.h @@ -60,20 +60,27 @@ private slots: void updateStateControl(); private: + void updatePreview(QWindow *); + void updatePreview(QWidget *); void createTypeGroupBox(); QCheckBox *createCheckBox(const QString &text); QRadioButton *createRadioButton(const QString &text); QMainWindow *parentWindow; - PreviewWindow *previewWindow; + + QWindow *previewWindow; + PreviewWidget *previewWidget; PreviewDialog *previewDialog; - QWidget *previewWidget; + + QWindow *activePreview; + QGroupBox *widgetTypeGroupBox; QGroupBox *additionalOptionsGroupBox; TypeControl *typeControl; HintControl *hintsControl; WindowStatesControl *statesControl; + QRadioButton *previewWindowButton; QRadioButton *previewWidgetButton; QRadioButton *previewDialogButton; QCheckBox *modalWindowCheckBox; diff --git a/tests/manual/windowflags/main.cpp b/tests/manual/windowflags/main.cpp index a7d7307525..4da878d8cc 100644 --- a/tests/manual/windowflags/main.cpp +++ b/tests/manual/windowflags/main.cpp @@ -43,5 +43,6 @@ int main(int argc, char *argv[]) if (!arguments.contains(QLatin1String("-e"))) controller.registerEventFilter(); controller.show(); + controller.lower(); return app.exec(); } diff --git a/tests/manual/windowflags/previewwindow.cpp b/tests/manual/windowflags/previewwindow.cpp index 65a287f788..19473c9eee 100644 --- a/tests/manual/windowflags/previewwindow.cpp +++ b/tests/manual/windowflags/previewwindow.cpp @@ -172,37 +172,37 @@ static QPlainTextEdit *createControlPanel(QWidget *widget) QGridLayout *buttonLayout = new QGridLayout; bottomLayout->addStretch(); bottomLayout->addLayout(buttonLayout); - QPushButton *showNormalButton = new QPushButton(PreviewWindow::tr("Show normal")); + QPushButton *showNormalButton = new QPushButton(PreviewWidget::tr("Show normal")); QObject::connect(showNormalButton, SIGNAL(clicked()), widget, SLOT(showNormal())); buttonLayout->addWidget(showNormalButton, 0, 0); - QPushButton *showMinimizedButton = new QPushButton(PreviewWindow::tr("Show minimized")); + QPushButton *showMinimizedButton = new QPushButton(PreviewWidget::tr("Show minimized")); QObject::connect(showMinimizedButton, SIGNAL(clicked()), widget, SLOT(showMinimized())); buttonLayout->addWidget(showMinimizedButton, 0, 1); - QPushButton *showMaximizedButton = new QPushButton(PreviewWindow::tr("Show maximized")); + QPushButton *showMaximizedButton = new QPushButton(PreviewWidget::tr("Show maximized")); QObject::connect(showMaximizedButton, SIGNAL(clicked()), widget, SLOT(showMaximized())); buttonLayout->addWidget(showMaximizedButton, 0, 2); - QPushButton *showFullScreenButton = new QPushButton(PreviewWindow::tr("Show fullscreen")); + QPushButton *showFullScreenButton = new QPushButton(PreviewWidget::tr("Show fullscreen")); QObject::connect(showFullScreenButton, SIGNAL(clicked()), widget, SLOT(showFullScreen())); buttonLayout->addWidget(showFullScreenButton, 0, 3); - QPushButton *updateInfoButton = new QPushButton(PreviewWindow::tr("&Update Info")); + QPushButton *updateInfoButton = new QPushButton(PreviewWidget::tr("&Update Info")); QObject::connect(updateInfoButton, SIGNAL(clicked()), widget, SLOT(updateInfo())); buttonLayout->addWidget(updateInfoButton, 1, 0); - QPushButton *closeButton = new QPushButton(PreviewWindow::tr("&Close")); + QPushButton *closeButton = new QPushButton(PreviewWidget::tr("&Close")); QObject::connect(closeButton, SIGNAL(clicked()), widget, SLOT(close())); buttonLayout->addWidget(closeButton, 1, 3); return textEdit; } -PreviewWindow::PreviewWindow(QWidget *parent) +PreviewWidget::PreviewWidget(QWidget *parent) : QWidget(parent) { textEdit = createControlPanel(this); setWindowTitle(tr("Preview <QWidget> Qt %1").arg(QLatin1String(QT_VERSION_STR))); } -bool PreviewWindow::event(QEvent *event) +bool PreviewWidget::event(QEvent *event) { const bool ret = QWidget::event(event); @@ -219,7 +219,7 @@ bool PreviewWindow::event(QEvent *event) return ret; } -void PreviewWindow::setWindowFlags(Qt::WindowFlags flags) +void PreviewWidget::setWindowFlags(Qt::WindowFlags flags) { if (flags == windowFlags()) return; @@ -227,7 +227,7 @@ void PreviewWindow::setWindowFlags(Qt::WindowFlags flags) QTimer::singleShot(0, this, SLOT(updateInfo())); } -void PreviewWindow::updateInfo() +void PreviewWidget::updateInfo() { textEdit->setPlainText(formatWidgetInfo(this)); } diff --git a/tests/manual/windowflags/previewwindow.h b/tests/manual/windowflags/previewwindow.h index 9730e7a3f9..023ddd910c 100644 --- a/tests/manual/windowflags/previewwindow.h +++ b/tests/manual/windowflags/previewwindow.h @@ -35,12 +35,12 @@ QT_BEGIN_NAMESPACE class QPlainTextEdit; QT_END_NAMESPACE -class PreviewWindow : public QWidget +class PreviewWidget : public QWidget { Q_OBJECT public: - PreviewWindow(QWidget *parent = 0); + PreviewWidget(QWidget *parent = 0); void setWindowFlags(Qt::WindowFlags flags); |