diff options
Diffstat (limited to 'tests')
3 files changed, 296 insertions, 60 deletions
diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 215b3bf3b78..af6a6a41efe 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -10,8 +10,19 @@ #if defined(__cpp_lib_expected) # include <expected> -#else -# include <QtCore/private/qexpected_p.h> + +template <typename T> +using QJniReturnValue = std::expected<T, jthrowable>; +using BadAccessException = std::bad_expected_access<jthrowable>; + +// even with __cpp_lib_expected >= 202211L, monadic functions seem to be rather +// broken or not reliably available +#define EXPECTED_HAS_MONADIC (__cpp_lib_expected >= 202211L) + +static_assert(QtJniTypes::Traits<QJniReturnValue<int>>::signature() == + QtJniTypes::Traits<int>::signature()); +static_assert(QtJniTypes::Traits<QJniReturnValue<QString>>::signature() == + QtJniTypes::Traits<QString>::signature()); #endif QT_BEGIN_NAMESPACE @@ -2510,35 +2521,12 @@ void tst_QJniObject::implicitExceptionHandling_setStaticField() void>); } -#if __cpp_lib_expected -template <typename T> -using QJniReturnValue = std::expected<T, jthrowable>; -using BadAccessException = std::bad_expected_access<jthrowable>; -// even with __cpp_lib_expected >= 202211L, monadic functions seem to be rather -// broken or not reliably available -#define EXPECTED_HAS_MONADIC false -#elif TL_EXPECTED_VERSION_MAJOR -#define EXPECTED_HAS_MONADIC true -template <typename T> -using QJniReturnValue = tl::expected<T, jthrowable>; -using BadAccessException = tl::bad_expected_access<jthrowable>; -#endif - -static_assert(QtJniTypes::Traits<QJniReturnValue<int>>::signature() == - QtJniTypes::Traits<int>::signature()); -static_assert(QtJniTypes::Traits<QJniReturnValue<QString>>::signature() == - QtJniTypes::Traits<QString>::signature()); - - void tst_QJniObject::constructWithException() { -#if __cpp_lib_expected - qInfo() << "Testing explicit exception handling with std::expected" << __cpp_lib_expected; -#elif defined(TL_EXPECTED_VERSION_MAJOR) - qInfo() << "Testing explicit exception handling with tl::expected"; +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); #else - qInfo() << "Testing explicit exception handling with QJniReturnValue"; -#endif + qInfo() << "Testing explicit exception handling with std::expected" << __cpp_lib_expected; const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); { @@ -2575,10 +2563,14 @@ void tst_QJniObject::constructWithException() } QVERIFY(!QJniEnvironment().checkAndClearExceptions()); +#endif } void tst_QJniObject::callMethodWithException() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else TestClass testObject; { auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod"); @@ -2640,11 +2632,14 @@ void tst_QJniObject::callMethodWithException() QCOMPARE_GE(stackTrace.size(), 1); QCOMPARE(stackTrace.at(0), u"java.lang.Throwable: "_s + A_STRING_OBJECT()); } +#endif } void tst_QJniObject::callMethodWithMonadic() { -#if !EXPECTED_HAS_MONADIC +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#elif !EXPECTED_HAS_MONADIC QSKIP("Used version of std::expected does not have monadic functions"); #else enum Monadic { @@ -2750,6 +2745,9 @@ void tst_QJniObject::callMethodWithMonadic() void tst_QJniObject::callMethodWithTryCatch() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else TestClass testObject; const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); @@ -2762,10 +2760,14 @@ void tst_QJniObject::callMethodWithTryCatch() catch (BadAccessException &e) { qWarning().noquote() << QJniEnvironment::stackTrace(e.error()).join('\n'); } +#endif } void tst_QJniObject::callStaticMethodWithException() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else { auto result = TestClass::callStaticMethod<QJniReturnValue<int>>("staticIntMethod"); QVERIFY(result); @@ -2790,10 +2792,14 @@ void tst_QJniObject::callStaticMethodWithException() QCOMPARE_GE(stackTrace.size(), 1); QCOMPARE(stackTrace.at(0), u"java.lang.Throwable: "_s + A_STRING_OBJECT()); } +#endif } void tst_QJniObject::getFieldWithException() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else TestClass testObject; { auto result = testObject.getField<QJniReturnValue<jboolean>>("BOOL_FIELD"); @@ -2808,10 +2814,14 @@ void tst_QJniObject::getFieldWithException() result = testObject.getField<QJniReturnValue<QString>>("INVALID_STRING"); QVERIFY(!result && result.error()); } +#endif } void tst_QJniObject::setFieldWithException() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else TestClass testObject; { auto result = testObject.setField<QJniReturnValue<jboolean>>("BOOL_FIELD", true); @@ -2828,10 +2838,14 @@ void tst_QJniObject::setFieldWithException() QVERIFY(!result); QVERIFY(result.error()); } +#endif } void tst_QJniObject::getStaticFieldWithException() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else { auto result = TestClass::getStaticField<QJniReturnValue<jshort>>("S_SHORT_VAR"); QVERIFY(result); @@ -2847,10 +2861,14 @@ void tst_QJniObject::getStaticFieldWithException() QVERIFY(!result); QVERIFY(result.error()); } +#endif } void tst_QJniObject::setStaticFieldWithException() { +#ifndef __cpp_lib_expected + QSKIP("std::expected not available."); +#else { auto result = TestClass::setStaticField<QJniReturnValue<jboolean>>("S_BOOLEAN_VAR", true); QVERIFY(result); @@ -2866,6 +2884,7 @@ void tst_QJniObject::setStaticFieldWithException() QVERIFY(!result); QVERIFY(result.error()); } +#endif } QTEST_MAIN(tst_QJniObject) diff --git a/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp b/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp index 22aa9d44262..547dc9de4f7 100644 --- a/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp +++ b/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp @@ -9,6 +9,7 @@ #include <QtNetwork/private/bitstreams_p.h> #include <QtCore/qregularexpression.h> +#include <QtCore/qthread.h> #include <limits> @@ -41,6 +42,9 @@ private slots: void testCONTINUATIONFrame(); void goaway_data(); void goaway(); + void serverInitiatedGoaways_data(); + void serverInitiatedGoaways(); + void clientInitiatedGoaway(); private: enum PeerType { Client, Server }; @@ -1132,10 +1136,10 @@ void tst_QHttp2Connection::goaway() QVERIFY(waitForSettingsExchange(connection, serverConnection)); QSignalSpy newIncomingStreamSpy{ serverConnection, &QHttp2Connection::newIncomingStream }; - - QSignalSpy clientIncomingStreamSpy{ connection, &QHttp2Connection::newIncomingStream }; - QSignalSpy clientHeaderReceivedSpy{ clientStream, &QHttp2Stream::headersReceived }; QSignalSpy clientGoawaySpy{ connection, &QHttp2Connection::receivedGOAWAY }; + QSignalSpy clientClosedSpy{ connection, &QHttp2Connection::connectionClosed }; + QSignalSpy serverGoawaySpy{ serverConnection, &QHttp2Connection::receivedGOAWAY }; + QSignalSpy serverClosedSpy{ serverConnection, &QHttp2Connection::connectionClosed }; const HPack::HttpHeader headers = getRequiredHeaders(); clientStream->sendHEADERS(headers, false); @@ -1143,57 +1147,78 @@ void tst_QHttp2Connection::goaway() QVERIFY(newIncomingStreamSpy.wait()); auto *serverStream = newIncomingStreamSpy.front().front().value<QHttp2Stream *>(); QVERIFY(serverStream); - QVERIFY(serverConnection->sendGOAWAY(Http2::CANCEL)); + serverConnection->close(); // NO_ERROR -> graceful shutdown + + // New stream creation is illegal now auto createStreamResult = serverConnection->createLocalStreamInternal(); QVERIFY(createStreamResult.has_error()); QCOMPARE(createStreamResult.error(), QHttp2Connection::CreateStreamError::ReceivedGOAWAY); + // Client received initial GOAWAY QVERIFY(clientGoawaySpy.wait()); - QCOMPARE(clientGoawaySpy.size(), 1); - // The error code used: - QCOMPARE(clientGoawaySpy.first().first().value<Http2::Http2Error>(), Http2::CANCEL); - // Last ID that will be processed - QCOMPARE(clientGoawaySpy.first().last().value<quint32>(), clientStream->streamID()); + QCOMPARE(clientGoawaySpy.first().first().value<Http2::Http2Error>(), Http2::HTTP2_NO_ERROR); + QCOMPARE(clientGoawaySpy.first().last().value<quint32>(), Http2::lastValidStreamID); clientGoawaySpy.clear(); - // Test that creating a stream the normal way results in an error: - QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> - invalidStream = connection->createStream(); + // New client-stream creation is illegal now + auto invalidStream = connection->createStream(); QVERIFY(!invalidStream.ok()); QVERIFY(invalidStream.has_error()); QCOMPARE(invalidStream.error(), QHttp2Connection::CreateStreamError::ReceivedGOAWAY); + // Client receives final GOAWAY with actual lastStreamID (after PING RTT) + QVERIFY(clientGoawaySpy.wait()); + QCOMPARE(clientGoawaySpy.first().at(0).value<Http2::Http2Error>(), Http2::HTTP2_NO_ERROR); + QCOMPARE(clientGoawaySpy.first().at(1).value<quint32>(), clientStream->streamID()); + clientGoawaySpy.clear(); + // Directly create a stream to avoid the GOAWAY check: quint32 nextStreamId = clientStream->streamID() + 2; - QHttp2Stream *secondClientStream = connection->createStreamInternal_impl(nextStreamId); - QSignalSpy streamResetSpy{ secondClientStream, &QHttp2Stream::rstFrameReceived }; - secondClientStream->sendHEADERS(headers, endStreamOnHEADERS); + QHttp2Stream *ignoredClientStream = connection->createStreamInternal_impl(nextStreamId); + QSignalSpy streamResetSpy{ ignoredClientStream, &QHttp2Stream::rstFrameReceived }; + ignoredClientStream->sendHEADERS(headers, endStreamOnHEADERS); // The stream should be ignored: using namespace std::chrono_literals; QVERIFY(!streamResetSpy.wait(100ms)); // We don't get reset because we are ignored if (endStreamOnHEADERS) return; - secondClientStream->sendDATA("my data", createNewStreamAfterDelay); + ignoredClientStream->sendDATA("my data", createNewStreamAfterDelay); // We cheat and try to send data after the END_STREAM flag has been sent if (!createNewStreamAfterDelay) { // Manually send a frame with END_STREAM so the QHttp2Stream thinks it's fine to send more // DATA connection->frameWriter.start(Http2::FrameType::DATA, Http2::FrameFlag::END_STREAM, - secondClientStream->streamID()); + ignoredClientStream->streamID()); connection->frameWriter.write(*connection->getSocket()); QVERIFY(!streamResetSpy.wait(100ms)); // We don't get reset because we are ignored - // Even without the GOAWAY this should fail (more activity after END_STREAM) - secondClientStream->sendDATA("my data", true); - QTest::ignoreMessage(QtCriticalMsg, - QRegularExpression(u".*Connection error: DATA on invalid stream.*"_s)); - QVERIFY(clientGoawaySpy.wait()); - QCOMPARE(clientGoawaySpy.size(), 1); - QCOMPARE(clientGoawaySpy.first().first().value<Http2::Http2Error>(), + const auto tstStream = [](const auto &spy, Http2::Http2Error code, + QLatin1StringView errMsg) { + QCOMPARE(spy.first().at(0).template value<Http2::Http2Error>(), code); + QCOMPARE(spy.first().at(1).template value<QString>(), errMsg); + }; + QLatin1StringView serverErrorMsg("DATA on invalid stream"); + QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(".*" + serverErrorMsg + ".*")); + QSignalSpy clientStreamErrorSpy(clientStream, &QHttp2Stream::errorOccurred); + QSignalSpy secondclientStreamErrorSpy(ignoredClientStream, &QHttp2Stream::errorOccurred); + QSignalSpy serverStreamErrorSpy(serverStream, &QHttp2Stream::errorOccurred); + + // Triggers a connectionError of 'ENHANCE_YOUR_CALM' on the server + // (more activity after END_STREAM) + ignoredClientStream->sendDATA("my data", true); + QTRY_COMPARE(serverClosedSpy.count(), 1); + tstStream(serverStreamErrorSpy, Http2::ENHANCE_YOUR_CALM, serverErrorMsg); + + QTRY_COMPARE(clientGoawaySpy.count(), 1); + QCOMPARE(clientGoawaySpy.first().at(0).value<Http2::Http2Error>(), Http2::ENHANCE_YOUR_CALM); - QCOMPARE(clientGoawaySpy.first().last().value<quint32>(), clientStream->streamID()); - return; // connection is dead by now + QCOMPARE(clientGoawaySpy.first().at(1).value<quint32>(), clientStream->streamID()); + QTRY_COMPARE(clientClosedSpy.count(), 1); + QLatin1StringView clientErrorMsg("Received GOAWAY"); + tstStream(clientStreamErrorSpy, Http2::ENHANCE_YOUR_CALM, clientErrorMsg); + tstStream(secondclientStreamErrorSpy, Http2::ENHANCE_YOUR_CALM, clientErrorMsg); + return; } // Override the deadline timer so we don't have to wait too long @@ -1216,6 +1241,196 @@ void tst_QHttp2Connection::goaway() QCOMPARE(clientGoawaySpy.first().last().value<quint32>(), clientStream->streamID()); } +void tst_QHttp2Connection::serverInitiatedGoaways_data() +{ + QTest::addColumn<QString>("scenario"); + + QTest::newRow("graceful-shutdown") << u"graceful-shutdown"_s; + QTest::newRow("graceful-then-error") << u"graceful-then-error"_s; + QTest::newRow("increasing-lastStreamId") << u"increasing-lastStreamId"_s; +} + +void tst_QHttp2Connection::serverInitiatedGoaways() +{ + QFETCH(QString, scenario); + + auto [client, server] = makeFakeConnectedSockets(); + auto clientConn = makeHttp2Connection(client.get(), {}, Client); + auto serverConn = makeHttp2Connection(server.get(), {}, Server); + + // Client creates stream + auto *clientStream = clientConn->createStream().unwrap(); + QVERIFY(clientStream); + QVERIFY(waitForSettingsExchange(clientConn, serverConn)); + QVERIFY(clientStream->sendHEADERS(getRequiredHeaders(), false)); + + // Server receives stream + QSignalSpy newStreamSpy{ serverConn, &QHttp2Connection::newIncomingStream }; + QVERIFY(newStreamSpy.wait()); + auto *serverStream = newStreamSpy.front().front().value<QHttp2Stream *>(); + QVERIFY(serverStream); + + QSignalSpy clientGoawaySpy{ clientConn, &QHttp2Connection::receivedGOAWAY }; + QSignalSpy clientClosedSpy{ clientConn, &QHttp2Connection::connectionClosed }; + QSignalSpy serverGoawaySpy{ serverConn, &QHttp2Connection::receivedGOAWAY }; + QSignalSpy serverClosedSpy{ serverConn, &QHttp2Connection::connectionClosed }; + QSignalSpy clientErrorSpy{ clientStream, &QHttp2Stream::errorOccurred }; + + serverConn->close(); // Server initiates graceful shutdown + + // Client receives initial GOAWAY with 2^31-1 + QVERIFY(clientGoawaySpy.wait()); + QCOMPARE(clientGoawaySpy.count(), 1); + QCOMPARE(clientGoawaySpy.at(0).at(0).value<Http2::Http2Error>(), Http2::HTTP2_NO_ERROR); + QCOMPARE(clientGoawaySpy.at(0).at(1).value<quint32>(), Http2::lastValidStreamID); + + // After receiving a GOAWAY we should not be able to create new streams + auto clientStream2 = clientConn->createStream(); + QCOMPARE_EQ(clientStream2.error(), QHttp2Connection::CreateStreamError::ReceivedGOAWAY); + + // Client receives final GOAWAY with actual lastStreamID (after PING RTT) + QVERIFY(clientGoawaySpy.wait(500)); + QCOMPARE(clientGoawaySpy.count(), 2); + QCOMPARE(clientGoawaySpy.at(1).at(0).value<Http2::Http2Error>(), Http2::HTTP2_NO_ERROR); + const quint32 finalLastStreamId = clientGoawaySpy.at(1).at(1).value<quint32>(); + QCOMPARE(finalLastStreamId, clientStream->streamID()); + + // Stream still active - graceful shutdown allows completion + QVERIFY(clientStream->isActive()); + QCOMPARE(clientClosedSpy.count(), 0); + + if (scenario == "increasing-lastStreamId"_L1) { + QLatin1StringView errMsg("Repeated GOAWAY with invalid last stream ID"); + QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(".*" + errMsg + ".*")); + + // Send GOAWAY with higher lastStreamId than the final one (protocol violation) + const quint32 invalidHigherId = finalLastStreamId + 2; + serverConn->sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, invalidHigherId); + + QTRY_COMPARE(clientErrorSpy.count(), 1); + QCOMPARE(clientErrorSpy.count(), 1); + QCOMPARE(clientErrorSpy.first().first().value<Http2::Http2Error>(), Http2::PROTOCOL_ERROR); + QCOMPARE(clientErrorSpy.first().last().value<QString>(), errMsg); + + // Client detects violation and responds with PROTOCOL_ERROR + QVERIFY(serverGoawaySpy.wait()); + QCOMPARE(serverGoawaySpy.last().at(0).value<Http2::Http2Error>(), Http2::PROTOCOL_ERROR); + + QTRY_COMPARE(clientClosedSpy.count(), 1); + QTRY_COMPARE(serverClosedSpy.count(), 1); + return; + } else if (scenario == "graceful-then-error") { + // RFC 9113 6.8: An endpoint MAY send multiple GOAWAY frames if circumstances change + serverConn->close(Http2::INTERNAL_ERROR); + + // Client receives error GOAWAY + QTRY_COMPARE(clientGoawaySpy.count(), 3); + QCOMPARE(clientGoawaySpy.at(2).at(0).value<Http2::Http2Error>(), Http2::INTERNAL_ERROR); + + // Error GOAWAY lastStreamId must not exceed previous + QVERIFY(clientGoawaySpy.at(2).at(1).value<quint32>() <= finalLastStreamId); + + // Server closes immediately after error + QTRY_COMPARE(serverClosedSpy.count(), 1); + + // Client stream should receive error + QVERIFY(!clientStream->isActive()); + QTRY_COMPARE(clientErrorSpy.count(), 1); + QCOMPARE(clientErrorSpy.count(), 1); + QCOMPARE(clientErrorSpy.first().first().value<Http2::Http2Error>(), Http2::INTERNAL_ERROR); + QTRY_COMPARE(clientClosedSpy.count(), 1); + + // Additional close() calls should be ignored + serverConn->close(); + serverConn->close(Http2::FLOW_CONTROL_ERROR); + qApp->processEvents(); + QCOMPARE(clientGoawaySpy.count(), 3); // No new GOAWAY + return; + } else if ("graceful-shutdown") { + QSignalSpy serverDataSpy{ serverStream, &QHttp2Stream::dataReceived }; + QVERIFY(clientStream->sendDATA("final-data", true)); + QVERIFY(serverDataSpy.wait()); + + QSignalSpy clientHeadersSpy{ clientStream, &QHttp2Stream::headersReceived }; + const HPack::HttpHeader responseHeaders{ { ":status", "200" } }; + QVERIFY(serverStream->sendHEADERS(responseHeaders, true)); + QVERIFY(clientHeadersSpy.wait()); + + QCOMPARE(clientStream->state(), QHttp2Stream::State::Closed); + + // Connection closes after all streams complete + QTRY_COMPARE(serverClosedSpy.count(), 1); + QTRY_COMPARE(clientClosedSpy.count(), 1); + + // No additional GOAWAYs + QCOMPARE(clientGoawaySpy.count(), 2); + } +} + +void tst_QHttp2Connection::clientInitiatedGoaway() +{ + // Clients don't need two-phase GOAWAY because they control their own + // stream creation (no race condition). Client sends single GOAWAY with the + // last server-initiated (even) stream ID it processed. + auto [client, server] = makeFakeConnectedSockets(); + auto clientConn = makeHttp2Connection(client.get(), {}, Client); + auto serverConn = makeHttp2Connection(server.get(), {}, Server); + + // Client creates stream + auto *clientStream = clientConn->createStream().unwrap(); + QVERIFY(clientStream); + QVERIFY(waitForSettingsExchange(clientConn, serverConn)); + QVERIFY(clientStream->sendHEADERS(getRequiredHeaders(), false)); + + // Server receives stream + QSignalSpy newStreamSpy{ serverConn, &QHttp2Connection::newIncomingStream }; + QVERIFY(newStreamSpy.wait()); + auto *serverStream = newStreamSpy.front().front().value<QHttp2Stream *>(); + QVERIFY(serverStream); + + QSignalSpy clientGoawaySpy{ clientConn, &QHttp2Connection::receivedGOAWAY }; + QSignalSpy clientClosedSpy{ clientConn, &QHttp2Connection::connectionClosed }; + QSignalSpy serverGoawaySpy{ serverConn, &QHttp2Connection::receivedGOAWAY }; + QSignalSpy serverClosedSpy{ serverConn, &QHttp2Connection::connectionClosed }; + + // Client initiates graceful shutdown + clientConn->close(); + + // Client should not be able to create new streams now + auto rejectedClientStream = clientConn->createStream(); + QCOMPARE_EQ(rejectedClientStream.error(), QHttp2Connection::CreateStreamError::ReceivedGOAWAY); + + // Server receives GOAWAY + QVERIFY(serverGoawaySpy.wait()); + QCOMPARE(serverGoawaySpy.count(), 1); + QCOMPARE(serverGoawaySpy.at(0).at(0).value<Http2::Http2Error>(), Http2::HTTP2_NO_ERROR); + + const quint32 lastStreamId = serverGoawaySpy.at(0).at(1).value<quint32>(); + QCOMPARE(lastStreamId, 0u); + + // Existing streams can still complete + QVERIFY(clientStream->isActive()); + QVERIFY(serverStream->isActive()); + + // Complete the stream exchange + QSignalSpy serverDataSpy{ serverStream, &QHttp2Stream::dataReceived }; + QVERIFY(clientStream->sendDATA("final-data", true)); + QVERIFY(serverDataSpy.wait()); + + QSignalSpy clientHeadersSpy{ clientStream, &QHttp2Stream::headersReceived }; + const HPack::HttpHeader responseHeaders{ { ":status", "200" } }; + QVERIFY(serverStream->sendHEADERS(responseHeaders, true)); + QVERIFY(clientHeadersSpy.wait()); + + QCOMPARE(clientStream->state(), QHttp2Stream::State::Closed); + QCOMPARE(serverStream->state(), QHttp2Stream::State::Closed); + + QTRY_COMPARE(clientClosedSpy.count(), 1); + + QCOMPARE(serverGoawaySpy.count(), 1); + QTRY_COMPARE(serverClosedSpy.count(), 1); +} + QTEST_MAIN(tst_QHttp2Connection) #include "tst_qhttp2connection.moc" diff --git a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp index 16a69e4337d..4de2b255dd5 100644 --- a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp +++ b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp @@ -1100,12 +1100,14 @@ void tst_QTabBar::kineticWheel() leftEdge = QPoint(0, 0); rightEdge = leftButton->geometry().topLeft(); } - // avoid border lines - leftEdge += QPoint(2, 2); + // make sure the point is inside tabbar rect + const auto tabbarCenter = tabbar.geometry().center(); if (horizontal) { - rightEdge += QPoint(-2, 2); + leftEdge = QPoint(leftEdge.x() + 10, tabbarCenter.y()); + rightEdge = QPoint(rightEdge.x() - 10, tabbarCenter.y()); } else { - rightEdge += QPoint(2, -2); + leftEdge = QPoint(tabbarCenter.x(), leftEdge.y() + 10); + rightEdge = QPoint(tabbarCenter.x(), rightEdge.y() - 10); } QCOMPARE(tabbar.tabAt(leftEdge), 0); @@ -1393,7 +1395,7 @@ void tst_QTabBar::hoverTab() QCOMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_None); // inserting a tab at index 2 again should paint the new tab hovered - tabbar.insertTab(2, "C2"); + tabbar.insertTab(2, "X"); QTRY_COMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_MouseOver); QCOMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); } |
