Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package qt6-grpc for openSUSE:Factory checked in at 2026-02-03 21:30:20 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/qt6-grpc (Old) and /work/SRC/openSUSE:Factory/.qt6-grpc.new.1995 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "qt6-grpc" Tue Feb 3 21:30:20 2026 rev:24 rq:1330580 version:6.10.2 Changes: -------- --- /work/SRC/openSUSE:Factory/qt6-grpc/qt6-grpc.changes 2025-11-25 15:54:19.378213615 +0100 +++ /work/SRC/openSUSE:Factory/.qt6-grpc.new.1995/qt6-grpc.changes 2026-02-03 21:31:35.584892092 +0100 @@ -1,0 +2,6 @@ +Sat Jan 31 08:10:35 UTC 2026 - Christophe Marin <[email protected]> + +- Update to 6.10.2: + * https://www.qt.io/blog/qt-6.10.2-released + +------------------------------------------------------------------- Old: ---- qtgrpc-everywhere-src-6.10.1.tar.xz New: ---- qtgrpc-everywhere-src-6.10.2.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ qt6-grpc.spec ++++++ --- /var/tmp/diff_new_pack.KPetFq/_old 2026-02-03 21:31:36.232919306 +0100 +++ /var/tmp/diff_new_pack.KPetFq/_new 2026-02-03 21:31:36.236919474 +0100 @@ -16,7 +16,7 @@ # -%define real_version 6.10.1 +%define real_version 6.10.2 %define short_version 6.10 %define short_name qtgrpc %define tar_name qtgrpc-everywhere-src @@ -31,7 +31,7 @@ %global __requires_exclude qt6qmlimport\\((qtgrpc\\.examples.*|QtGrpcChat).* # Name: qt6-grpc%{?pkg_suffix} -Version: 6.10.1 +Version: 6.10.2 Release: 0 Summary: gRPC and Protobuf generator and bindings for Qt framework License: GPL-3.0-or-later ++++++ qtgrpc-everywhere-src-6.10.1.tar.xz -> qtgrpc-everywhere-src-6.10.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/.cmake.conf new/qtgrpc-everywhere-src-6.10.2/.cmake.conf --- old/qtgrpc-everywhere-src-6.10.1/.cmake.conf 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/.cmake.conf 2026-01-22 20:42:07.000000000 +0100 @@ -1,7 +1,9 @@ -set(QT_REPO_MODULE_VERSION "6.10.1") +set(QT_REPO_MODULE_VERSION "6.10.2") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") -set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1") -list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1") -list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1") -list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_CONTEXTLESS_CONNECT=1") +set(QT_EXTRA_INTERNAL_TARGET_DEFINES + "QT_LEAN_HEADERS=1" + "QT_NO_CONTEXTLESS_CONNECT=1" + "QT_NO_FOREACH=1" + "QT_NO_QASCONST=1" +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/.tag new/qtgrpc-everywhere-src-6.10.2/.tag --- old/qtgrpc-everywhere-src-6.10.1/.tag 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/.tag 2026-01-22 20:42:07.000000000 +0100 @@ -1 +1 @@ -4fb8bebd2cf4a82a3501bb303a5b666dd6f8dd4c +24c787e87e823c1bc7d3e37c7e4b4b6c0cb79e69 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/dependencies.yaml new/qtgrpc-everywhere-src-6.10.2/dependencies.yaml --- old/qtgrpc-everywhere-src-6.10.1/dependencies.yaml 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/dependencies.yaml 2026-01-22 20:42:07.000000000 +0100 @@ -1,7 +1,7 @@ dependencies: ../qtbase: - ref: 90b845d15ffb97693dba527385db83510ebd121a + ref: 000d6c62f7880bb8d3054724e8da0b8ae244130e required: true ../qtdeclarative: - ref: 22fb5e739a2ea700448b22d34ade0d5c1927cb48 + ref: 09c70541c76659bcd8c49f05841b0e778c9ffd4c required: false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/examples/protobuf/sensors/doc/sensors.qdoc new/qtgrpc-everywhere-src-6.10.2/examples/protobuf/sensors/doc/sensors.qdoc --- old/qtgrpc-everywhere-src-6.10.1/examples/protobuf/sensors/doc/sensors.qdoc 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/examples/protobuf/sensors/doc/sensors.qdoc 2026-01-22 20:42:07.000000000 +0100 @@ -27,7 +27,8 @@ Use the emulator application to change the values of sensor data and send the data to the client's UDP port. - \image emulator.webp + \image emulator.webp {Screenshot showing an emulator console application + to send coordinates, weather temperaute, and a warning message} The applications use the generated messages from the \c protobuf_sensors library to communicate. The library is generated from the protobuf schema diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcclientbase.cpp new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcclientbase.cpp --- old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcclientbase.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcclientbase.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -200,6 +200,12 @@ bool QGrpcClientBase::attachChannel(std::shared_ptr<QAbstractGrpcChannel> channel) { Q_D(QGrpcClientBase); + + if (channel == d->channel) { + qGrpcWarning("Refusing to attach channel. The same channel is already assigned."); + return false; + } + // channel is not a QObject so we compare against the threadId set on construction. if (channel->d_func()->threadId != QThread::currentThreadId()) { qGrpcWarning("QtGrpc doesn't allow attaching the channel from a different thread"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpchttp2channel.cpp new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpchttp2channel.cpp --- old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpchttp2channel.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpchttp2channel.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -16,6 +16,7 @@ #include <QtNetwork/private/hpack_p.h> #include <QtNetwork/private/http2protocol_p.h> +#include <QtNetwork/private/qdecompresshelper_p.h> #include <QtNetwork/private/qhttp2connection_p.h> #if QT_CONFIG(localserver) # include <QtNetwork/qlocalsocket.h> @@ -178,6 +179,7 @@ const QByteArray DefaultContentType("application/grpc"); const QByteArray GrpcStatusDetailsHeader("grpc-status-details-bin"); const QByteArray GrpcAcceptEncodingHeader("grpc-accept-encoding"); +const QByteArray GrpcAcceptEncodingValue("identity,deflate,gzip"); const QByteArray GrpcEncodingHeader("grpc-encoding"); constexpr qsizetype GrpcMessageSizeHeaderSize = 5; @@ -362,6 +364,8 @@ QQueue<QByteArray> m_queue; QPointer<QHttp2Stream> m_stream; GrpcDataParser m_grpcDataParser; + QByteArray m_negotiatedEncoding; + std::unique_ptr<QDecompressHelper> m_decompressor; State m_state = State::Idle; const bool m_endStreamAtFirstData; bool m_writesDoneSent = false; @@ -537,13 +541,41 @@ m_grpcDataParser.feed(data); while (auto frame = m_grpcDataParser.parseNextFrame()) { + QByteArray finalPayload; + + if (frame->isCompressed) { + if (!m_decompressor || m_negotiatedEncoding.isEmpty()) { + finish({ QtGrpc::StatusCode::Internal, + "Protocol error: received compressed message " + "but no encoding was negotiated." }); + return; + } + m_decompressor->feed(std::move(frame->payload)); + // Read all decompressed data for this single message. + while (m_decompressor->hasData()) { + char buffer[4096]; + qsizetype bytesRead = m_decompressor->read(buffer, sizeof(buffer)); + if (bytesRead < 0) { + finish({ QtGrpc::StatusCode::Internal, + "Decompression failed: %1"_L1 + .arg(m_decompressor->errorString()) }); + return; + } + finalPayload.append(buffer, bytesRead); + } + m_decompressor->clear(); + m_decompressor->setEncoding(m_negotiatedEncoding); + } else { + finalPayload = std::move(frame->payload); + } + qCDebug(lcStream, "[%p] Processed gRPC message (compressed=%s, " "payloadSize=%" PRIdQSIZETYPE ", bufferRemaining=%" PRIdQSIZETYPE ")", - this, frame->isCompressed ? "true" : "false", frame->payload.size(), + this, frame->isCompressed ? "true" : "false", finalPayload.size(), m_grpcDataParser.bytesAvailable()); - emit m_context->messageReceived(frame->payload); + emit m_context->messageReceived(finalPayload); } if (endStream) { @@ -573,7 +605,6 @@ const static QByteArray TEHeader("te"); const static QByteArray TEValue("trailers"); const static QByteArray GrpcServiceNameHeader("service-name"); - const static QByteArray GrpcAcceptEncodingValue("identity,deflate,gzip"); const static QByteArray UserAgentHeader("user-agent"); const static QByteArray UserAgentValue("grpc-c++-qtgrpc/"_ba + QT_VERSION_STR + " ("_ba + QSysInfo::productType().toUtf8() + '/' @@ -670,6 +701,10 @@ { Q_ASSERT(!m_initialHeaders.empty()); Q_ASSERT(m_stream); + if (m_state >= State::Cancelled) { + qCDebug(lcStream, "[%p] Stream finished before sending the initial request", this); + return; + } Q_ASSERT(m_state == State::Idle); if (!m_stream->sendHEADERS(m_initialHeaders, false)) { @@ -737,15 +772,20 @@ void Http2Handler::cancelWithStatus(const QGrpcStatus &status) { - if (m_state >= State::Cancelled) + if (m_state >= State::Cancelled) { + qCWarning(lcStream, "[%p] Cannot cancel stream in state=%s", this, + QDebug::toBytes(m_state).data()); return; + } qCDebug(lcStream, "[%p] Cancelling (state=%s)", this, QDebug::toBytes(m_state).data()); m_state = State::Cancelled; - // Immediate cancellation by sending the RST_STREAM frame. - if (m_stream && !m_stream->sendRST_STREAM(Http2::Http2Error::CANCEL)) { - qCWarning(lcStream, "[%p] Failed cancellation (stream=%p, stream::state=%s)", this, - m_stream.get(), QDebug::toBytes(m_stream->state()).constData()); + if (m_stream && m_stream->state() != QHttp2Stream::State::Idle) { + // Immediate cancellation by sending the RST_STREAM frame. + if (!m_stream->sendRST_STREAM(Http2::Http2Error::CANCEL)) { + qCWarning(lcStream, "[%p] Failed cancellation (stream=%p, stream::state=%s)", this, + m_stream.get(), QDebug::toBytes(m_stream->state()).constData()); + } } finish(status); @@ -832,10 +872,28 @@ } else if (validation.requireGrpcStatus && k == GrpcStatusDetailsHeader) { // Allowed optional headers // TODO: Implement status-details - QTBUG-138362 - } else if (phase == HeaderPhase::Initial - && (k == GrpcEncodingHeader || k == GrpcAcceptEncodingHeader)) { + } else if (phase == HeaderPhase::Initial && k == GrpcEncodingHeader) { + // Allowed optional headers + if (v == "identity"_ba) + continue; + if (!GrpcAcceptEncodingValue.contains(v) + || !QDecompressHelper::isSupportedEncoding(v)) { + finish({ StatusCode::Internal, + "Server responded with an unsupported compression algorithm: %1"_L1 + .arg(v) }); + return; + } + // Create and configure the decompressor for this stream. + m_decompressor = std::make_unique<QDecompressHelper>(); + if (!m_decompressor->setEncoding(v)) { + finish({ StatusCode::Internal, + "Failed to initialize decompressor for algorithm: %1"_L1.arg(v) }); + return; + } + m_negotiatedEncoding = v; + } else if (phase == HeaderPhase::Initial && k == GrpcAcceptEncodingHeader) { // Allowed optional headers - // TODO: Implement compression handling - QTBUG-129286 + // TODO: Implement client-side (request) compression handling - QTBUG-140235 } else if (k.startsWith(':')) { qCWarning(lcStream, "[%p] Received unhandled HTTP/2 pseudo-header: { key: '%s', value: '%s' } " diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcoperation.cpp new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcoperation.cpp --- old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcoperation.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcoperation.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -112,8 +112,11 @@ */ bool QGrpcOperation::read(QProtobufMessage *message) const { - Q_ASSERT_X(message != nullptr, "QGrpcOperation::read", - "Can't read to nullptr QProtobufMessage"); + if (!message) { + qGrpcWarning("Read called on nullptr message"); + return false; + } + Q_D(const QGrpcOperation); const auto ser = d->operationContext->serializer(); Q_ASSERT_X(ser, "QGrpcOperation", "The serializer is null"); @@ -243,6 +246,19 @@ return *d->operationContext; } +void QGrpcOperation::writeMessage(const QProtobufMessage &message) +{ + Q_D(const QGrpcOperation); + auto messageData = d->operationContext->serializer()->serialize(&message); + emit d->operationContext->writeMessageRequested(messageData); +} + +void QGrpcOperation::writesDone() +{ + Q_D(const QGrpcOperation); + emit d->operationContext->writesDoneRequested(); +} + void QGrpcOperation::onMessageReceived(const QByteArray &data) { Q_D(QGrpcOperation); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcoperation.h new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcoperation.h --- old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcoperation.h 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcoperation.h 2026-01-22 20:42:07.000000000 +0100 @@ -71,6 +71,8 @@ return const_cast<QGrpcOperationContext &>(std::as_const(*this).context()); } void context() const && = delete; + void writeMessage(const QProtobufMessage &message); + void writesDone(); private: void onMessageReceived(const QByteArray &data); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcstream.cpp new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcstream.cpp --- old/qtgrpc-everywhere-src-6.10.1/src/grpc/qgrpcstream.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/grpc/qgrpcstream.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -97,8 +97,7 @@ */ void QGrpcClientStream::writeMessage(const QProtobufMessage &message) { - QByteArray data = QGrpcOperation::context().serializer()->serialize(&message); - emit QGrpcOperation::context().writeMessageRequested(data); + QGrpcOperation::writeMessage(message); } /*! @@ -110,7 +109,7 @@ */ void QGrpcClientStream::writesDone() { - emit QGrpcOperation::context().writesDoneRequested(); + QGrpcOperation::writesDone(); } bool QGrpcClientStream::event(QEvent *event) @@ -165,8 +164,7 @@ */ void QGrpcBidiStream::writeMessage(const QProtobufMessage &message) { - QByteArray data = QGrpcOperation::context().serializer()->serialize(&message); - emit QGrpcOperation::context().writeMessageRequested(data); + QGrpcOperation::writeMessage(message); } /*! @@ -175,7 +173,7 @@ */ void QGrpcBidiStream::writesDone() { - emit QGrpcOperation::context().writesDoneRequested(); + QGrpcOperation::writesDone(); } bool QGrpcBidiStream::event(QEvent *event) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/protobuf/doc/qtprotobuf.qdocconf new/qtgrpc-everywhere-src-6.10.2/src/protobuf/doc/qtprotobuf.qdocconf --- old/qtgrpc-everywhere-src-6.10.1/src/protobuf/doc/qtprotobuf.qdocconf 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/protobuf/doc/qtprotobuf.qdocconf 2026-01-22 20:42:07.000000000 +0100 @@ -64,6 +64,10 @@ ../../protobufqttypes \ ../../wellknown +# Exclude auto-generated Protocol Buffer wrapper headers +excludedirs += ../../wellknown/.tmp/google/protobuf +internalfilepatterns = wrappers.qpb.h + # Add additional documentation dirs sourcedirs += ../../tools/doc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/src/protobuf/doc/src/qtprotobuf.qdoc new/qtgrpc-everywhere-src-6.10.2/src/protobuf/doc/src/qtprotobuf.qdoc --- old/qtgrpc-everywhere-src-6.10.1/src/protobuf/doc/src/qtprotobuf.qdoc 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/src/protobuf/doc/src/qtprotobuf.qdoc 2026-01-22 20:42:07.000000000 +0100 @@ -138,9 +138,9 @@ \title Example of installation for Windows using vcpkg \brief Installing instructions for Protobuf and \gRPC on Windows. - You can install \c Protobuf and \c \gRPC packages on Windows using + You can install Protobuf and \gRPC packages on Windows using \e vcpkg. - \note As an alternative solution, you can build \c Protobuf and \c \gRPC + \note As an alternative solution, you can build Protobuf and \gRPC packages manually. Build instructions can be found for \l {https://github.com/protocolbuffers/protobuf/blob/main/cmake/README.md#windows-builds}{Protobuf} and \l{https://github.com/grpc/grpc/blob/v1.60.0/BUILDING.md#windows}{gRPC} @@ -158,7 +158,7 @@ \endlist The \c vcpkg is a cross-platform C/C++ package manager. - You can use the \c vcpkg for \c Protobuf, \c \gRPC and its dependencies + You can use the \c vcpkg for Protobuf, \gRPC and its dependencies installation: \badcode .\vcpkg.exe install protobuf protobuf:x64-windows @@ -169,7 +169,7 @@ the vcpkg.json name. Finally, you can build and execute Qt-based projects with - \c Protobuf and \c \gRPC support. + Protobuf and \gRPC support. For instance, run \l {Magic 8 Ball} from your installed version of Qt. The example requires both packages to be installed, since \c {Magic 8 Ball} @@ -178,14 +178,18 @@ Following steps are required to start \c {Magic 8 Ball} example: \list \li Find \l {Magic 8 Ball} in \c Examples section: - \image qt-creator.webp + \image qt-creator.webp {Screenshot showing the Qt Creator home + menu, in examples section with "magic" typed in the + search bar and shows the examples with this name} \li Choose MSVC-based building kit from Qt-creator configuration settings: - \image msvc-kit.webp - \li Add the paths to the installed \c Protobuf and \c \gRPC packages + \image msvc-kit.webp {Screenshot showing the msvc kit that + built the project} + \li Add the paths to the installed Protobuf and \gRPC packages to the \c CMAKE_PREFIX_PATH environment variable inside \c {Initial Configuration} settings block: - \image path-env-variable.webp + \image path-env-variable.webp {Screenshot showing the build + options from qt creator with the msvc kit} \li Build and Run. \endlist */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/end2end/event.proto new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/end2end/event.proto --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/end2end/event.proto 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/end2end/event.proto 2026-01-22 20:42:07.000000000 +0100 @@ -13,3 +13,7 @@ string name = 2; uint64 number = 3; } + +message EventList { + repeated Event events = 1; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/end2end/eventhub.proto new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/end2end/eventhub.proto --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/end2end/eventhub.proto 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/end2end/eventhub.proto 2026-01-22 20:42:07.000000000 +0100 @@ -10,6 +10,7 @@ service EventHub { rpc Push(Event) returns (None) {} rpc Subscribe(None) returns (stream Event) {} + rpc SubscribeList(None) returns (stream EventList) {} rpc Notify(stream Event) returns (None) {} rpc Exchange(stream Event) returns (stream Event) {} } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/end2end/tst_grpc_client_end2end.cpp new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/end2end/tst_grpc_client_end2end.cpp --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/end2end/tst_grpc_client_end2end.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/end2end/tst_grpc_client_end2end.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -77,6 +77,9 @@ void bidiStreamsInOrder(); + void clientHandlesCompression_data() const; + void clientHandlesCompression(); + private: static std::shared_ptr<grpc::ServerCredentials> serverSslCredentials() { @@ -146,7 +149,6 @@ void QtGrpcClientEnd2EndTest::cleanup() { m_client.reset(); - QVERIFY(m_server->stopAsyncProcessing()); } void QtGrpcClientEnd2EndTest::clientMetadataReceived_data() const @@ -174,6 +176,7 @@ QFETCH(const MultiHash, channelMetadata); // Setup Server-side handling + auto processor = m_server->createProcessor(); struct ServerData { grpc::ServerAsyncResponseWriter<None> op{ &ctx }; @@ -182,37 +185,43 @@ Event request; None response; }; - auto *data = new ServerData; - - CallbackTag *callHandler = new CallbackTag([&](bool ok) { - QVERIFY(ok); + ServerData *data = new ServerData; - const std::multimap<grpc::string_ref, grpc::string_ref> - &receivedMd = data->ctx.client_metadata(); - auto mergedMd = channelMetadata; - mergedMd.unite(callMetadata); - - for (auto it = mergedMd.cbegin(); it != mergedMd.cend(); ++it) { - // Check that each key-value pair sent by the client exists on the server - auto serverRange = receivedMd.equal_range(it.key().toStdString()); - auto clientRange = mergedMd.equal_range(it.key()); - - QCOMPARE_EQ(std::distance(serverRange.first, serverRange.second), - std::distance(clientRange.first, clientRange.second)); - while (clientRange.first != clientRange.second) { - // Look for the exact entry in the server range. The order may - // be changed but it must be present. - const auto it = std::find_if(serverRange.first, serverRange.second, [&](auto it) { - return it.first == clientRange.first.key().toStdString() - && it.second == clientRange.first.value().toStdString(); - }); - QVERIFY(it != serverRange.second); - std::advance(clientRange.first, 1); + CallbackTag *callHandler = new CallbackTag( + [&](bool ok) { + QVERIFY(ok); + + const std::multimap<grpc::string_ref, grpc::string_ref> + &receivedMd = data->ctx.client_metadata(); + auto mergedMd = channelMetadata; + mergedMd.unite(callMetadata); + + for (auto it = mergedMd.cbegin(); it != mergedMd.cend(); ++it) { + // Check that each key-value pair sent by the client exists on the server + auto serverRange = receivedMd.equal_range(it.key().toStdString()); + auto clientRange = mergedMd.equal_range(it.key()); + + QCOMPARE_EQ(std::distance(serverRange.first, serverRange.second), + std::distance(clientRange.first, clientRange.second)); + while (clientRange.first != clientRange.second) { + // Look for the exact entry in the server range. The order may + // be changed but it must be present. + const auto it = std::find_if(serverRange.first, serverRange.second, + [&](auto it) { + return it.first + == clientRange.first.key().toStdString() + && it.second + == clientRange.first.value().toStdString(); + }); + QVERIFY(it != serverRange.second); + std::advance(clientRange.first, 1); + } } - } - data->op.Finish(data->response, grpc::Status::OK, new DeleteTag<ServerData>(data)); - return CallbackTag::Delete; - }); + data->op.Finish(data->response, grpc::Status::OK, + new DeleteTag<ServerData>(data, processor.get())); + return CallbackTag::Delete; + }, + processor.get()); m_service->RequestPush(&data->ctx, &data->request, &data->op, m_server->cq(), m_server->cq(), callHandler); @@ -227,8 +236,6 @@ QVERIFY(response.has_value()); }); - QVERIFY(m_server->startAsyncProcessing()); - QSignalSpy finishedSpy(call.get(), &QGrpcOperation::finished); QVERIFY(finishedSpy.isValid()); QVERIFY(finishedSpy.wait()); @@ -262,6 +269,7 @@ QFETCH(const MultiHash, expectedTrailingMd); // Setup Server-side handling + auto processor = m_server->createProcessor(); struct ServerData { grpc::ServerAsyncResponseWriter<None> op{ &ctx }; @@ -270,18 +278,21 @@ Event request; None response; }; - auto *data = new ServerData; + ServerData *data = new ServerData; for (auto it = expectedInitialMd.cbegin(); it != expectedInitialMd.cend(); ++it) data->ctx.AddInitialMetadata(it.key().toStdString(), it.value().toStdString()); for (auto it = expectedTrailingMd.cbegin(); it != expectedTrailingMd.cend(); ++it) data->ctx.AddTrailingMetadata(it.key().toStdString(), it.value().toStdString()); - CallbackTag *callHandler = new CallbackTag([&](bool ok) { - QVERIFY(ok); - data->op.Finish(data->response, grpc::Status::OK, new DeleteTag<ServerData>(data)); - return CallbackTag::Delete; - }); + CallbackTag *callHandler = new CallbackTag( + [&](bool ok) { + QVERIFY(ok); + data->op.Finish(data->response, grpc::Status::OK, + new DeleteTag<ServerData>(data, processor.get())); + return CallbackTag::Delete; + }, + processor.get()); m_service->RequestPush(&data->ctx, &data->request, &data->op, m_server->cq(), m_server->cq(), callHandler); @@ -312,7 +323,6 @@ QVERIFY(trailingMd.contains(it.key(), it.value())); } }); - QVERIFY(m_server->startAsyncProcessing()); QSignalSpy finishedSpy(call.get(), &QGrpcOperation::finished); QVERIFY(finishedSpy.isValid()); @@ -324,6 +334,7 @@ constexpr auto SleepTime = std::chrono::milliseconds(5); // Setup Server-side handling + auto processor = m_server->createProcessor(); struct ServerData { grpc::ServerAsyncReaderWriter<Event, Event> op{ &ctx }; @@ -342,51 +353,59 @@ response.set_name("server-" + std::to_string(response.number())); } }; - auto *data = new ServerData; + ServerData *data = new ServerData; - CallbackTag *reader = new CallbackTag([&, current = 1u](bool ok) mutable { - if (!ok) { - data->readerDone = true; - if (data->writerDone) - data->op.Finish(grpc::Status::OK, new DeleteTag<ServerData>(data)); - return CallbackTag::Delete; - } - QCOMPARE_EQ(data->request.type(), Event::CLIENT); - QCOMPARE_EQ(data->request.number(), current); - std::string name = "client-" + std::to_string(current); - QCOMPARE_EQ(data->request.name(), name); - ++current; + CallbackTag *reader = new CallbackTag( + [&, current = 1u](bool ok) mutable { + if (!ok) { + data->readerDone = true; + if (data->writerDone) + data->op.Finish(grpc::Status::OK, + new DeleteTag<ServerData>(data, processor.get())); + return CallbackTag::Delete; + } + QCOMPARE_EQ(data->request.type(), Event::CLIENT); + QCOMPARE_EQ(data->request.number(), current); + std::string name = "client-" + std::to_string(current); + QCOMPARE_EQ(data->request.name(), name); + ++current; + + data->op.Read(&data->request, reader); + return CallbackTag::Proceed; + }, + processor.get()); + CallbackTag *writer = new CallbackTag( + [&](bool ok) { + QVERIFY(ok); + if (data->response.number() >= data->count) { + data->writerDone = true; + if (data->readerDone) + data->op.Finish(grpc::Status::OK, + new DeleteTag<ServerData>(data, processor.get())); + return CallbackTag::Delete; + } + std::this_thread::sleep_for(SleepTime); + data->updateResponse(); + data->op.Write(data->response, writer); + return CallbackTag::Proceed; + }, + processor.get()); + CallbackTag *callHandler = new CallbackTag( + [&](bool ok) { + QVERIFY(ok); + const auto &md = data->ctx.client_metadata(); + const auto countIt = md.find("call-count"); + QVERIFY(countIt != md.cend()); + data->count = std::stoul(std::string(countIt->second.data(), countIt->second.length())); + QCOMPARE_GT(data->count, 0); + + data->op.Read(&data->request, reader); + data->updateResponse(); + data->op.Write(data->response, writer); - data->op.Read(&data->request, reader); - return CallbackTag::Proceed; - }); - CallbackTag *writer = new CallbackTag([&](bool ok) { - QVERIFY(ok); - if (data->response.number() >= data->count) { - data->writerDone = true; - if (data->readerDone) - data->op.Finish(grpc::Status::OK, new DeleteTag<ServerData>(data)); return CallbackTag::Delete; - } - std::this_thread::sleep_for(SleepTime); - data->updateResponse(); - data->op.Write(data->response, writer); - return CallbackTag::Proceed; - }); - CallbackTag *callHandler = new CallbackTag([&](bool ok) { - QVERIFY(ok); - const auto &md = data->ctx.client_metadata(); - const auto countIt = md.find("call-count"); - QVERIFY(countIt != md.cend()); - data->count = std::stoul(std::string(countIt->second.data(), countIt->second.length())); - QCOMPARE_GT(data->count, 0); - - data->op.Read(&data->request, reader); - data->updateResponse(); - data->op.Write(data->response, writer); - - return CallbackTag::Delete; - }); + }, + processor.get()); m_service->RequestExchange(&data->ctx, &data->op, m_server->cq(), m_server->cq(), callHandler); // Client bidi stream @@ -428,12 +447,102 @@ }); delayedWriter.start(SleepTime); - QVERIFY(m_server->startAsyncProcessing()); - QSignalSpy finishedSpy(stream.get(), &QGrpcOperation::finished); QVERIFY(finishedSpy.isValid()); QVERIFY(finishedSpy.wait()); } + +void QtGrpcClientEnd2EndTest::clientHandlesCompression_data() const +{ + QTest::addColumn<grpc_compression_algorithm>("compressionAlgo"); + QTest::addRow("compress(None)") << GRPC_COMPRESS_NONE; + QTest::addRow("compress(Deflate)") << GRPC_COMPRESS_DEFLATE; + QTest::addRow("compress(Gzip)") << GRPC_COMPRESS_GZIP; +} + +void QtGrpcClientEnd2EndTest::clientHandlesCompression() +{ + QFETCH(const grpc_compression_algorithm, compressionAlgo); + EventList serverResponses; + + class SubscribeListHandler : public AbstractRpcTag + { + public: + SubscribeListHandler(EventList &responses_, EventHub::AsyncService &service_, + const grpc_compression_algorithm compressionAlgo_, + TagProcessor *processor) + : AbstractRpcTag(processor), op(&context()), service(service_), responses(responses_), + compressionAlgo(compressionAlgo_) + { + context().set_compression_algorithm(compressionAlgo); + context().set_compression_level(GRPC_COMPRESS_LEVEL_HIGH); + // create some 'compressable' data. Try to make it more complex + // as compression is not guaranteed to actually be applied. + for (size_t i = 0; i < 100; ++i) { + const auto v = i % 10; + Event ev; + ev.set_name("server;server;" + std::to_string(v)); + ev.set_number(v); + responses.mutable_events()->Add(std::move(ev)); + } + } + void start(grpc::ServerCompletionQueue *cq) override + { + service.RequestSubscribeList(&context(), &request, &op, cq, cq, this); + } + void process(bool ok) override + { + QVERIFY(ok); + if (index >= responseCount) { + op.Finish(grpc::Status::OK, new DeleteTag<SubscribeListHandler>(this, processor)); + return; + } + + grpc::WriteOptions wopts; + // Enable and disable the compression per-message + if (index % 2 == 0) + wopts.set_no_compression(); + op.Write(responses, wopts, this); + ++index; + } + + grpc::ServerAsyncWriter<EventList> op; + EventHub::AsyncService &service; + + None request; + EventList &responses; + + size_t index = 0; + const grpc_compression_algorithm compressionAlgo; + const size_t responseCount = 20; + }; + + auto processor = m_server->createProcessor(); + SubscribeListHandler *handler = new SubscribeListHandler(serverResponses, *m_service, + compressionAlgo, processor.get()); + m_server->startRpcTag(handler); + + auto call = m_client->SubscribeList(qt::None{}); + QVERIFY(call); + + connect(call.get(), &QGrpcOperation::finished, this, + [&](const QGrpcStatus &status) { QCOMPARE(status.code(), QtGrpc::StatusCode::Ok); }); + connect(call.get(), &QGrpcServerStream::messageReceived, this, [&] { + auto response = call->read<qt::EventList>(); + QVERIFY(response); + QCOMPARE_EQ(response->events().size(), serverResponses.events().size()); + for (int i = 0; i < response->events().size(); ++i) { + const auto &next = response->events().at(i); + const auto &baseline = serverResponses.events().at(i); + QCOMPARE_EQ(next.name(), QString::fromStdString(baseline.name())); + QCOMPARE_EQ(next.number(), baseline.number()); + } + }); + + QSignalSpy finishedSpy(call.get(), &QGrpcOperation::finished); + QVERIFY(finishedSpy.isValid()); + QVERIFY(finishedSpy.wait()); +} QTEST_MAIN(QtGrpcClientEnd2EndTest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/CMakeLists.txt new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/CMakeLists.txt --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/CMakeLists.txt 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/CMakeLists.txt 2026-01-22 20:42:07.000000000 +0100 @@ -30,5 +30,6 @@ PUBLIC protobuf::libprotobuf gRPC::grpc++ + Qt::Test ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/mockserver.cpp new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/mockserver.cpp --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/mockserver.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/mockserver.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -2,10 +2,85 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "mockserver.h" +#include "tags.h" + +#include <QtTest/qtest.h> #include <grpcpp/alarm.h> #include <grpcpp/server_builder.h> +using namespace std::chrono_literals; + +TagProcessor::TagProcessor(MockServer *server) + : mServer(server) +{ + QVERIFY(mServer); + QVERIFY(mServer->cq()); + mThread = std::thread(&TagProcessor::processLoop, this); +} + +TagProcessor::~TagProcessor() +{ + QVERIFY(waitForTagCompletion(5s)); + + mRunning.store(false); + wakeCQ(); + + if (mThread.joinable()) + mThread.join(); + + drainTags(); +} + +void TagProcessor::processLoop() +{ + while (mRunning.load(std::memory_order_acquire)) { + mServer->processTag(); + } +} + +void TagProcessor::registerTag(AbstractTag *tag) +{ + std::scoped_lock lock(mMutex); + const auto it = mActiveTags.insert(tag); + QVERIFY(it.second); +} + +void TagProcessor::unregisterTag(AbstractTag *tag) +{ + std::scoped_lock lock(mMutex); + const auto erased = mActiveTags.erase(tag); + QCOMPARE_EQ(erased, 1); + if (mActiveTags.empty()) + mCv.notify_all(); +} + +size_t TagProcessor::activeTagCount() const noexcept +{ + std::scoped_lock lock(mMutex); + return mActiveTags.size(); +} + +bool TagProcessor::waitForTagCompletion(std::chrono::milliseconds deadline) noexcept +{ + std::unique_lock lock(mMutex); + return mCv.wait_for(lock, deadline, [this] { return mActiveTags.empty(); }); +} + +void TagProcessor::wakeCQ() +{ + auto *tag = new VoidTag(this); + auto *alarm = new grpc::Alarm(); + alarm->Set(mServer->cq(), gpr_now(GPR_CLOCK_REALTIME), tag); +} + +void TagProcessor::drainTags() +{ + QVERIFY(!mRunning); + while (mServer->processTag(50)) + ; +} + MockServer::MockServer() = default; MockServer::~MockServer() { @@ -35,10 +110,7 @@ bool MockServer::stop() { State currentState = mState.load(); - if (currentState == State::Processing) - stopAsyncProcessing(); - - if (currentState != State::Started && currentState != State::Processing) + if (currentState != State::Started && currentState != State::ShuttingDown) return currentState == State::Stopped; mState = State::ShuttingDown; @@ -63,9 +135,8 @@ bool MockServer::processTag(int timeoutMs) { State currentState = mState.load(); - if (currentState != State::Processing && currentState != State::Started) { + if (currentState != State::Started) return false; - } void *rawTag = nullptr; bool ok = false; @@ -82,33 +153,6 @@ return false; } -bool MockServer::startAsyncProcessing(int timeoutMs) -{ - if (!transitionState(State::Started, State::Processing)) - return false; - - mProcessingThread = std::thread([this, timeoutMs] { - while (mState.load(std::memory_order_acquire) == State::Processing) { - processTag(timeoutMs); - } - }); - return true; -} - -bool MockServer::stopAsyncProcessing() -{ - if (!transitionState(State::Processing, State::Started)) - return false; - - if (mProcessingThread.joinable()) { - grpc::Alarm alarm; - // Trigger an event so that the processing loop detects the change. - alarm.Set(mCQ.get(), gpr_now(gpr_clock_type::GPR_CLOCK_REALTIME), new VoidTag()); - mProcessingThread.join(); - } - return true; -} - MockServer &MockServer::step(int timeoutMs) { mFutures.emplace_back(std::async(std::launch::async, @@ -126,6 +170,17 @@ return true; } +void MockServer::startRpcTag(AbstractRpcTag *tag) +{ + QVERIFY(tag); + tag->start(mCQ.get()); +} + +std::unique_ptr<TagProcessor> MockServer::createProcessor() +{ + return std::make_unique<TagProcessor>(this); +} + bool MockServer::transitionState(State from, State to) { State expected = from; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/mockserver.h new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/mockserver.h --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/mockserver.h 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/mockserver.h 2026-01-22 20:42:07.000000000 +0100 @@ -3,17 +3,17 @@ #pragma once -#include "tags.h" #include "certificates.h" #include <grpcpp/grpcpp.h> #include <grpcpp/security/server_credentials.h> +#include <future> #include <memory> #include <string> -#include <vector> #include <thread> -#include <future> +#include <unordered_set> +#include <vector> struct ListeningPort { @@ -22,6 +22,39 @@ int selectedPort = -1; }; +class MockServer; +class AbstractTag; +class AbstractRpcTag; + +class TagProcessor +{ +public: + explicit TagProcessor(MockServer *server); + ~TagProcessor(); + + TagProcessor(const TagProcessor &) = delete; + TagProcessor &operator=(const TagProcessor &) = delete; + + void registerTag(AbstractTag *tag); + void unregisterTag(AbstractTag *tag); + + [[nodiscard]] size_t activeTagCount() const noexcept; + [[nodiscard]] bool waitForTagCompletion(std::chrono::milliseconds deadline) noexcept; + +private: + void processLoop(); + void wakeCQ(); + void drainTags(); + + MockServer *mServer; + std::atomic_bool mRunning = true; + std::thread mThread; + + mutable std::mutex mMutex; + std::condition_variable mCv; + std::unordered_set<AbstractTag *> mActiveTags; +}; + class MockServer { public: @@ -29,7 +62,6 @@ Stopped, Starting, Started, - Processing, ShuttingDown, }; @@ -43,14 +75,14 @@ bool stop(); bool processTag(int timeoutMs = -1); - bool startAsyncProcessing(int timeoutMs = -1); - bool stopAsyncProcessing(); - void startRpcTag(AbstractRpcTag &tag) { tag.start(mCQ.get()); } + void startRpcTag(AbstractRpcTag *tag); MockServer &step(int timeoutMs = -1); bool waitForAllSteps(); + std::unique_ptr<TagProcessor> createProcessor(); + private: bool transitionState(State from, State to); @@ -58,6 +90,7 @@ std::unique_ptr<grpc::Server> mServer; std::unique_ptr<grpc::ServerCompletionQueue> mCQ; std::vector<std::future<bool>> mFutures; - std::thread mProcessingThread; std::atomic<State> mState = State::Stopped; }; + +#include "tags.h" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/tags.h new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/tags.h --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/mockserver/tags.h 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/mockserver/tags.h 2026-01-22 20:42:07.000000000 +0100 @@ -3,6 +3,8 @@ #pragma once +#include "mockserver.h" + #include <grpcpp/completion_queue.h> #include <grpcpp/server_context.h> @@ -12,16 +14,22 @@ class AbstractTag { public: - explicit AbstractTag() = default; - virtual ~AbstractTag() = default; + explicit AbstractTag(TagProcessor *processor_) : processor(processor_) + { + processor->registerTag(this); + } + virtual ~AbstractTag() { processor->unregisterTag(this); } AbstractTag(const AbstractTag &) = delete; AbstractTag &operator=(const AbstractTag &) = delete; - AbstractTag(AbstractTag &&) = default; - AbstractTag &operator=(AbstractTag &&) = default; + AbstractTag(AbstractTag &&) = delete; + AbstractTag &operator=(AbstractTag &&) = delete; virtual void process(bool ok) = 0; + +protected: + TagProcessor *processor; }; class CallbackTag : public AbstractTag @@ -29,7 +37,11 @@ public: enum Operation { Proceed, Delete }; using Function = std::function<Operation(bool)>; - explicit CallbackTag(Function fn) : mFn(std::move(fn)) { } + + explicit CallbackTag(Function fn, TagProcessor *processor_) + : AbstractTag(processor_), mFn(std::move(fn)) + { + } void process(bool ok) override { if (mFn(ok) == Delete) @@ -43,18 +55,27 @@ class VoidTag final : public CallbackTag { public: - VoidTag() : CallbackTag([](bool) { return Delete; }) { } + VoidTag(TagProcessor *processor_) : CallbackTag([](bool) { return Delete; }, processor_) { } }; +// TODO: gRPC 1.50.1 on windows has faulty lifetime management and still uses +// the context post completion in the interceptors. This should be fixed in +// newer versions. Remove this when upgrading to favor stack based lifetime +// management in testcases. template <typename Data> class DeleteTag final : public CallbackTag { public: - explicit DeleteTag(Data *data) : CallbackTag([](bool) { return Delete; }), data(data) + explicit DeleteTag(Data *data, TagProcessor *processor_) + : CallbackTag([](bool) { return Delete; }, processor_), data(data) { assert(this->data); } - ~DeleteTag() { delete data; } + ~DeleteTag() + { + delete data; + data = nullptr; + } private: Data *data; @@ -63,13 +84,15 @@ class AbstractRpcTag : public AbstractTag { public: - AbstractRpcTag() + AbstractRpcTag(TagProcessor *processor_) : AbstractTag(processor_) { - mContext.AsyncNotifyWhenDone(new CallbackTag([this](bool ok) { - if (ok && mContext.IsCancelled()) - mIsCancelled = true; - return CallbackTag::Delete; - })); + mContext.AsyncNotifyWhenDone(new CallbackTag( + [this](bool ok) { + if (ok && mContext.IsCancelled()) + mIsCancelled = true; + return CallbackTag::Delete; + }, + processor)); } virtual void start(grpc::ServerCompletionQueue *cq) = 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/test_server/assets/cert.pem new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/test_server/assets/cert.pem --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/test_server/assets/cert.pem 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/test_server/assets/cert.pem 2026-01-22 20:42:07.000000000 +0100 @@ -1,35 +1,35 @@ -----BEGIN CERTIFICATE----- -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 -hoxtQeCRSFXydW05sg== +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJw2IeQ1XLClzTkgkdnZwxst +lnc62T8m/ztvfS4ha45g5QxiwlxhLS4tZfh6BKB+DWUGnUc6ntpYvWqHCMT3ujgT +Z9HRBfCbm4pyEk0WQkE5xzanDW23ZX9GbsjnH8dqZeaUZeEM7mb+YJwdGm4cv8Ko +acVMXwT/1Xj3Cu7pm1rj262KxmYJyi+sHzVSAj0VtSVaOsDj6knXtt2PzuF5wkNz +ANLqCcSS8+PA6KUb1ZPFpmmqKBqDFmW3FuFBRn1XoseHk4W94wsi0bICPLp+qke6 +F0eJr9+kHa+P48TfP4c87ize8Iim2ZgFWpHl9zszP2oXSf5nvFW2PKvIdOqNdfGR +q7yXKUpZ/GaW4/wOfBYPf31vk73tW52gUH6dcob9il/dwglvDCkTPzSQ8/col0BJ +Dv8dRD4wrLTDJ/jCS2ITaV7ejSzOAGicEztXiO6kE0OYMmo7jnpjF3KSStmsnfWc +DncKiyLLLFPlE6Lo/HbL0AnZ/0LFZ9fP7eE9qxKly/LGETPu/rR2b+6q3ppeWGD3 +xLvnESPXVL5vyxwM85WT9ywrYTIRpTGXVlsBu1de0jWC9+/T6FYBR9ssft6WTseW +OOcBncdq6ZoI4l+9mpDSOoLYxKhjSWCxcINbNxR5ouj5k8sqqPEx5yfqh/sNvwup +txkkhp1L4h2JPT4IoOmnAgMBAAGjUzBRMB0GA1UdDgQWBBRE7ICsAMfwU0dOSEyV +JgKOMgVPBzAfBgNVHSMEGDAWgBRE7ICsAMfwU0dOSEyVJgKOMgVPBzAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQACPGrxhIgWCDaRQYhua8rYktli +gCIJD+7jQSbwKAS6rjXf6bfNQyCyOsbGBOS6K+xav5yI2YF2pmL31sPzXMWghY63 +v953Q5xkp9UZi6iwo73FJcrnyAFGGf9i9mJRbOrTP6Dex4dKhTRdv8GBA1fxf3wm +/Bs2A+ozRYbM1TF3mnSkePhVcwb7qk9d78WlhgD7ESlB5v97kcrIUTcMmp6f1gJi +RUGbmhvDkngocXLiI4K3gwRL994g+oEmFSPvqRx4iN2xPM9O45jgYIdkheRgQ8W1 +jZFQLy/T1GPsHI4ymg4sstpG4YGsWD3zh1HE2Xj3h8uhcIoveLkINiloyarNjE5h +SkXY/jfakU7sSNXzp8ofC53bBCQO/uEPL7V7ZkAj+MrApoqju3ITc+GquS0X7WEA +R2JOzLodeuyNeMY93yfxsn7Q74KtvdlVwRzp3XTdMun6FC5x4N1uS2gevuuaDKQN +RzfFS2TjiGch7JTbAtCFeFD7Y3/M/Wkfr9j2YGpAVIulu3x1uIlEXTiIEt8IoW8O +sJoB5Q0T1kPBMEiU6GrVf1oFLcpmPzJSrc0zM7e2LJ3giCVv67dEoiDY1k9ZFTf8 +W+57x8eKDT8qr7ifgt5HuZ45Lhr4oQfvWgCtTxeMQEAoLF2IewzlfuTMCAVJ5BNX +QDcVKBRng6JwpCcP4Q== -----END CERTIFICATE----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/test_server/assets/key.pem new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/test_server/assets/key.pem --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/client/shared/test_server/assets/key.pem 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/client/shared/test_server/assets/key.pem 2026-01-22 20:42:07.000000000 +0100 @@ -1,52 +1,52 @@ -----BEGIN PRIVATE KEY----- -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 -+mnlJc8OPylR/oZHIJW2cveEQjS4qA== +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCcNiHkNVywpc05 +IJHZ2cMbLZZ3Otk/Jv87b30uIWuOYOUMYsJcYS0uLWX4egSgfg1lBp1HOp7aWL1q +hwjE97o4E2fR0QXwm5uKchJNFkJBOcc2pw1tt2V/Rm7I5x/HamXmlGXhDO5m/mCc +HRpuHL/CqGnFTF8E/9V49wru6Zta49utisZmCcovrB81UgI9FbUlWjrA4+pJ17bd +j87hecJDcwDS6gnEkvPjwOilG9WTxaZpqigagxZltxbhQUZ9V6LHh5OFveMLItGy +Ajy6fqpHuhdHia/fpB2vj+PE3z+HPO4s3vCIptmYBVqR5fc7Mz9qF0n+Z7xVtjyr +yHTqjXXxkau8lylKWfxmluP8DnwWD399b5O97VudoFB+nXKG/Ypf3cIJbwwpEz80 +kPP3KJdASQ7/HUQ+MKy0wyf4wktiE2le3o0szgBonBM7V4jupBNDmDJqO456Yxdy +kkrZrJ31nA53CosiyyxT5ROi6Px2y9AJ2f9CxWfXz+3hPasSpcvyxhEz7v60dm/u +qt6aXlhg98S75xEj11S+b8scDPOVk/csK2EyEaUxl1ZbAbtXXtI1gvfv0+hWAUfb +LH7elk7HljjnAZ3HaumaCOJfvZqQ0jqC2MSoY0lgsXCDWzcUeaLo+ZPLKqjxMecn +6of7Db8LqbcZJIadS+IdiT0+CKDppwIDAQABAoICACXSN290+g2s/xy7RYTs4//D +EGCqx2mya+58hl7yaYOt8xcGHE/YmWNKS5uXE2K2UlDpApB54Xc1MBtIOXfTEOu9 +yw+yX5FLY6PoDYVLg9wd/J0/YhFz62ee6njK+NdD7AshV+9evaQDJ+n0Y+9QF5+u +PfmujXz84jco/SUuO1aMv6XraTDQYvsa3e1/fxpCFLtH9ty38gRR7a0EQg82dzH0 +eLkYQCgvekxk6w76x8HBA5MmxxHwNi6HX0tHjiUP4FIWAcJKYNvT4NiMER8IRvsZ +08QW0pW/uw5ENN34PX3lYzdK/Qrg9uMHPNABUVQsisiJfflSLXfFKThgTupxOKJV +CRE+Yhjb6fC1FMea+/EIgRY4PXO7lCDyUuquL4YLKI8BCbcDc5CD327I4yxuImu7 +wqkYyaCtgxPkI9Vk3gJYhqcI+GVyVUkEQYguX1ydqDA7wyp2IhoE6Cg6DP33rHwc +os3jdgVlQidf+z9Ch0J5lEl7nwk5uLU6t3rfS4Ghpdy73vjDlUq7n4j4eHzlxdAI +4XLcfTEAMT8yRV9ACTZNesYIi904FvCokaMgOQ1PTCCRVc6l53sjbFReRv5dtrro +jHq9WQx7gAHN9+NhbSrc1eqODAnq5odHvQFQxJVBBWn5XaRhJfCtSK+OVZYkHWX3 +4SuofZQJgmYIyebgM+U9AoIBAQDM3GdRLgAwKfP6O34ljzyWbAS/vpAWY0bfpouE +li7POzk/5DuX2elRGeEfeQaR4mTFJX2W5Fz4pnGx3xVC2GzqmlcvWM7nOeknwGUf +lttQhK/Y85/CSBEaYPnINpe8F6/a2+4oSnBnVoUgPeVnqDxhx68R5PR0bDdCDizO +VYHOoJdf/IawjwXTfVC4pLnuaPXNNOeBrgB/TXfFYPimW5J7oiw/lbob26DACpo4 +ITsxloQjAq02NoASp8A32vKRUnjKQAPxHuLb+ioVDmyVu/Xw+8gl0LfEbL5vAMO+ +18j1oR+CQyDn9qo/HuuB2LGL5xpjZR09zpKx/D6+yje6s34LAoIBAQDDNMr4BL12 +Rj6lL1uSw04gvAxBc68iSsPbW+97Rb3aGSIGbL86nD1ESdNgq3C7o4IG/ce3CbXy +PhPnzTU4Kg30yTJdJm/QayTMipZlQZLzc8PNQknNqz5syvF3JAtEpVlpWeysh35Q +cyon0Ah7T5Tm8V8M65wTf7KZ0UF4b4ByPuWQLZfJCgb8SpNtT15pjQ4c0PcXpYk8 +zpm4oJCN3RUpXi30AipRYBJ7gc46o6T9+pZ/Mdmp2WklD4shgIXC5pkaiV/5PhQq +9q0n2w8iaFU7JSKVe7yv/RFJZMCntS6P7VBCAf5gYIfuULynllxx3uDrNiclEDw+ +K91/QnnX8jBVAoIBAQCNZJ4GoLpOg9Y54q/5WnhV1e4dLXijixfSq09mToW2UEj+ +OReMgkGP0U3Y/B41uE0W6P5ak/k7QR39x1wUS+44qhf8vM3pN8YdwqPI/sUWOM5p +7hRY8oajb0VXE578mlistNkWg/I60LOHglEAj1RFpJ3Huv+iD0LAW6o/KzMxmxN/ +k3qfB8fcpYR+PGt8CoOEg7w5fBApzR0aZQiZQWDD5jWmGUBfk+HKSkcQ8Ja7bgh7 +ZZCJd2pD9fYsVvjOpl5qMW7HECtB6tL6v37ghd+E2TLWLs13TBrP4HY6FRNFvVRT +AuQGVfBBKqUfdKFuTy6eZZ6eFyKWp+PiqQ131gTrAoIBAASBroHlUh5t5rpXioyr +15zn2nyUWCG5iiYBTFkTNhvX4rI1RoDq5Hs0HR4pNxQN5U2WBEtUfQ/XoQwD78uZ +JPNWxcPixEgSgSn7tRcnWWYncQjHE/8cifdnBAYVHfF6w8Kr4cvl4OOolPuQUPHP +14cxYVliAxtsIkpsy08le9inXRNkChIJGjou2pJ2d56GNCI0LNAt8SonNuNNSakM +xpVK6FKuzh1M04BoccNmzcNTSrArDXRfYY8KedLPLcdfHX/AVifh6ANJ6Jt38jSA +Jh+UbuT2k1eYxxJjshLtGuMVvnmXpDDDab/1uzU/QmkalSS4/lRbuJhS2O08MqXq +oHUCggEAGLsYlWOMAlLt/FqsV5baFyad5gtdJ79sFcNILkrwu+7Kc3OMPY8cKAnC +nC2C1mzDKQopIAe2Z4GUtttQopRh95UrnUZigm3JS/kr7qdzKJLbyQXRLD+h9fu/ +05dAjrzMaqdtfw000ULsZqEcMwMEen1zhPacAA96OWD2mA7f5SgsL+aUQ4VHZZ58 +4FLXbE7EJg+C5EUHSsQI93Ml4n1hfY/kjy7ku5Pd28JbaxjqkfiOPx4aFOVlgmT3 +hsQpKiZCwYlxBViNKdNg4B4AOVkdsWZFNFQ1O9PsRq1IzdH0JWTMJ3DajCbiivtR +FeaogtqTO5Awnke3irEkqWDeA53ADQ== -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/qgrpchttp2channel/tst_qgrpchttp2channel.cpp new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/qgrpchttp2channel/tst_qgrpchttp2channel.cpp --- old/qtgrpc-everywhere-src-6.10.1/tests/auto/grpc/qgrpchttp2channel/tst_qgrpchttp2channel.cpp 2025-11-13 21:37:06.000000000 +0100 +++ new/qtgrpc-everywhere-src-6.10.2/tests/auto/grpc/qgrpchttp2channel/tst_qgrpchttp2channel.cpp 2026-01-22 20:42:07.000000000 +0100 @@ -75,8 +75,12 @@ thread->wait(); TestService::Client client; + QSignalSpy spy(&client, &QGrpcClientBase::channelChanged); QVERIFY(!client.attachChannel(threadChannel)); - QVERIFY(client.attachChannel(std::make_shared<QGrpcHttp2Channel>(QUrl()))); + auto validChannel = std::make_shared<QGrpcHttp2Channel>(QUrl()); + QVERIFY(client.attachChannel(validChannel)); + QVERIFY(!client.attachChannel(validChannel)); + QCOMPARE_EQ(spy.count(), 1); } void QGrpcHttp2ChannelTest::rpcThreadTest()
