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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
+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
-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
+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
-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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-----
-MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC2BSpYo+CoDjQ/
-9qyurATB6hzvHCYC/ZXbFXJmZ3UOzLy1FNaHEFFRuqYztbE3N/KmT1rjLW/fpaMI
-C/GD6wf8mEGYkJHlLg8ICqqOaUDA1bcrLoKkX4YEbjkyuolWiYBFa0nOQ7EkG3Mq
-QnqT/So//6wolDotuplnoPaS4EiPap5XF7rMeJktm4QY7lEXZd9LTKgYg+dPDRmV
-m2Bx/UhKDNKjsvSIV0nUEBqGnEWDajG4TQ/A6Wv5gk8LIp2TKsfB83HAr5BWXE7e
-uQ1kvSfU6csS/QWQCYxCLG2P11WSCO3RZDTmiFQTcryZ2CetUr757ppx6TY5ucaR
-O0obCISmytfRLVGodkJB01Auufg7iJ2naqajv9GElRgFUtVV618fgYggmtuPlkFX
-sdxtDQJItotywq5ghiDfvgETNy+yqVaBpCa0n3LODuwyqm8KlI6PjM24SKo/M743
-ZdHDptG4gRg+ZqocEdaQpTHHx/MeDJ0U9fuvXGkFfWxVeNA9Rkk/Ab4CQ33BrhYt
-EpUHRWLrKkLQn6Ds7nG3x+VK9SbvI5ZTiFcJUE0t/OPYZ2sQXEgAe7SA8w4Fj/73
-FvcAVu9vs/Oc3ZnlPXHRhtAx4m2riMqNN0G7hKkn0zzmfw2jW95FPLX6chphnCLF
-Mc8t2w7VbGJFNnJF2SCgKb3vxG+RiwIDAQABAoICABgR7cwj+7Dtk+5DdglxOP3V
-sjbSf5pUo20B1IuDMeEfpPLDSy04NLC8oH7lD4bQULe6dbfmCc3G7xzAy5LY0UkP
-b1k4APsQEK/NDUng1E3L1TmSIHeRJD1QwCKbtU6qxzom2m8612GBecTEXsZUK7Kj
-4kXJeDoU23VvPTwmCaJhhIfa3PpuFj+IObs8RJ/1+kXQYMhBJST4hAIbNnFMf7M/
-J9m68AhZlqtC5+cWIkEjGWkYU7V8iKlJmJ9N2A2ekiTtRwUmkk1BTrK4QPOp6esH
-cbHUoNgv0wrrKh/j52503NIcULyNmaSqvxMPGQwcDaIqceUPZHrUvNP2xbF2emgE
-ym8x2RgWZFXMZOE7srNOhjQKc9nY4ca+LF2cc/ku8RgYlyOwMsmyt8lCRjHPY8If
-su4WXm9s2m7XsVHQpDWQ+v/rS9b0jN2+5+dzoTw2n5ngdEe/3hJrnfw7/x0RlMXD
-Uaomj4coQRPEuttddNXdlG9nPiJTsGb1qu2RCfDNZhlJR7azqeEEPByEB1kQ4RPT
-83K3RnxxSIvusjKin9o6rMJGRe/BIYiMR7FP7SfKXHhhhFpLv9S5SVGh7AHzb+ry
-uzG9gJPshNCXEillt0u4oo10X4bpNPR/tTJh365Gl6VFYlNjCKqvMiBOXgJiXn88
-G70a1HaO8mDhS41GMdbxAoIBAQDrmxl9vtujMhmz/5CukuGNFNk/nwGGhuoEDvQH
-Tej6bgn0SHOQd5MwltKSpGuZ986qHs3vV7kkN8sQQG0uQfyyyZ366TknOI5CCEgz
-QcmvbY79U5JEed0KXtoQPsqkprxeJP+K7yQlGeDGSK9GSN0CI9Jo5H2t0WNObYHN
-uQkMuyx461YSX5c99UcGScUMjn5WmV8w0DNYU8HnkPhGOditZunZTeJ1XcWXLz97
-Ffq43AzInxiJcRY9CNTWw0X8bprXd9ka9UfbOZzNnoKkS8mkaMZyAdbdvKGhvNGG
-WA+p2Yqtzq2C3slSJV/D+ZYIUJb3DgJ9ZcvC5MGG/lUv9g7xAoIBAQDFxqK7/G7+
-gKaHH+OTvDnDe9m+JvNzAc0YM+uYBJBJgZ4XKNWDi6y3JmSCUtbhku6RBgRNN0KK
-3tq/vKzjmvN49pU56ij4atyZqkoY3i6hGyOA5R46fKyb8UMiOZm1tfh8WikRDss1
-h3E1mpfLst61PqPoEdhvXz03lG9iYU6uiE6L7K6peOO7pCmk2jH7zEQmlaWi8hzF
-PzNEF/cHaSBfBubLm8hfZPuNLWE1hCnQItXweT2T9Bcu+042Z9469Z4P3MWOcCug
-Ql05jGojxyAlzYR1I+lMK1FujT0oQ+gFcGNE/ndEoOhFHxmlmD9yLv3LVTMYPHsq
-7YXDLy96nCA7AoIBAQCfJqArjvdy3+745hPnuRRfZsvx7AjtxMjjgO5a2MgeEqLq
-vt5BomRtGBSaNjLxSLHzvOdDXDCWRJIJIlweOTjn1MXArjaLReritBGBflktBYbn
-nMJbOy6TSMto2eGtI2xu8/Mi+LOj1D0/8+1iPun7/hKuBFrZRW4dll9uhiWU0gMS
-k3YK50OU+NDHcKGI/+BbwzLIGHv2mG2NbSIo3f5989zXi4MD4RoOLD5neMtqgEqq
-Yr4Cab+p7wNHJ5VpFZXHIxAm47VsYxiG1SJOtVs6kgQrEw7/reJJDPFEHMxH4cmJ
-6ujOVIwNz21HRpuQdk/kBzSrXE4uErSf6cHFqiMhAoIBAHoG2RmL4x/8WMM+lbft
-huZqYCrG9aacEeNBBbfB+RSheN1pQHPtlh9a/OC8JAECG1g3kifiVJhCcE2lKDc2
-v8p+ugwFwkmkBYB6ZUV6sOKOUBWTSFdl3UpKTdWsHH7VS//N0VDJA/B/JQah3867
-ClZh09e4SwZMiQTl/OOCjn15dJ0453uBL2HzJA6m4fguTE5SPuSO5dl96S+2aaCU
-6Hg5VeWCtNrG/75XpYbTiMj29XFuHORQ0o0WWWeQJrnSGjhHS01bQE+dItADJun0
-To2EhJmSErwAbjn7wyQ44cuZUGadaxFZBna/fZ+ClILrI4R1iRUHHCecbc/EKVNJ
-SUkCggEAXEGJbaefUR8m579CZv0uHg2ZvYkKD9q9P3Xu0vmQXo80HC0kVQNe5KkI
-fSPmn3TIcC/CbfN6PQle/hxzalka9788VZyO8CXmPgw/QJX/Bmn8uD6of2DNZkd6
-SnOnSnWZU79St9YVWpuq/Nip60T4pFKguOUJJLyQmu8GRgDHbQBmXpOJuWGidnwy
-CyrNYNqKLbPxb1NCOyEYBWZpYW4ujfiXMGpQ3JhB7yCrNeaHO6TK/xmIo5lrrspW
-/advikrrmlOmP11lrQ1HIDH1oCauImRS99AxXhOJoZd0aFdOzGOv25GbnqxMCxrj
-+mnlJc8OPylR/oZHIJW2cveEQjS4qA==
+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
+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()

Reply via email to