diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2016-07-28 13:20:41 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2016-08-01 18:20:00 +0200 |
commit | f6fc34294f5691da8aa7ab8dc0452c6fa9036b67 (patch) | |
tree | 3b8fe9841f88aa81e2f8f2d57cbbca2c4736ca6f /tests | |
parent | 6f75096afc000991111bb0fd7a7e530ce3518627 (diff) | |
parent | 0eb77c3011ee4d6bbc46dd2d40c9324e9bfcbecf (diff) |
Merge remote-tracking branch 'origin/5.7' into dev
Conflicts:
configure
5.7 now supports clang on android; but dev re-worked configure
src/gui/kernel/qevent.h
One side renamed a parameter of a constructor; the other added an
alternate constructor on the next line. Applied the rename to both
for consistency.
tests/auto/tools/moc/tst_moc.cpp
Each side added a new test at the end.
.qmake.conf
Ignored 5.7's change to MODULE_VERSION.
configure.json
No conflict noticed by git; but changes in 5.7 were needed for the
re-worked configure to accommodate 5.7's stricter handling of C++11.
Change-Id: I9cda53836a32d7bf83828212c7ea00b1de3e09d2
Diffstat (limited to 'tests')
16 files changed, 1036 insertions, 57 deletions
diff --git a/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp index de6884d454..5b03b35780 100644 --- a/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp @@ -2723,27 +2723,43 @@ void tst_QDataStream::status_QBitArray() } #define MAP_TEST(byteArray, initialStatus, expectedStatus, expectedHash) \ - { \ - QByteArray ba = byteArray; \ - QDataStream stream(&ba, QIODevice::ReadOnly); \ - stream.setStatus(initialStatus); \ - stream >> hash; \ - QCOMPARE((int)stream.status(), (int)expectedStatus); \ - QCOMPARE(hash.size(), expectedHash.size()); \ - QCOMPARE(hash, expectedHash); \ - } \ - { \ - QByteArray ba = byteArray; \ - StringMap expectedMap; \ - StringHash::const_iterator it = expectedHash.constBegin(); \ - for (; it != expectedHash.constEnd(); ++it) \ - expectedMap.insert(it.key(), it.value()); \ - QDataStream stream(&ba, QIODevice::ReadOnly); \ - stream.setStatus(initialStatus); \ - stream >> map; \ - QCOMPARE((int)stream.status(), (int)expectedStatus); \ - QCOMPARE(map.size(), expectedMap.size()); \ - QCOMPARE(map, expectedMap); \ + for (bool inTransaction = false;; inTransaction = true) { \ + { \ + QByteArray ba = byteArray; \ + QDataStream stream(&ba, QIODevice::ReadOnly); \ + if (inTransaction) \ + stream.startTransaction(); \ + stream.setStatus(initialStatus); \ + stream >> hash; \ + QCOMPARE((int)stream.status(), (int)expectedStatus); \ + if (!inTransaction || stream.commitTransaction()) { \ + QCOMPARE(hash.size(), expectedHash.size()); \ + QCOMPARE(hash, expectedHash); \ + } else { \ + QVERIFY(hash.isEmpty()); \ + } \ + } \ + { \ + QByteArray ba = byteArray; \ + StringMap expectedMap; \ + StringHash::const_iterator it = expectedHash.constBegin(); \ + for (; it != expectedHash.constEnd(); ++it) \ + expectedMap.insert(it.key(), it.value()); \ + QDataStream stream(&ba, QIODevice::ReadOnly); \ + if (inTransaction) \ + stream.startTransaction(); \ + stream.setStatus(initialStatus); \ + stream >> map; \ + QCOMPARE((int)stream.status(), (int)expectedStatus); \ + if (!inTransaction || stream.commitTransaction()) { \ + QCOMPARE(map.size(), expectedMap.size()); \ + QCOMPARE(map, expectedMap); \ + } else { \ + QVERIFY(map.isEmpty()); \ + } \ + } \ + if (inTransaction) \ + break; \ } void tst_QDataStream::status_QHash_QMap() @@ -2788,38 +2804,60 @@ void tst_QDataStream::status_QHash_QMap() } #define LIST_TEST(byteArray, initialStatus, expectedStatus, expectedList) \ - { \ - QByteArray ba = byteArray; \ - QDataStream stream(&ba, QIODevice::ReadOnly); \ - stream.setStatus(initialStatus); \ - stream >> list; \ - QCOMPARE((int)stream.status(), (int)expectedStatus); \ - QCOMPARE(list.size(), expectedList.size()); \ - QCOMPARE(list, expectedList); \ - } \ - { \ - LinkedList expectedLinkedList; \ - for (int i = 0; i < expectedList.count(); ++i) \ - expectedLinkedList << expectedList.at(i); \ - QByteArray ba = byteArray; \ - QDataStream stream(&ba, QIODevice::ReadOnly); \ - stream.setStatus(initialStatus); \ - stream >> linkedList; \ - QCOMPARE((int)stream.status(), (int)expectedStatus); \ - QCOMPARE(linkedList.size(), expectedLinkedList.size()); \ - QCOMPARE(linkedList, expectedLinkedList); \ - } \ - { \ - Vector expectedVector; \ - for (int i = 0; i < expectedList.count(); ++i) \ - expectedVector << expectedList.at(i); \ - QByteArray ba = byteArray; \ - QDataStream stream(&ba, QIODevice::ReadOnly); \ - stream.setStatus(initialStatus); \ - stream >> vector; \ - QCOMPARE((int)stream.status(), (int)expectedStatus); \ - QCOMPARE(vector.size(), expectedVector.size()); \ - QCOMPARE(vector, expectedVector); \ + for (bool inTransaction = false;; inTransaction = true) { \ + { \ + QByteArray ba = byteArray; \ + QDataStream stream(&ba, QIODevice::ReadOnly); \ + if (inTransaction) \ + stream.startTransaction(); \ + stream.setStatus(initialStatus); \ + stream >> list; \ + QCOMPARE((int)stream.status(), (int)expectedStatus); \ + if (!inTransaction || stream.commitTransaction()) { \ + QCOMPARE(list.size(), expectedList.size()); \ + QCOMPARE(list, expectedList); \ + } else { \ + QVERIFY(list.isEmpty()); \ + } \ + } \ + { \ + LinkedList expectedLinkedList; \ + for (int i = 0; i < expectedList.count(); ++i) \ + expectedLinkedList << expectedList.at(i); \ + QByteArray ba = byteArray; \ + QDataStream stream(&ba, QIODevice::ReadOnly); \ + if (inTransaction) \ + stream.startTransaction(); \ + stream.setStatus(initialStatus); \ + stream >> linkedList; \ + QCOMPARE((int)stream.status(), (int)expectedStatus); \ + if (!inTransaction || stream.commitTransaction()) { \ + QCOMPARE(linkedList.size(), expectedLinkedList.size()); \ + QCOMPARE(linkedList, expectedLinkedList); \ + } else { \ + QVERIFY(linkedList.isEmpty()); \ + } \ + } \ + { \ + Vector expectedVector; \ + for (int i = 0; i < expectedList.count(); ++i) \ + expectedVector << expectedList.at(i); \ + QByteArray ba = byteArray; \ + QDataStream stream(&ba, QIODevice::ReadOnly); \ + if (inTransaction) \ + stream.startTransaction(); \ + stream.setStatus(initialStatus); \ + stream >> vector; \ + QCOMPARE((int)stream.status(), (int)expectedStatus); \ + if (!inTransaction || stream.commitTransaction()) { \ + QCOMPARE(vector.size(), expectedVector.size()); \ + QCOMPARE(vector, expectedVector); \ + } else { \ + QVERIFY(vector.isEmpty()); \ + } \ + } \ + if (inTransaction) \ + break; \ } void tst_QDataStream::status_QLinkedList_QList_QVector() diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp index f0b9c05810..487c13be94 100644 --- a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp +++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp @@ -34,6 +34,7 @@ #include <qfile.h> #include <qdir.h> #include <qset.h> +#include <qtextcodec.h> #ifdef Q_OS_WIN # include <windows.h> #endif @@ -108,6 +109,38 @@ void tst_QTemporaryDir::getSetCheck() QCOMPARE(true, obj1.autoRemove()); } +static inline bool canHandleUnicodeFileNames() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + return true; +#else + // Check for UTF-8 by converting the Euro symbol (see tst_utf8) + return QFile::encodeName(QString(QChar(0x20AC))) == QByteArrayLiteral("\342\202\254"); +#endif +} + +static QString hanTestText() +{ + QString text; + text += QChar(0x65B0); + text += QChar(0x5E10); + text += QChar(0x6237); + return text; +} + +static QString umlautTestText() +{ + QString text; + text += QChar(0xc4); + text += QChar(0xe4); + text += QChar(0xd6); + text += QChar(0xf6); + text += QChar(0xdc); + text += QChar(0xfc); + text += QChar(0xdf); + return text; +} + void tst_QTemporaryDir::fileTemplate_data() { QTest::addColumn<QString>("constructorTemplate"); @@ -124,6 +157,14 @@ void tst_QTemporaryDir::fileTemplate_data() QTest::newRow("constructor with XXXX suffix") << "qt_XXXXXX_XXXX" << "qt_"; QTest::newRow("constructor with XXXX prefix") << "qt_XXXX" << "qt_"; QTest::newRow("constructor with XXXXX prefix") << "qt_XXXXX" << "qt_"; + if (canHandleUnicodeFileNames()) { + // Test Umlauts (contained in Latin1) + QString prefix = "qt_" + umlautTestText(); + QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix; + // Test Chinese + prefix = "qt_" + hanTestText(); + QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix; + } } void tst_QTemporaryDir::fileTemplate() diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp index 9b46121981..59cd3a8411 100644 --- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp +++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp @@ -34,6 +34,7 @@ #include <qfile.h> #include <qdir.h> #include <qset.h> +#include <qtextcodec.h> #if defined(Q_OS_WIN) # include <windows.h> @@ -140,6 +141,38 @@ void tst_QTemporaryFile::getSetCheck() QCOMPARE(true, obj1.autoRemove()); } +static inline bool canHandleUnicodeFileNames() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + return true; +#else + // Check for UTF-8 by converting the Euro symbol (see tst_utf8) + return QFile::encodeName(QString(QChar(0x20AC))) == QByteArrayLiteral("\342\202\254"); +#endif +} + +static QString hanTestText() +{ + QString text; + text += QChar(0x65B0); + text += QChar(0x5E10); + text += QChar(0x6237); + return text; +} + +static QString umlautTestText() +{ + QString text; + text += QChar(0xc4); + text += QChar(0xe4); + text += QChar(0xd6); + text += QChar(0xf6); + text += QChar(0xdc); + text += QChar(0xfc); + text += QChar(0xdf); + return text; +} + void tst_QTemporaryFile::fileTemplate_data() { QTest::addColumn<QString>("constructorTemplate"); @@ -166,6 +199,14 @@ void tst_QTemporaryFile::fileTemplate_data() QTest::newRow("set template, with xxx") << "" << "qt_" << ".xxx" << "qt_XXXXXX.xxx"; QTest::newRow("set template, with >6 X's") << "" << "qt_" << ".xxx" << "qt_XXXXXXXXXXXXXX.xxx"; QTest::newRow("set template, with >6 X's, no suffix") << "" << "qt_" << "" << "qt_XXXXXXXXXXXXXX"; + if (canHandleUnicodeFileNames()) { + // Test Umlauts (contained in Latin1) + QString prefix = "qt_" + umlautTestText(); + QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix << QString() << QString(); + // Test Chinese + prefix = "qt_" + hanTestText(); + QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix << QString() << QString(); + } } void tst_QTemporaryFile::fileTemplate() diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index 825cb05d74..374fec221e 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -271,6 +271,7 @@ private slots: void testOperators() const; void reserve(); + void reserveZero(); void reallocAfterCopy_data(); void reallocAfterCopy(); void initializeListInt(); @@ -2365,13 +2366,34 @@ void tst_QVector::reserve() { QVector<Foo> a; a.resize(2); + QCOMPARE(fooCtor, 2); QVector<Foo> b(a); b.reserve(1); QCOMPARE(b.size(), a.size()); + QCOMPARE(fooDtor, 0); } QCOMPARE(fooCtor, fooDtor); } +// This is a regression test for QTBUG-51758 +void tst_QVector::reserveZero() +{ + QVector<int> vec; + vec.detach(); + vec.reserve(0); // should not crash + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.squeeze(); + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.reserve(-1); + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.append(42); + QCOMPARE(vec.size(), 1); + QVERIFY(vec.capacity() >= 1); +} + // This is a regression test for QTBUG-11763, where memory would be reallocated // soon after copying a QVector. void tst_QVector::reallocAfterCopy_data() diff --git a/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp index 26d443c2c6..9556a167c5 100644 --- a/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp +++ b/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp @@ -557,6 +557,8 @@ private slots: void checkCommentIndentation_data() const; void crashInXmlStreamReader() const; void write8bitCodec() const; + void invalidStringCharacters_data() const; + void invalidStringCharacters() const; void hasError() const; private: @@ -1614,6 +1616,63 @@ void tst_QXmlStream::write8bitCodec() const QVERIFY(decodedText.startsWith(expected)); } +void tst_QXmlStream::invalidStringCharacters() const +{ + // test scan in attributes + QFETCH(QString, testString); + QFETCH(bool, expectedResultNoError); + + QByteArray values = testString.toUtf8(); + QBuffer inBuffer; + inBuffer.setData(values); + QVERIFY(inBuffer.open(QIODevice::ReadOnly)); + QXmlStreamReader reader(&inBuffer); + do { + reader.readNext(); + } while (!reader.atEnd()); + QCOMPARE((reader.error() == QXmlStreamReader::NoError), expectedResultNoError); +} + +void tst_QXmlStream::invalidStringCharacters_data() const +{ + // test scan in attributes + QTest::addColumn<bool>("expectedResultNoError"); + QTest::addColumn<QString>("testString"); + QChar ctrl(0x1A); + QTest::newRow("utf8, attributes, legal") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'/>"); + QTest::newRow("utf8, attributes, only char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='")+ctrl+QString("'/>"); + QTest::newRow("utf8, attributes, 1st char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='")+ctrl+QString("abc'/>"); + QTest::newRow("utf8, attributes, middle char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='abc")+ctrl+QString("efgx'/>"); + QTest::newRow("utf8, attributes, last char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='abcde")+ctrl+QString("'/>"); + // + QTest::newRow("utf8, text, legal") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abcx1A</root>"); + QTest::newRow("utf8, text, only, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>")+ctrl+QString("</root>"); + QTest::newRow("utf8, text, 1st char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abc")+ctrl+QString("def</root>"); + QTest::newRow("utf8, text, middle char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abc")+ctrl+QString("efg</root>"); + QTest::newRow("utf8, text, last char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abc")+ctrl+QString("</root>"); + // + QTest::newRow("utf8, cdata text, legal") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcdefghi]]></root>"); + QTest::newRow("utf8, cdata text, only, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[")+ctrl+QString("]]></root>"); + QTest::newRow("utf8, cdata text, 1st char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[")+ctrl+QString("abcdefghi]]></root>"); + QTest::newRow("utf8, cdata text, middle char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcd")+ctrl+QString("efghi]]></root>"); + QTest::newRow("utf8, cdata text, last char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcdefghi")+ctrl+QString("]]></root>"); + // + QTest::newRow("utf8, mixed, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a")+ctrl+QString("a'><![CDATA[abcdefghi")+ctrl+QString("]]></root>"); + QTest::newRow("utf8, tag") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><roo")+ctrl+QString("t attr='aa'><![CDATA[abcdefghi]]></roo")+ctrl+QString("t>"); + // + QTest::newRow("utf8, attributes, 1st char, legal escaping hex") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a '/>"); + QTest::newRow("utf8, attributes, 1st char, control escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa'/>"); + QTest::newRow("utf8, attributes, middle char, legal escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaa'/>"); + QTest::newRow("utf8, attributes, last char, control escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa'/>"); + QTest::newRow("utf8, attributes, 1st char, legal escaping dec") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a '/>"); + QTest::newRow("utf8, attributes, 1st char, control escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaa'/>"); + QTest::newRow("utf8, attributes, middle char, legal escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaaaa'/>"); + QTest::newRow("utf8, attributes, last char, control escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaa'/>"); + QTest::newRow("utf8, tag escaping") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcdefghi]]></root>"); + // + QTest::newRow("utf8, mix of illegal control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a�a'><![CDATA[abcdefghi]]></root>"); + // +} #include "tst_qxmlstream.moc" // vim: et:ts=4:sw=4:sts=4 diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index 7e1a02d716..18812fd090 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -94,6 +94,8 @@ private slots: void setPixel_data(); void setPixel(); + void defaultColorTable_data(); + void defaultColorTable(); void setColorCount(); void setColor(); @@ -1450,6 +1452,38 @@ void tst_QImage::convertToFormatPreserveText() QCOMPARE(imgResult2.textKeys(), listResult); } +void tst_QImage::defaultColorTable_data() +{ + QTest::addColumn<QImage::Format>("format"); + QTest::addColumn<int>("createdDataCount"); + QTest::addColumn<int>("externalDataCount"); + + // For historical reasons, internally created mono images get a default colormap. + // Externally created and Indexed8 images do not. + QTest::newRow("Mono") << QImage::Format_Mono << 2 << 0; + QTest::newRow("MonoLSB") << QImage::Format_MonoLSB << 2 << 0; + QTest::newRow("Indexed8") << QImage::Format_Indexed8 << 0 << 0; + QTest::newRow("ARGB32_PM") << QImage::Format_A2BGR30_Premultiplied << 0 << 0; +} + +void tst_QImage::defaultColorTable() +{ + QFETCH(QImage::Format, format); + QFETCH(int, createdDataCount); + QFETCH(int, externalDataCount); + + QImage img1(1, 1, format); + QCOMPARE(img1.colorCount(), createdDataCount); + QCOMPARE(img1.colorTable().size(), createdDataCount); + + quint32 buf; + QImage img2(reinterpret_cast<uchar *>(&buf), 1, 1, format); + QCOMPARE(img2.colorCount(), externalDataCount); + + QImage nullImg(0, 0, format); + QCOMPARE(nullImg.colorCount(), 0); +} + void tst_QImage::setColorCount() { QImage img(0, 0, QImage::Format_Indexed8); @@ -3194,8 +3228,8 @@ void tst_QImage::pixel() QImage monolsb(&a, 1, 1, QImage::Format_MonoLSB); QImage indexed(&a, 1, 1, QImage::Format_Indexed8); - QCOMPARE(QColor(mono.pixel(0, 0)), QColor(Qt::black)); - QCOMPARE(QColor(monolsb.pixel(0, 0)), QColor(Qt::black)); + mono.pixel(0, 0); // Don't crash + monolsb.pixel(0, 0); // Don't crash indexed.pixel(0, 0); // Don't crash } } diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp index 6bb502edcb..fb6b0c6e32 100644 --- a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp @@ -673,6 +673,12 @@ void tst_QTcpSocket::bindThenResolveHost() const quint16 port = 80; socket->connectToHost(hostName, port); + // Additionally, initiate a delayed close before the socket connects + // to ensure that we don't lose the socket engine in HostLookupState. + // After a connection has been established, socket should send all + // the pending data and close the socket engine automatically. + QVERIFY(socket->putChar(0)); + socket->close(); QVERIFY2(socket->waitForConnected(), (hostName.toLocal8Bit() + ": " + QByteArray::number(port) + ' ' + QtNetworkSettings::msgSocketError(*socket)).constData()); diff --git a/tests/auto/other/qobjectrace/tst_qobjectrace.cpp b/tests/auto/other/qobjectrace/tst_qobjectrace.cpp index ae5953aa35..aa78d70716 100644 --- a/tests/auto/other/qobjectrace/tst_qobjectrace.cpp +++ b/tests/auto/other/qobjectrace/tst_qobjectrace.cpp @@ -46,6 +46,7 @@ class tst_QObjectRace: public QObject private slots: void moveToThreadRace(); void destroyRace(); + void disconnectRace(); }; class RaceObject : public QObject @@ -293,6 +294,180 @@ void tst_QObjectRace::destroyRace() delete threads[i]; } +static QAtomicInteger<unsigned> countedStructObjectsCount; +struct CountedFunctor +{ + CountedFunctor() : destroyed(false) { countedStructObjectsCount.fetchAndAddRelaxed(1); } + CountedFunctor(const CountedFunctor &) : destroyed(false) { countedStructObjectsCount.fetchAndAddRelaxed(1); } + CountedFunctor &operator=(const CountedFunctor &) { return *this; } + ~CountedFunctor() { destroyed = true; countedStructObjectsCount.fetchAndAddRelaxed(-1);} + void operator()() const {QCOMPARE(destroyed, false);} + +private: + bool destroyed; +}; + +class DisconnectRaceSenderObject : public QObject +{ + Q_OBJECT +signals: + void theSignal(); +}; + +class DisconnectRaceThread : public QThread +{ + Q_OBJECT + + DisconnectRaceSenderObject *sender; + bool emitSignal; +public: + DisconnectRaceThread(DisconnectRaceSenderObject *s, bool emitIt) + : QThread(), sender(s), emitSignal(emitIt) + { + } + + void run() + { + while (!isInterruptionRequested()) { + QMetaObject::Connection conn = connect(sender, &DisconnectRaceSenderObject::theSignal, + sender, CountedFunctor(), Qt::BlockingQueuedConnection); + if (emitSignal) + emit sender->theSignal(); + disconnect(conn); + yieldCurrentThread(); + } + } +}; + +class DeleteReceiverRaceSenderThread : public QThread +{ + Q_OBJECT + + DisconnectRaceSenderObject *sender; +public: + DeleteReceiverRaceSenderThread(DisconnectRaceSenderObject *s) + : QThread(), sender(s) + { + } + + void run() + { + while (!isInterruptionRequested()) { + emit sender->theSignal(); + yieldCurrentThread(); + } + } +}; + +class DeleteReceiverRaceReceiver : public QObject +{ + Q_OBJECT + + DisconnectRaceSenderObject *sender; + QObject *receiver; + QTimer *timer; +public: + DeleteReceiverRaceReceiver(DisconnectRaceSenderObject *s) + : QObject(), sender(s), receiver(0) + { + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &DeleteReceiverRaceReceiver::onTimeout); + timer->start(1); + } + + void onTimeout() + { + if (receiver) + delete receiver; + receiver = new QObject; + connect(sender, &DisconnectRaceSenderObject::theSignal, receiver, CountedFunctor(), Qt::BlockingQueuedConnection); + } +}; + +class DeleteReceiverRaceReceiverThread : public QThread +{ + Q_OBJECT + + DisconnectRaceSenderObject *sender; +public: + DeleteReceiverRaceReceiverThread(DisconnectRaceSenderObject *s) + : QThread(), sender(s) + { + } + + void run() + { + QScopedPointer<DeleteReceiverRaceReceiver> receiver(new DeleteReceiverRaceReceiver(sender)); + exec(); + } +}; + +void tst_QObjectRace::disconnectRace() +{ + enum { ThreadCount = 20, TimeLimit = 3000 }; + + QCOMPARE(countedStructObjectsCount.load(), 0u); + + { + QScopedPointer<DisconnectRaceSenderObject> sender(new DisconnectRaceSenderObject()); + QScopedPointer<QThread> senderThread(new QThread()); + senderThread->start(); + sender->moveToThread(senderThread.data()); + + DisconnectRaceThread *threads[ThreadCount]; + for (int i = 0; i < ThreadCount; ++i) { + threads[i] = new DisconnectRaceThread(sender.data(), !(i % 10)); + threads[i]->start(); + } + + QTime timeLimiter; + timeLimiter.start(); + + while (timeLimiter.elapsed() < TimeLimit) + QTest::qWait(10); + + for (int i = 0; i < ThreadCount; ++i) { + threads[i]->requestInterruption(); + QVERIFY(threads[i]->wait(300)); + delete threads[i]; + } + + senderThread->quit(); + QVERIFY(senderThread->wait(300)); + } + + QCOMPARE(countedStructObjectsCount.load(), 0u); + + { + QScopedPointer<DisconnectRaceSenderObject> sender(new DisconnectRaceSenderObject()); + QScopedPointer<DeleteReceiverRaceSenderThread> senderThread(new DeleteReceiverRaceSenderThread(sender.data())); + senderThread->start(); + sender->moveToThread(senderThread.data()); + + DeleteReceiverRaceReceiverThread *threads[ThreadCount]; + for (int i = 0; i < ThreadCount; ++i) { + threads[i] = new DeleteReceiverRaceReceiverThread(sender.data()); + threads[i]->start(); + } + + QTime timeLimiter; + timeLimiter.start(); + + while (timeLimiter.elapsed() < TimeLimit) + QTest::qWait(10); + + senderThread->requestInterruption(); + QVERIFY(senderThread->wait(300)); + + for (int i = 0; i < ThreadCount; ++i) { + threads[i]->quit(); + QVERIFY(threads[i]->wait(300)); + delete threads[i]; + } + } + + QCOMPARE(countedStructObjectsCount.load(), 0u); +} QTEST_MAIN(tst_QObjectRace) #include "tst_qobjectrace.moc" diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 94df29f539..dc28422294 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -2034,6 +2034,13 @@ void tst_Moc::warnings_data() << 0 << QString("IGNORE_ALL_STDOUT") << QStringLiteral("Warning: Failed to resolve include \"doesnotexist.h\" for moc file <standard input>"); + + QTest::newRow("QTBUG-54815: Crash on invalid input") + << QByteArray("class M{(})F<{}d000000000000000#0") + << QStringList() + << 0 + << QString() + << QString("standard input:1: Note: No relevant classes found. No output generated."); } void tst_Moc::warnings() diff --git a/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro b/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro index 9405c33a48..10cd1dcc54 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro +++ b/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qitemdelegate -QT += widgets testlib +QT += widgets widgets-private testlib SOURCES += tst_qitemdelegate.cpp win32:!winrt: LIBS += -luser32 diff --git a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp index e67edfcccb..0720a4f766 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp +++ b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp @@ -51,6 +51,8 @@ #include <QPlainTextEdit> #include <QDialog> +#include <QtWidgets/private/qabstractitemdelegate_p.h> + Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) #if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) @@ -216,6 +218,8 @@ private slots: void task257859_finalizeEdit(); void QTBUG4435_keepSelectionOnCheck(); + + void QTBUG16469_textForRole(); }; @@ -1521,6 +1525,64 @@ void tst_QItemDelegate::testLineEditValidation() QCOMPARE(item->data(Qt::DisplayRole).toString(), QStringLiteral("abc,def")); } +void tst_QItemDelegate::QTBUG16469_textForRole() +{ +#ifndef QT_BUILD_INTERNAL + QSKIP("This test requires a developer build"); +#else + struct TestDelegate : public QItemDelegate + { + QString textForRole(Qt::ItemDataRole role, const QVariant &value, const QLocale &locale) + { + QAbstractItemDelegatePrivate *d = reinterpret_cast<QAbstractItemDelegatePrivate *>(qGetPtrHelper(d_ptr)); + return d->textForRole(role, value, locale); + } + } delegate; + QLocale locale; + + const float f = 123.456f; + QCOMPARE(delegate.textForRole(Qt::DisplayRole, f, locale), locale.toString(f)); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, f, locale), locale.toString(f)); + const double d = 123.456; + QCOMPARE(delegate.textForRole(Qt::DisplayRole, d, locale), locale.toString(d, 'g', 6)); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, d, locale), locale.toString(d, 'g', 6)); + const int i = 1234567; + QCOMPARE(delegate.textForRole(Qt::DisplayRole, i, locale), locale.toString(i)); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, i, locale), locale.toString(i)); + const qlonglong ll = 1234567; + QCOMPARE(delegate.textForRole(Qt::DisplayRole, ll, locale), locale.toString(ll)); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, ll, locale), locale.toString(ll)); + const uint ui = 1234567; + QCOMPARE(delegate.textForRole(Qt::DisplayRole, ui, locale), locale.toString(ui)); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, ui, locale), locale.toString(ui)); + const qulonglong ull = 1234567; + QCOMPARE(delegate.textForRole(Qt::DisplayRole, ull, locale), locale.toString(ull)); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, ull, locale), locale.toString(ull)); + + const QDateTime dateTime = QDateTime::currentDateTime(); + const QDate date = dateTime.date(); + const QTime time = dateTime.time(); + const QString shortDate = locale.toString(date, QLocale::ShortFormat); + const QString longDate = locale.toString(date, QLocale::LongFormat); + const QString shortTime = locale.toString(time, QLocale::ShortFormat); + const QString longTime = locale.toString(time, QLocale::LongFormat); + QCOMPARE(delegate.textForRole(Qt::DisplayRole, date, locale), shortDate); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, date, locale), longDate); + QCOMPARE(delegate.textForRole(Qt::DisplayRole, time, locale), shortTime); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, time, locale), longTime); + QCOMPARE(delegate.textForRole(Qt::DisplayRole, dateTime, locale), shortDate + QLatin1Char(' ') + shortTime); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, dateTime, locale), longDate + QLatin1Char(' ') + longTime); + + const QString text("text"); + QCOMPARE(delegate.textForRole(Qt::DisplayRole, text, locale), text); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, text, locale), text); + const QString multipleLines("multiple\nlines"); + QString multipleLines2 = multipleLines; + multipleLines2.replace(QLatin1Char('\n'), QChar::LineSeparator); + QCOMPARE(delegate.textForRole(Qt::DisplayRole, multipleLines, locale), multipleLines2); + QCOMPARE(delegate.textForRole(Qt::ToolTipRole, multipleLines, locale), multipleLines); +#endif +} // ### _not_ covered: diff --git a/tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro b/tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro new file mode 100644 index 0000000000..93da4b8c91 --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/embeddedintoforeignwindow.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +QT += gui-private +CONFIG += console c++11 +CONFIG -= app_bundle +SOURCES += main.cpp itemwindow.cpp +HEADERS += itemwindow.h +include(../diaglib/diaglib.pri) diff --git a/tests/manual/embeddedintoforeignwindow/itemwindow.cpp b/tests/manual/embeddedintoforeignwindow/itemwindow.cpp new file mode 100644 index 0000000000..32a2e6557c --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/itemwindow.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://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 "itemwindow.h" + +#include <QtGui/QPainter> +#include <QtGui/QPaintEvent> + +void TextItem::paint(QPainter &painter) +{ + painter.fillRect(m_rect, m_col); + painter.drawRect(m_rect); + QTextOption textOption; + textOption.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + painter.drawText(m_rect, m_text, textOption); +} + +void ButtonItem::mouseEvent(QMouseEvent *mouseEvent) +{ + if (mouseEvent->type() == QEvent::MouseButtonPress && rect().contains(mouseEvent->pos())) { + mouseEvent->accept(); + emit clicked(); + } +} + +void ButtonItem::keyEvent(QKeyEvent *keyEvent) +{ + if (m_shortcut && keyEvent->type() == QEvent::KeyPress + && (keyEvent->key() + int(keyEvent->modifiers())) == m_shortcut) { + keyEvent->accept(); + emit clicked(); + } +} + +void ItemWindow::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QRect rect(QPoint(0, 0), size()); + painter.fillRect(rect, m_background); + foreach (Item *i, m_items) + i->paint(painter); +} diff --git a/tests/manual/embeddedintoforeignwindow/itemwindow.h b/tests/manual/embeddedintoforeignwindow/itemwindow.h new file mode 100644 index 0000000000..8ea9adaa16 --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/itemwindow.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://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$ +** +****************************************************************************/ + +#ifndef ITEMWINDOW_H +#define ITEMWINDOW_H + +#include <QtGui/QKeySequence> +#include <QtGui/QRasterWindow> + +QT_USE_NAMESPACE + +// ItemWindow: Primitive UI item classes for use with a ItemWindow based +// on QRasterWindow without widgets allowing a simple UI without widgets. + +// Basic item to be used in a ItemWindow +class Item : public QObject { + Q_OBJECT +public: + explicit Item(QObject *parent = nullptr) : QObject(parent) {} + + virtual void paint(QPainter &painter) = 0; + virtual void mouseEvent(QMouseEvent *) {} + virtual void keyEvent(QKeyEvent *) {} +}; + +// Positionable text item +class TextItem : public Item { + Q_OBJECT +public: + explicit TextItem(const QString &text, const QRect &rect, const QColor &col, + QObject *parent = nullptr) + : Item(parent), m_text(text), m_rect(rect), m_col(col) {} + + void paint(QPainter &painter) override; + + QRect rect() const { return m_rect; } + +private: + const QString m_text; + const QRect m_rect; + const QColor m_col; +}; + +// "button" item +class ButtonItem : public TextItem { + Q_OBJECT +public: + explicit ButtonItem(const QString &text, const QRect &rect, const QColor &col, + QObject *parent = nullptr) + : TextItem(text, rect, col, parent), m_shortcut(0) {} + + void mouseEvent(QMouseEvent *mouseEvent) override; + void keyEvent(QKeyEvent *keyEvent) override; + + int shortcut() const { return m_shortcut; } + void setShortcut(int shortcut) { m_shortcut = shortcut; } + +signals: + void clicked(); + +private: + int m_shortcut; +}; + +#define PROPAGATE_EVENT(windowHandler, eventClass, itemHandler) \ +void windowHandler(eventClass *e) override \ +{ \ + foreach (Item *i, m_items) \ + i->itemHandler(e); \ +} + +class ItemWindow : public QRasterWindow { + Q_OBJECT +public: + explicit ItemWindow(QWindow *parent = nullptr) : QRasterWindow(parent), m_background(Qt::white) {} + + void addItem(Item *item) { m_items.append(item); } + + QColor background() const { return m_background; } + void setBackground(const QColor &background) { m_background = background; } + +protected: + void paintEvent(QPaintEvent *) override; + PROPAGATE_EVENT(mousePressEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(mouseReleaseEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(mouseDoubleClickEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(mouseMoveEvent, QMouseEvent, mouseEvent) + PROPAGATE_EVENT(keyPressEvent, QKeyEvent, keyEvent) + PROPAGATE_EVENT(keyReleaseEvent, QKeyEvent, keyEvent) + +private: + QList<Item *> m_items; + QColor m_background; +}; + +#endif // ITEMWINDOW_H diff --git a/tests/manual/embeddedintoforeignwindow/main.cpp b/tests/manual/embeddedintoforeignwindow/main.cpp new file mode 100644 index 0000000000..d7b33683a7 --- /dev/null +++ b/tests/manual/embeddedintoforeignwindow/main.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://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 "itemwindow.h" + +#include <QtGui/QGuiApplication> + +#include <QtCore/QCommandLineOption> +#include <QtCore/QCommandLineParser> +#include <QtCore/QDebug> +#include <QtCore/QStringList> + +#ifdef Q_OS_WIN +# include <qpa/qplatformnativeinterface.h> +# include <QtCore/QMetaObject> +# include <QtCore/qt_windows.h> +#endif + +#include <eventfilter.h> // diaglib +#include <qwindowdump.h> + +#include <iostream> + +QT_USE_NAMESPACE + +static const char usage[] = + "\nEmbeds a QWindow into a native foreign window passed on the command line.\n" + "When no window ID is passed, a test window is created (Windows only)."; + +static QString windowTitle() +{ + return QLatin1String(QT_VERSION_STR) + QLatin1Char(' ') + QGuiApplication::platformName(); +} + +#ifdef Q_OS_WIN +// Helper to create a native test window (Windows) +static QString registerWindowClass(const QString &name) +{ + QString result; + void *proc = DefWindowProc; + QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface(); + if (!QMetaObject::invokeMethod(ni, "registerWindowClass", Qt::DirectConnection, + Q_RETURN_ARG(QString, result), + Q_ARG(QString, name), + Q_ARG(void *, proc))) { + qWarning("registerWindowClass failed"); + } + return result; +} + +static HWND createNativeWindow(const QString &name) +{ + const HWND hwnd = + CreateWindowEx(0, reinterpret_cast<const wchar_t *>(name.utf16()), + L"NativeWindow", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, GetModuleHandle(NULL), NULL); + + if (!hwnd) { + qErrnoWarning("Cannot create window \"%s\"", qPrintable(name)); + return 0; + } + + const QString text = windowTitle() + QLatin1String(" 0x") + QString::number(quint64(hwnd), 16); + SetWindowText(hwnd, reinterpret_cast<const wchar_t *>(text.utf16())); + return hwnd; +} +#endif // Q_OS_WIN + +// Helper functions for simple management of native windows. +static WId createNativeTestWindow() +{ + WId result = 0; +#ifdef Q_OS_WIN + const QString className = registerWindowClass(QLatin1String("TestClass") + windowTitle()); + const HWND nativeWin = createNativeWindow(className); + result = WId(nativeWin); +#else // Q_OS_WIN + Q_UNIMPLEMENTED(); +#endif + return result; +} + +static void showNativeWindow(WId wid) +{ +#ifdef Q_OS_WIN + ShowWindow(HWND(wid), SW_SHOW); +#else // Q_OS_WIN + Q_UNUSED(wid) + Q_UNIMPLEMENTED(); +#endif +} + +static void setFocusToNativeWindow(WId wid) +{ +#ifdef Q_OS_WIN + SetFocus(HWND(wid)); +#else // Q_OS_WIN + Q_UNUSED(wid) + Q_UNIMPLEMENTED(); +#endif +} + +static void destroyNativeWindow(WId wid) +{ +#ifdef Q_OS_WIN + DestroyWindow(HWND(wid)); +#else // Q_OS_WIN + Q_UNUSED(wid) + Q_UNIMPLEMENTED(); +#endif +} + +// Main test window to be embedded into foreign window with some buttons. +class EmbeddedTestWindow : public ItemWindow { + Q_OBJECT +public: + explicit EmbeddedTestWindow(QWindow *parent = nullptr) : ItemWindow(parent) + { + const int spacing = 10; + const QSize buttonSize(100, 30); + const int width = 3 * buttonSize.width() + 4 * spacing; + + QPoint pos(spacing, spacing); + addItem(new TextItem(::windowTitle(), QRect(pos, QSize(width - 2 * spacing, buttonSize.height())), + Qt::white)); + + pos.ry() += 2 * spacing + buttonSize.height(); + + ButtonItem *mgi = new ButtonItem("Map to global", QRect(pos, buttonSize), + QColor(Qt::yellow).lighter(), this); + connect(mgi, &ButtonItem::clicked, this, &EmbeddedTestWindow::testMapToGlobal); + addItem(mgi); + + pos.rx() += buttonSize.width() + spacing; + ButtonItem *di = new ButtonItem("Dump Wins", QRect(pos, buttonSize), + QColor(Qt::cyan).lighter(), this); + connect(di, &ButtonItem::clicked, this, [] () { QtDiag::dumpAllWindows(); }); + addItem(di); + + pos.rx() += buttonSize.width() + spacing; + ButtonItem *qi = new ButtonItem("Quit", QRect(pos, buttonSize), + QColor(Qt::red).lighter(), this); + qi->setShortcut(Qt::CTRL + Qt::Key_Q); + connect(qi, &ButtonItem::clicked, qApp, &QCoreApplication::quit); + addItem(qi); + + setBackground(Qt::lightGray); + resize(width, pos.y() + buttonSize.height() + spacing); + } + +public slots: + void testMapToGlobal(); +}; + +void EmbeddedTestWindow::testMapToGlobal() +{ + const QPoint globalPos = mapToGlobal(QPoint(0,0)); + qDebug() << "mapToGlobal(QPoint(0,0)" << globalPos + << "cursor at:" << QCursor::pos(); +} + +struct EventFilterOption +{ + const char *name; + const char *description; + QtDiag::EventFilter::EventCategories categories; +}; + +EventFilterOption eventFilterOptions[] = { +{"mouse-events", "Dump mouse events.", QtDiag::EventFilter::MouseEvents}, +{"keyboard-events", "Dump keyboard events.", QtDiag::EventFilter::KeyEvents}, +{"state-events", "Dump state/focus change events.", QtDiag::EventFilter::StateChangeEvents | QtDiag::EventFilter::FocusEvents} +}; + +static inline bool isOptionSet(int argc, char *argv[], const char *option) +{ + return (argv + argc) != + std::find_if(argv + 1, argv + argc, + [option] (const char *arg) { return !qstrcmp(arg, option); }); +} + +int main(int argc, char *argv[]) +{ + // Check for no scaling before QApplication is instantiated. + if (isOptionSet(argc, argv, "-s")) + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); + QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); + QGuiApplication::setApplicationDisplayName("Foreign Window Embedding Tester"); + + QGuiApplication app(argc, argv); + + // Process command line + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + parser.setApplicationDescription(QLatin1String(usage)); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption noScalingDummy(QStringLiteral("s"), + QStringLiteral("Disable High DPI scaling.")); + parser.addOption(noScalingDummy); + const int eventFilterOptionCount = int(sizeof(eventFilterOptions) / sizeof(eventFilterOptions[0])); + for (int i = 0; i < eventFilterOptionCount; ++i) { + parser.addOption(QCommandLineOption(QLatin1String(eventFilterOptions[i].name), + QLatin1String(eventFilterOptions[i].description))); + } + parser.addPositionalArgument(QStringLiteral("[windows]"), QStringLiteral("Window ID.")); + + parser.process(QCoreApplication::arguments()); + + QtDiag::EventFilter::EventCategories eventCategories = 0; + for (int i = 0; i < eventFilterOptionCount; ++i) { + if (parser.isSet(QLatin1String(eventFilterOptions[i].name))) + eventCategories |= eventFilterOptions[i].categories; + } + if (eventCategories) + app.installEventFilter(new QtDiag::EventFilter(eventCategories, &app)); + + // Obtain foreign window to test with. + WId testForeignWinId = 0; + bool createdTestWindow = false; + if (parser.positionalArguments().isEmpty()) { + testForeignWinId = createNativeTestWindow(); + if (!testForeignWinId) + parser.showHelp(-1); + showNativeWindow(testForeignWinId); + createdTestWindow = true; + } else { + bool ok; + const QString &winIdArgument = parser.positionalArguments().constFirst(); + testForeignWinId = winIdArgument.toULongLong(&ok, 0); + if (!ok) { + std::cerr << "Invalid window id: \"" << qPrintable(winIdArgument) << "\"\n"; + return -1; + } + } + if (!testForeignWinId) + parser.showHelp(1); + + QWindow *foreignWindow = QWindow::fromWinId(testForeignWinId); + if (!foreignWindow) + return -2; + foreignWindow->setObjectName("ForeignWindow"); + EmbeddedTestWindow *embeddedTestWindow = new EmbeddedTestWindow(foreignWindow); + embeddedTestWindow->setObjectName("EmbeddedTestWindow"); + embeddedTestWindow->show(); + setFocusToNativeWindow(embeddedTestWindow->winId()); // Windows: Set keyboard focus. + + const int exitCode = app.exec(); + delete embeddedTestWindow; + delete foreignWindow; + if (createdTestWindow) + destroyNativeWindow(testForeignWinId); + return exitCode; +} + +#include "main.moc" diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 6f77c82dc5..b9789c354d 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -2,8 +2,10 @@ TEMPLATE=subdirs SUBDIRS = bearerex \ filetest \ +embeddedintoforeignwindow \ foreignwindows \ gestures \ +highdpi \ inputmethodhints \ keypadnavigation \ lance \ @@ -62,4 +64,4 @@ 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 \ -foreignwindows +embeddedintoforeignwindow foreignwindows |