diff options
Diffstat (limited to 'tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp')
-rw-r--r-- | tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp | 359 |
1 files changed, 207 insertions, 152 deletions
diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index fe7822718f..689ff452f9 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -1,34 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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/QtTest> +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2017 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QSignalSpy> +#include <QtEndian> +#if QT_CONFIG(process) +#include <QProcess> +#endif +#include <QScopeGuard> +#include <QVersionNumber> +#include <QSemaphore> #include <qcoreapplication.h> #include <qfileinfo.h> @@ -46,8 +28,9 @@ #include <QNetworkInterface> #include <qstringlist.h> +#include <QSet> #include "../../../network-settings.h" -#include "emulationdetector.h" +#include <QtTest/private/qemulationdetector_p.h> #if defined(Q_OS_LINUX) #define SHOULD_CHECK_SYSCALL_SUPPORT @@ -63,6 +46,8 @@ # define RELIABLE_BYTES_AVAILABLE #endif +using namespace Qt::StringLiterals; + Q_DECLARE_METATYPE(QHostAddress) QT_FORWARD_DECLARE_CLASS(QUdpSocket) @@ -120,6 +105,8 @@ private slots: void asyncReadDatagram(); void writeInHostLookupState(); + void readyReadConnectionThrottling(); + protected slots: void empty_readyReadSlot(); void empty_connectedSlot(); @@ -138,6 +125,7 @@ private: QList<QHostAddress> allAddresses; QHostAddress multicastGroup4, multicastGroup6; QList<QHostAddress> linklocalMulticastGroups; + QNetworkInterface ifaceWithIPv6; QUdpSocket *m_asyncSender; QUdpSocket *m_asyncReceiver; }; @@ -188,26 +176,7 @@ QNetworkInterface tst_QUdpSocket::interfaceForGroup(const QHostAddress &multicas if (!scope.isEmpty()) return QNetworkInterface::interfaceFromName(scope); - static QNetworkInterface ipv6if = [=]() { - // find any link local address in the allAddress list - for (const QHostAddress &addr: qAsConst(allAddresses)) { - if (addr.isLoopback()) - continue; - - QString scope = addr.scopeId(); - if (!scope.isEmpty()) { - QNetworkInterface iface = QNetworkInterface::interfaceFromName(scope); - qDebug() << "Will bind IPv6 sockets to" << iface; - return iface; - } - } - - qWarning("interfaceForGroup(%s) could not find any link-local IPv6 address! " - "Make sure this test is behind a check of QtNetworkSettings::hasIPv6().", - qUtf8Printable(multicastGroup.toString())); - return QNetworkInterface(); - }(); - return ipv6if; + return ifaceWithIPv6; } bool tst_QUdpSocket::shouldWorkaroundLinuxKernelBug() @@ -286,18 +255,25 @@ void tst_QUdpSocket::initTestCase() // ff12:: is temporary, not prefix-based, link-local r[0] = qToBigEndian(Q_UINT64_C(0xff12) << 48); QHostAddress llbase(*reinterpret_cast<Q_IPV6ADDR *>(&r)); - for (const QHostAddress &a : qAsConst(allAddresses)) { + for (const QHostAddress &a : std::as_const(allAddresses)) { QString scope = a.scopeId(); if (scope.isEmpty()) continue; llbase.setScopeId(scope); linklocalMulticastGroups << llbase; + if (!ifaceWithIPv6.isValid()) { + // Remember the first interface we've found that has IPv6 so we can + // bind non-link-local sockets to it (the first is least likely to + // be some weird virtual interface). + ifaceWithIPv6 = QNetworkInterface::interfaceFromName(scope); + } } qDebug() << "Will use multicast groups" << multicastGroup4 << multicastGroup6 << linklocalMulticastGroups; + qDebug() << "Will bind IPv6 sockets to" << ifaceWithIPv6; m_workaroundLinuxKernelBug = shouldWorkaroundLinuxKernelBug(); - if (EmulationDetector::isRunningArmOnX86()) + if (QTestPrivate::isRunningArmOnX86()) QSKIP("This test is unreliable due to QEMU emulation shortcomings."); } @@ -351,7 +327,7 @@ void tst_QUdpSocket::unconnectedServerAndClientTest() QSignalSpy stateChangedSpy(&serverSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); QVERIFY2(serverSocket.bind(), serverSocket.errorString().toLatin1().constData()); - QCOMPARE(stateChangedSpy.count(), 1); + QCOMPARE(stateChangedSpy.size(), 1); const char *message[] = {"Yo mista", "Yo", "Wassap"}; @@ -400,10 +376,11 @@ void tst_QUdpSocket::broadcasting() const char *message[] = {"Yo mista", "", "Yo", "Wassap"}; QList<QHostAddress> broadcastAddresses; - foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) { + const auto ifaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &iface : ifaces) { if ((iface.flags() & QNetworkInterface::CanBroadcast) && iface.flags() & QNetworkInterface::IsUp) { - for (int i=0;i<iface.addressEntries().count();i++) { + for (int i=0;i<iface.addressEntries().size();i++) { QHostAddress broadcast = iface.addressEntries().at(i).broadcast(); if (broadcast.protocol() == QAbstractSocket::IPv4Protocol) broadcastAddresses.append(broadcast); @@ -428,7 +405,7 @@ void tst_QUdpSocket::broadcasting() for (int k = 0; k < 4; k++) { broadcastSocket.writeDatagram(message[i], strlen(message[i]), QHostAddress::Broadcast, serverPort); - foreach (QHostAddress addr, broadcastAddresses) + for (const QHostAddress &addr : std::as_const(broadcastAddresses)) broadcastSocket.writeDatagram(message[i], strlen(message[i]), addr, serverPort); } QTestEventLoop::instance().enterLoop(15); @@ -437,7 +414,6 @@ void tst_QUdpSocket::broadcasting() QEXPECT_FAIL("", "Broadcasting to 255.255.255.255 does not work on FreeBSD", Abort); - QVERIFY(false); // seems that QFAIL() doesn't respect the QEXPECT_FAIL() :/ #endif QFAIL("Network operation timed out"); } @@ -449,7 +425,7 @@ void tst_QUdpSocket::broadcasting() QVERIFY(dgram.isValid()); QByteArray arr = dgram.data(); - QCOMPARE(arr.length(), messageLength); + QCOMPARE(arr.size(), messageLength); arr.resize(messageLength); QCOMPARE(arr, QByteArray(message[i])); @@ -509,27 +485,27 @@ void tst_QUdpSocket::loop() QHostAddress peterAddress = makeNonAny(peter.localAddress()); QHostAddress paulAddress = makeNonAny(paul.localAddress()); - QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(), - paulAddress, paul.localPort()), qint64(peterMessage.length())); - QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(), - peterAddress, peter.localPort()), qint64(paulMessage.length())); + QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.size(), + paulAddress, paul.localPort()), qint64(peterMessage.size())); + QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.size(), + peterAddress, peter.localPort()), qint64(paulMessage.size())); QVERIFY2(peter.waitForReadyRead(9000), QtNetworkSettings::msgSocketError(peter).constData()); QVERIFY2(paul.waitForReadyRead(9000), QtNetworkSettings::msgSocketError(paul).constData()); - QNetworkDatagram peterDatagram = peter.receiveDatagram(paulMessage.length() * 2); - QNetworkDatagram paulDatagram = paul.receiveDatagram(peterMessage.length() * 2); + QNetworkDatagram peterDatagram = peter.receiveDatagram(paulMessage.size() * 2); + QNetworkDatagram paulDatagram = paul.receiveDatagram(peterMessage.size() * 2); if (success) { - QCOMPARE(peterDatagram.data().length(), qint64(paulMessage.length())); - QCOMPARE(paulDatagram.data().length(), qint64(peterMessage.length())); + QCOMPARE(peterDatagram.data().size(), qint64(paulMessage.size())); + QCOMPARE(paulDatagram.data().size(), qint64(peterMessage.size())); } else { // this code path seems to never be executed - QVERIFY(peterDatagram.data().length() != paulMessage.length()); - QVERIFY(paulDatagram.data().length() != peterMessage.length()); + QVERIFY(peterDatagram.data().size() != paulMessage.size()); + QVERIFY(paulDatagram.data().size() != peterMessage.size()); } - QCOMPARE(peterDatagram.data().left(paulMessage.length()), paulMessage); - QCOMPARE(paulDatagram.data().left(peterMessage.length()), peterMessage); + QCOMPARE(peterDatagram.data().left(paulMessage.size()), paulMessage); + QCOMPARE(paulDatagram.data().left(peterMessage.size()), peterMessage); QCOMPARE(peterDatagram.senderAddress(), paulAddress); QCOMPARE(paulDatagram.senderAddress(), peterAddress); @@ -586,27 +562,27 @@ void tst_QUdpSocket::ipv6Loop() peterPort = peter.localPort(); paulPort = paul.localPort(); - QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(), QHostAddress("::1"), - paulPort), qint64(peterMessage.length())); - QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(), - QHostAddress("::1"), peterPort), qint64(paulMessage.length())); + QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.size(), QHostAddress("::1"), + paulPort), qint64(peterMessage.size())); + QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.size(), + QHostAddress("::1"), peterPort), qint64(paulMessage.size())); QVERIFY(peter.waitForReadyRead(5000)); QVERIFY(paul.waitForReadyRead(5000)); - QNetworkDatagram peterDatagram = peter.receiveDatagram(paulMessage.length() * 2); - QNetworkDatagram paulDatagram = paul.receiveDatagram(peterMessage.length() * 2); + QNetworkDatagram peterDatagram = peter.receiveDatagram(paulMessage.size() * 2); + QNetworkDatagram paulDatagram = paul.receiveDatagram(peterMessage.size() * 2); if (success) { - QCOMPARE(peterDatagram.data().length(), qint64(paulMessage.length())); - QCOMPARE(paulDatagram.data().length(), qint64(peterMessage.length())); + QCOMPARE(peterDatagram.data().size(), qint64(paulMessage.size())); + QCOMPARE(paulDatagram.data().size(), qint64(peterMessage.size())); } else { // this code path seems to never be executed - QVERIFY(peterDatagram.data().length() != paulMessage.length()); - QVERIFY(paulDatagram.data().length() != peterMessage.length()); + QVERIFY(peterDatagram.data().size() != paulMessage.size()); + QVERIFY(paulDatagram.data().size() != peterMessage.size()); } - QCOMPARE(peterDatagram.data().left(paulMessage.length()), paulMessage); - QCOMPARE(paulDatagram.data().left(peterMessage.length()), peterMessage); + QCOMPARE(peterDatagram.data().left(paulMessage.size()), paulMessage); + QCOMPARE(paulDatagram.data().left(peterMessage.size()), peterMessage); QCOMPARE(peterDatagram.senderAddress(), paulAddress); QCOMPARE(paulDatagram.senderAddress(), peterAddress); @@ -634,7 +610,7 @@ void tst_QUdpSocket::dualStack() QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0)); //test v4 -> dual - QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), dualSock.localPort()), v4Data.length()); + QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.size(), QHostAddress(QHostAddress::LocalHost), dualSock.localPort()), v4Data.size()); QVERIFY2(dualSock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(dualSock).constData()); QNetworkDatagram dgram = dualSock.receiveDatagram(100); QVERIFY(dgram.isValid()); @@ -646,7 +622,7 @@ void tst_QUdpSocket::dualStack() QCOMPARE(dgram.senderAddress(), makeNonAny(v4Sock.localAddress(), QHostAddress::Null)); if (dgram.destinationPort() != -1) { QCOMPARE(dgram.destinationPort(), int(dualSock.localPort())); - QVERIFY(dgram.destinationAddress().isEqual(dualSock.localAddress())); + QVERIFY(dgram.destinationAddress().isEqual(makeNonAny(dualSock.localAddress(), QHostAddress::LocalHost))); } else { qInfo("Getting IPv4 destination address failed."); } @@ -657,7 +633,7 @@ void tst_QUdpSocket::dualStack() QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0)); //test v6 -> dual - QCOMPARE((int)v6Sock.writeDatagram(v6Data.constData(), v6Data.length(), QHostAddress(QHostAddress::LocalHostIPv6), dualSock.localPort()), v6Data.length()); + QCOMPARE((int)v6Sock.writeDatagram(v6Data.constData(), v6Data.size(), QHostAddress(QHostAddress::LocalHostIPv6), dualSock.localPort()), v6Data.size()); QVERIFY2(dualSock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(dualSock).constData()); dgram = dualSock.receiveDatagram(100); QVERIFY(dgram.isValid()); @@ -668,7 +644,7 @@ void tst_QUdpSocket::dualStack() QCOMPARE(dgram.destinationAddress(), makeNonAny(dualSock.localAddress(), QHostAddress::LocalHostIPv6)); //test dual -> v6 - QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length()); + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.size(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.size()); QVERIFY2(v6Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v6Sock).constData()); dgram = v6Sock.receiveDatagram(100); QVERIFY(dgram.isValid()); @@ -680,7 +656,7 @@ void tst_QUdpSocket::dualStack() } //test dual -> v4 - QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length()); + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.size(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.size()); QVERIFY2(v4Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v4Sock).constData()); dgram = v4Sock.receiveDatagram(100); QVERIFY(dgram.isValid()); @@ -719,19 +695,19 @@ void tst_QUdpSocket::dualStackAutoBinding() //test an autobound socket can send to both v4 and v6 addresses (v4 first) QUdpSocket dualSock; - QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length()); + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.size(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.size()); QVERIFY2(v4Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v4Sock).constData()); buffer.reserve(100); size = v4Sock.readDatagram(buffer.data(), 100, &from, &port); - QCOMPARE((int)size, dualData.length()); + QCOMPARE((int)size, dualData.size()); buffer.resize(size); QCOMPARE(buffer, dualData); - QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length()); + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.size(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.size()); QVERIFY2(v6Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v6Sock).constData()); buffer.reserve(100); size = v6Sock.readDatagram(buffer.data(), 100, &from, &port); - QCOMPARE((int)size, dualData.length()); + QCOMPARE((int)size, dualData.size()); buffer.resize(size); QCOMPARE(buffer, dualData); } @@ -740,19 +716,19 @@ void tst_QUdpSocket::dualStackAutoBinding() //test an autobound socket can send to both v4 and v6 addresses (v6 first) QUdpSocket dualSock; - QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length()); + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.size(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.size()); QVERIFY2(v6Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v6Sock).constData()); buffer.reserve(100); size = v6Sock.readDatagram(buffer.data(), 100, &from, &port); - QCOMPARE((int)size, dualData.length()); + QCOMPARE((int)size, dualData.size()); buffer.resize(size); QCOMPARE(buffer, dualData); - QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length()); + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.size(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.size()); QVERIFY2(v4Sock.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(v4Sock).constData()); buffer.reserve(100); size = v4Sock.readDatagram(buffer.data(), 100, &from, &port); - QCOMPARE((int)size, dualData.length()); + QCOMPARE((int)size, dualData.size()); buffer.resize(size); QCOMPARE(buffer, dualData); } @@ -773,7 +749,7 @@ void tst_QUdpSocket::dualStackNoIPv4onV6only() QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0)); //test v4 -> v6 (should not be received as this is a v6 only socket) - QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), v6Sock.localPort()), v4Data.length()); + QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.size(), QHostAddress(QHostAddress::LocalHost), v6Sock.localPort()), v4Data.size()); QVERIFY(!v6Sock.waitForReadyRead(1000)); } @@ -893,17 +869,17 @@ void tst_QUdpSocket::writeDatagram() #if defined (Q_OS_HPUX) QSKIP("HP-UX 11.11 on hai (PA-RISC 64) truncates too long datagrams."); #endif - QCOMPARE(bytesspy.count(), 0); - QCOMPARE(errorspy.count(), 1); + QCOMPARE(bytesspy.size(), 0); + QCOMPARE(errorspy.size(), 1); QCOMPARE(*static_cast<const int *>(errorspy.at(0).at(0).constData()), int(QUdpSocket::DatagramTooLargeError)); QCOMPARE(client.error(), QUdpSocket::DatagramTooLargeError); break; } - QCOMPARE(bytesspy.count(), 1); + QCOMPARE(bytesspy.size(), 1); QCOMPARE(*static_cast<const qint64 *>(bytesspy.at(0).at(0).constData()), qint64(i * 1024)); - QCOMPARE(errorspy.count(), 0); + QCOMPARE(errorspy.size(), 0); if (!server.waitForReadyRead(5000)) QSKIP(QString("UDP packet lost at size %1, unable to complete the test.").arg(i * 1024).toLatin1().data()); QCOMPARE(server.pendingDatagramSize(), qint64(i * 1024)); @@ -1016,7 +992,7 @@ void tst_QUdpSocket::writeDatagramToNonExistingPeer() QVERIFY(sUdp.bind()); QCOMPARE(sUdp.writeDatagram("", 1, peerAddress, peerPort), qint64(1)); QTestEventLoop::instance().enterLoop(1); - QCOMPARE(sReadyReadSpy.count(), 0); + QCOMPARE(sReadyReadSpy.size(), 0); } void tst_QUdpSocket::writeToNonExistingPeer_data() @@ -1059,8 +1035,8 @@ void tst_QUdpSocket::writeToNonExistingPeer() // the third one will succeed... QCOMPARE(sConnected.write("", 1), qint64(1)); QTestEventLoop::instance().enterLoop(1); - QCOMPARE(sConnectedReadyReadSpy.count(), 0); - QCOMPARE(sConnectedErrorSpy.count(), 1); + QCOMPARE(sConnectedReadyReadSpy.size(), 0); + QCOMPARE(sConnectedErrorSpy.size(), 1); QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError)); // we should now get a read error @@ -1259,11 +1235,12 @@ void tst_QUdpSocket::multicastTtlOption_data() QTest::addColumn<int>("ttl"); QTest::addColumn<int>("expected"); - QList<QHostAddress> addresses; - addresses += QHostAddress(QHostAddress::AnyIPv4); - addresses += QHostAddress(QHostAddress::AnyIPv6); + const QHostAddress addresses[] = { + QHostAddress(QHostAddress::AnyIPv4), + QHostAddress(QHostAddress::AnyIPv6), + }; - foreach (const QHostAddress &address, addresses) { + for (const QHostAddress &address : addresses) { const QByteArray addressB = address.toString().toLatin1(); QTest::newRow((addressB + " 0").constData()) << address << 0 << 0; QTest::newRow((addressB + " 1").constData()) << address << 1 << 1; @@ -1305,11 +1282,12 @@ void tst_QUdpSocket::multicastLoopbackOption_data() QTest::addColumn<int>("loopback"); QTest::addColumn<int>("expected"); - QList<QHostAddress> addresses; - addresses += QHostAddress(QHostAddress::AnyIPv4); - addresses += QHostAddress(QHostAddress::AnyIPv6); + const QHostAddress addresses[] = { + QHostAddress(QHostAddress::AnyIPv4), + QHostAddress(QHostAddress::AnyIPv6), + }; - foreach (const QHostAddress &address, addresses) { + for (const QHostAddress &address : addresses) { const QByteArray addressB = address.toString().toLatin1(); QTest::newRow((addressB + " 0").constData()) << address << 0 << 0; QTest::newRow((addressB + " 1").constData()) << address << 1 << 1; @@ -1352,7 +1330,7 @@ void tst_QUdpSocket::multicastJoinBeforeBind_data() QTest::newRow("valid ipv4 group address") << multicastGroup4; QTest::newRow("invalid ipv4 group address") << QHostAddress(QHostAddress::Broadcast); QTest::newRow("valid ipv6 group address") << multicastGroup6; - for (const QHostAddress &a : qAsConst(linklocalMulticastGroups)) + for (const QHostAddress &a : std::as_const(linklocalMulticastGroups)) QTest::addRow("valid ipv6 %s-link group address", a.scopeId().toLatin1().constData()) << a; QTest::newRow("invalid ipv6 group address") << QHostAddress(QHostAddress::AnyIPv6); } @@ -1372,7 +1350,7 @@ void tst_QUdpSocket::multicastLeaveAfterClose_data() QTest::addColumn<QHostAddress>("groupAddress"); QTest::newRow("ipv4") << multicastGroup4; QTest::newRow("ipv6") << multicastGroup6; - for (const QHostAddress &a : qAsConst(linklocalMulticastGroups)) + for (const QHostAddress &a : std::as_const(linklocalMulticastGroups)) QTest::addRow("ipv6-link-%s", a.scopeId().toLatin1().constData()) << a; } @@ -1409,11 +1387,12 @@ void tst_QUdpSocket::setMulticastInterface_data() { QTest::addColumn<QNetworkInterface>("iface"); QTest::addColumn<QHostAddress>("address"); - QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); - foreach (const QNetworkInterface &iface, interfaces) { + const QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &iface : interfaces) { if ((iface.flags() & QNetworkInterface::IsUp) == 0) continue; - foreach (const QNetworkAddressEntry &entry, iface.addressEntries()) { + const auto entries = iface.addressEntries(); + for (const QNetworkAddressEntry &entry : entries) { const QByteArray testName = iface.name().toLatin1() + ':' + entry.ip().toString().toLatin1(); QTest::newRow(testName.constData()) << iface << entry.ip(); } @@ -1466,13 +1445,13 @@ void tst_QUdpSocket::multicast_data() QTest::newRow("valid bind, group ipv4 address") << anyAddress << true << groupAddress << true; QTest::newRow("valid bind, invalid group ipv4 address") << anyAddress << true << anyAddress << false; QTest::newRow("valid bind, group ipv6 address") << any6Address << true << group6Address << true; - for (const QHostAddress &a : qAsConst(linklocalMulticastGroups)) + for (const QHostAddress &a : std::as_const(linklocalMulticastGroups)) QTest::addRow("valid bind, %s-link group ipv6 address", a.scopeId().toLatin1().constData()) << any6Address << true << a << true; QTest::newRow("valid bind, invalid group ipv6 address") << any6Address << true << any6Address << false; QTest::newRow("dual bind, group ipv4 address") << dualAddress << true << groupAddress << false; QTest::newRow("dual bind, group ipv6 address") << dualAddress << true << group6Address << true; - for (const QHostAddress &a : qAsConst(linklocalMulticastGroups)) + for (const QHostAddress &a : std::as_const(linklocalMulticastGroups)) QTest::addRow("dual bind, %s-link group ipv6 address", a.scopeId().toLatin1().constData()) << dualAddress << true << a << true; } @@ -1517,15 +1496,16 @@ void tst_QUdpSocket::multicast() if (!joinResult) return; - QList<QByteArray> datagrams = QList<QByteArray>() - << QByteArray("0123") - << QByteArray("4567") - << QByteArray("89ab") - << QByteArray("cdef"); + const QByteArray datagrams[] = { + "0123"_ba, + "4567"_ba, + "89ab"_ba, + "cdef"_ba, + }; QUdpSocket sender; sender.bind(); - foreach (const QByteArray &datagram, datagrams) { + for (const QByteArray &datagram : datagrams) { QNetworkDatagram dgram(datagram, groupAddress, receiver.localPort()); dgram.setInterfaceIndex(interfaceForGroup(groupAddress).index()); QCOMPARE(int(sender.writeDatagram(dgram)), @@ -1568,7 +1548,7 @@ void tst_QUdpSocket::echo() { QFETCH(bool, connect); QHostInfo info = QHostInfo::fromName(QtNetworkSettings::echoServerName()); - QVERIFY(info.addresses().count()); + QVERIFY(info.addresses().size()); QHostAddress remote = info.addresses().first(); QUdpSocket sock; @@ -1595,7 +1575,7 @@ void tst_QUdpSocket::echo() in = sock.read(sock.pendingDatagramSize()); } else { in.resize(sock.pendingDatagramSize()); - sock.readDatagram(in.data(), in.length(), &from, &port); + sock.readDatagram(in.data(), in.size(), &from, &port); } if (in==out) successes++; @@ -1618,7 +1598,8 @@ void tst_QUdpSocket::linkLocalIPv6() QList <QHostAddress> addresses; QSet <QString> scopes; QHostAddress localMask("fe80::"); - foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) { + const auto ifaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &iface : ifaces) { //Windows preallocates link local addresses to interfaces that are down. //These may or may not work depending on network driver if (iface.flags() & QNetworkInterface::IsUp) { @@ -1633,7 +1614,8 @@ void tst_QUdpSocket::linkLocalIPv6() continue; #endif - foreach (QNetworkAddressEntry addressEntry, iface.addressEntries()) { + const auto entries = iface.addressEntries(); + for (const QNetworkAddressEntry &addressEntry : entries) { QHostAddress addr(addressEntry.ip()); if (!addr.scopeId().isEmpty() && addr.isInSubnet(localMask, 64)) { scopes << addr.scopeId(); @@ -1648,7 +1630,7 @@ void tst_QUdpSocket::linkLocalIPv6() QList <QUdpSocket*> sockets; quint16 port = 0; - foreach (const QHostAddress& addr, addresses) { + for (const QHostAddress &addr : std::as_const(addresses)) { QUdpSocket *s = new QUdpSocket; QVERIFY2(s->bind(addr, port), addr.toString().toLatin1() + '/' + QByteArray::number(port) + ": " + qPrintable(s->errorString())); @@ -1657,7 +1639,7 @@ void tst_QUdpSocket::linkLocalIPv6() } QByteArray testData("hello"); - foreach (QUdpSocket *s, sockets) { + for (QUdpSocket *s : std::as_const(sockets)) { QUdpSocket neutral; QVERIFY(neutral.bind(QHostAddress(QHostAddress::AnyIPv6))); QSignalSpy neutralReadSpy(&neutral, SIGNAL(readyRead())); @@ -1665,27 +1647,26 @@ void tst_QUdpSocket::linkLocalIPv6() QSignalSpy spy(s, SIGNAL(readyRead())); QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort())); - QTRY_VERIFY(neutralReadSpy.count() > 0); //note may need to accept a firewall prompt + QTRY_VERIFY(neutralReadSpy.size() > 0); //note may need to accept a firewall prompt - QNetworkDatagram dgram = neutral.receiveDatagram(testData.length() * 2); + QNetworkDatagram dgram = neutral.receiveDatagram(testData.size() * 2); QVERIFY(dgram.isValid()); QCOMPARE(dgram.senderAddress(), s->localAddress()); QCOMPARE(dgram.senderPort(), int(s->localPort())); QCOMPARE(dgram.destinationAddress(), s->localAddress()); QCOMPARE(dgram.destinationPort(), int(neutral.localPort())); - QCOMPARE(dgram.data().length(), testData.length()); + QCOMPARE(dgram.data().size(), testData.size()); QCOMPARE(dgram.data(), testData); QVERIFY(neutral.writeDatagram(dgram.makeReply(testData))); - QTRY_VERIFY(spy.count() > 0); //note may need to accept a firewall prompt + QTRY_VERIFY(spy.size() > 0); //note may need to accept a firewall prompt - dgram = s->receiveDatagram(testData.length() * 2); + dgram = s->receiveDatagram(testData.size() * 2); QCOMPARE(dgram.data(), testData); //sockets bound to other interfaces shouldn't have received anything - foreach (QUdpSocket *s2, sockets) { + for (QUdpSocket *s2 : std::as_const(sockets)) QCOMPARE((int)s2->bytesAvailable(), 0); - } //Sending to the same address with different scope should normally fail //However it will pass if there is a route between two interfaces, @@ -1704,7 +1685,8 @@ void tst_QUdpSocket::linkLocalIPv4() QList <QHostAddress> addresses; QHostAddress localMask("169.254.0.0"); - foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) { + const auto ifaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &iface : ifaces) { //Windows preallocates link local addresses to interfaces that are down. //These may or may not work depending on network driver (they do not work for the Bluetooth PAN driver) if (iface.flags() & QNetworkInterface::IsUp) { @@ -1718,7 +1700,8 @@ void tst_QUdpSocket::linkLocalIPv4() if (iface.name().startsWith("utun")) continue; #endif - foreach (QNetworkAddressEntry addr, iface.addressEntries()) { + const auto entries = iface.addressEntries(); + for (const QNetworkAddressEntry &addr : entries) { if (addr.ip().isInSubnet(localMask, 16)) { addresses << addr.ip(); qDebug() << "Found IPv4 link local address" << addr.ip(); @@ -1731,7 +1714,7 @@ void tst_QUdpSocket::linkLocalIPv4() QList <QUdpSocket*> sockets; quint16 port = 0; - foreach (const QHostAddress& addr, addresses) { + for (const QHostAddress &addr : std::as_const(addresses)) { QUdpSocket *s = new QUdpSocket; QVERIFY2(s->bind(addr, port), qPrintable(s->errorString())); port = s->localPort(); //bind same port, different networks @@ -1742,15 +1725,15 @@ void tst_QUdpSocket::linkLocalIPv4() QVERIFY(neutral.bind(QHostAddress(QHostAddress::AnyIPv4))); QByteArray testData("hello"); - foreach (QUdpSocket *s, sockets) { + for (QUdpSocket *s : std::as_const(sockets)) { QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort())); QVERIFY2(neutral.waitForReadyRead(10000), QtNetworkSettings::msgSocketError(neutral).constData()); - QNetworkDatagram dgram = neutral.receiveDatagram(testData.length() * 2); + QNetworkDatagram dgram = neutral.receiveDatagram(testData.size() * 2); QVERIFY(dgram.isValid()); QCOMPARE(dgram.senderAddress(), s->localAddress()); QCOMPARE(dgram.senderPort(), int(s->localPort())); - QCOMPARE(dgram.data().length(), testData.length()); + QCOMPARE(dgram.data().size(), testData.size()); QCOMPARE(dgram.data(), testData); // Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of @@ -1769,14 +1752,13 @@ void tst_QUdpSocket::linkLocalIPv4() QVERIFY(neutral.writeDatagram(dgram.makeReply(testData))); QVERIFY2(s->waitForReadyRead(10000), QtNetworkSettings::msgSocketError(*s).constData()); - dgram = s->receiveDatagram(testData.length() * 2); + dgram = s->receiveDatagram(testData.size() * 2); QVERIFY(dgram.isValid()); QCOMPARE(dgram.data(), testData); //sockets bound to other interfaces shouldn't have received anything - foreach (QUdpSocket *s2, sockets) { + for (QUdpSocket *s2 : std::as_const(sockets)) QCOMPARE((int)s2->bytesAvailable(), 0); - } } qDeleteAll(sockets); } @@ -1804,7 +1786,7 @@ void tst_QUdpSocket::readyRead() QTest::qWait(100); // make sure only one signal was emitted - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(receiver.hasPendingDatagrams()); #ifdef RELIABLE_BYTES_AVAILABLE QCOMPARE(receiver.bytesAvailable(), qint64(2)); @@ -1816,7 +1798,7 @@ void tst_QUdpSocket::readyRead() // no new signal should be emitted because we haven't read the first datagram yet QTest::qWait(100); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(receiver.hasPendingDatagrams()); QVERIFY(receiver.bytesAvailable() >= 1); // most likely is 1, but it could be 1 + 2 in the future QCOMPARE(receiver.pendingDatagramSize(), qint64(2)); @@ -1828,7 +1810,7 @@ void tst_QUdpSocket::readyRead() // write a new datagram and ensure the signal is emitted now sender.writeDatagram("abc", makeNonAny(receiver.localAddress()), port); QTest::qWait(100); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QVERIFY(receiver.hasPendingDatagrams()); #ifdef RELIABLE_BYTES_AVAILABLE QCOMPARE(receiver.bytesAvailable(), qint64(3)); @@ -1908,7 +1890,7 @@ void tst_QUdpSocket::asyncReadDatagram() QTestEventLoop::instance().enterLoop(1); QVERIFY(!QTestEventLoop::instance().timeout()); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); delete m_asyncSender; delete m_asyncReceiver; @@ -1926,5 +1908,78 @@ void tst_QUdpSocket::writeInHostLookupState() QVERIFY(!socket.putChar('0')); } +void tst_QUdpSocket::readyReadConnectionThrottling() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + using namespace std::chrono_literals; + + // QTBUG-105871: + // We have some signal/slot connection throttling in QAbstractSocket, but it + // was caring about the bytes, not about the datagrams. + // Test that we don't disable read notifications until we have at least one + // datagram available. Otherwise our good users who use the datagram APIs + // can get into scenarios where they no longer get the readyRead signal + // unless they call a read function once in a while. + + QUdpSocket receiver; + QVERIFY(receiver.bind(QHostAddress(QHostAddress::LocalHost), 0)); + + QSemaphore semaphore; + + // Repro-ing deterministically eludes me, so we are bruteforcing it: + // The thread acts as a remote sender, flooding the receiver with datagrams, + // and at some point the receiver would get into the broken state mentioned + // earlier. + std::unique_ptr<QThread> thread(QThread::create([&semaphore, port = receiver.localPort()]() { + QUdpSocket sender; + sender.connectToHost(QHostAddress(QHostAddress::LocalHost), port); + QCOMPARE(sender.state(), QUdpSocket::ConnectedState); + + constexpr qsizetype PayloadSize = 242; + const QByteArray payload(PayloadSize, 'a'); + + semaphore.acquire(); // Wait for main thread to be ready + while (true) { + // We send 100 datagrams at a time, then sleep. + // This is mostly to let the main thread catch up between bursts so + // it doesn't get stuck in the loop. + for (int i = 0; i < 100; ++i) { + [[maybe_unused]] + qsizetype sent = sender.write(payload); + Q_ASSERT(sent > 0); + } + if (QThread::currentThread()->isInterruptionRequested()) + break; + QThread::sleep(20ms); + } + })); + thread->start(); + auto threadStopAndWaitGuard = qScopeGuard([&thread] { + thread->requestInterruption(); + thread->quit(); + thread->wait(); + }); + + qsizetype count = 0; + QObject::connect(&receiver, &QUdpSocket::readyRead, &receiver, + [&] { + while (receiver.hasPendingDatagrams()) { + receiver.readDatagram(nullptr, 0); + ++count; + } + // If this prints `false, xxxx` we were pretty much guaranteed + // that we would not get called again: + // qDebug() << receiver.hasPendingDatagrams() << receiver.bytesAvailable(); + }, + Qt::QueuedConnection); + + semaphore.release(); + constexpr qsizetype MaxCount = 500; + QVERIFY2(QTest::qWaitFor([&] { return count >= MaxCount; }, 10s), + QByteArray::number(count).constData()); +} + QTEST_MAIN(tst_QUdpSocket) #include "tst_qudpsocket.moc" |