Jelajahi Sumber

Add implementation of a QAbstractGrpcChannel based on gRPC C++ native library

Fixes: #145
Giulio Girardi 4 tahun lalu
induk
melakukan
185bba8ff4

+ 4 - 3
README.md

@@ -183,7 +183,7 @@ file(GLOB PROTO_FILES ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/path/to/protofile1.pr
  ...
  ${CMAKE_CURRENT_SOURCE_DIR}/path/to/protofileN.proto)
 # Function below generates source files for specified PROTO_FILES,
-# and link them to the MyTarget as static library 
+# and link them to the MyTarget as static library
 add_executable(MyTarget main.cpp) # Add your target here
 qtprotobuf_generate(TARGET MyTarget
 OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated
@@ -268,8 +268,9 @@ qtprotobuf_link_target is cmake helper function that links generated protobuf ta
 
 *QT_PROTOBUF_EXECUTABLE* - contains full path to QtProtobuf generator add_executable
 
-</br>
->**Note:** In case if you use static QtProtobuf not with cmake/qmake build system, you additionaly **need manually** add QT_PROTOBUF_STATIC compiler definition
+*QT_PROTOBUF_NATIVE_GRPC_CHANNEL* - build an additional channel wrapping native gGRPC C++ library (**Note:** Additional linking to gRPC is required)
+
+> **Note:** In case if you use static QtProtobuf not with cmake/qmake build system, you additionaly **need manually** add QT_PROTOBUF_STATIC compiler definition
 
 ## Integration with qmake project
 

+ 13 - 2
src/grpc/CMakeLists.txt

@@ -24,7 +24,8 @@ file(GLOB SOURCES qgrpcasyncoperationbase.cpp
     qgrpccredentials.cpp
     qgrpcsslcredentials.cpp
     qgrpcinsecurecredentials.cpp
-    qgrpcuserpasswordcredentials.cpp)
+    qgrpcuserpasswordcredentials.cpp
+)
 
 file(GLOB HEADERS qgrpcasyncoperationbase_p.h
     qgrpcasyncreply.h
@@ -40,6 +41,11 @@ file(GLOB HEADERS qgrpcasyncoperationbase_p.h
     qgrpcuserpasswordcredentials.h
     qtgrpcglobal.h)
 
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    list(APPEND SOURCES qgrpcchannel.cpp)
+    list(APPEND HEADERS qgrpcchannel.h qgrpcchannel_p.h)
+endif()
+
 protobuf_generate_qt_headers(PUBLIC_HEADER ${HEADERS} COMPONENT ${TARGET})
 
 add_library(${TARGET} ${SOURCES})
@@ -57,12 +63,17 @@ extract_qt_variable(QT_HOST_DATA)
 target_compile_definitions(${TARGET} PRIVATE QT_BUILD_GRPC_LIB)
 
 set_target_properties(${TARGET} PROPERTIES VERSION ${PROJECT_VERSION} PUBLIC_HEADER "${HEADERS};${GENERATED_PUBLIC_HEADER}" OUTPUT_NAME ${TARGET})
-target_include_directories(${TARGET} PUBLIC 
+target_include_directories(${TARGET} PUBLIC
     $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
     $<BUILD_INTERFACE:${QT_PROTOBUF_BINARY_DIR}/include/${TARGET}>
     $<INSTALL_INTERFACE:${TARGET_INCLUDE_DIR}>
 )
 target_link_libraries(${TARGET} PUBLIC ${QT_PROTOBUF_PROJECT}::QtProtobuf Qt5::Core Qt5::Network)
+
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    target_link_libraries(${TARGET} PUBLIC gRPC::grpc++)
+endif()
+
 target_compile_features(${TARGET} PUBLIC cxx_std_14
                                          cxx_auto_type
                                          cxx_decltype

+ 349 - 0
src/grpc/qgrpcchannel.cpp

@@ -0,0 +1,349 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Giulio Girardi <giulio.girardi@protechgroup.it>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "qgrpcchannel.h"
+#include "qgrpcchannel_p.h"
+
+#include <QEventLoop>
+#include <QThread>
+
+#include <memory>
+#include <thread>
+#include <unordered_map>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/client_unary_call.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/sync_stream_impl.h>
+#include <grpcpp/security/credentials.h>
+
+#include "qabstractgrpccredentials.h"
+#include "qgrpcasyncreply.h"
+#include "qgrpcstatus.h"
+#include "qgrpcsubscription.h"
+#include "qabstractgrpcclient.h"
+#include "qgrpccredentials.h"
+#include "qprotobufserializerregistry_p.h"
+#include "qtprotobuflogging.h"
+
+using namespace QtProtobuf;
+
+namespace QtProtobuf {
+
+static inline grpc::Status parseByteBuffer(const grpc::ByteBuffer &buffer, QByteArray &data)
+{
+    std::vector<grpc::Slice> slices;
+    auto status = buffer.Dump(&slices);
+
+    if (!status.ok())
+        return status;
+
+    for (auto slice : slices) {
+        data.append(QByteArray((const char *)slice.begin(), slice.size()));
+    }
+
+    return grpc::Status::OK;
+}
+
+static inline void parseQByteArray(const QByteArray &bytearray, grpc::ByteBuffer &buffer)
+{
+    grpc::Slice slice(bytearray.data(), bytearray.size());
+    grpc::ByteBuffer tmp(&slice, 1);
+    buffer.Swap(&tmp);
+}
+
+QGrpcChannelSubscription::QGrpcChannelSubscription(grpc::Channel *channel, const QString &method, const QByteArray &data, QObject *parent) : QObject(parent)
+{
+    grpc::ByteBuffer request;
+    parseQByteArray(data, request);
+
+    reader = grpc_impl::internal::ClientReaderFactory<grpc::ByteBuffer>::Create(channel,
+        grpc::internal::RpcMethod(method.toStdString().c_str(), grpc::internal::RpcMethod::SERVER_STREAMING),
+        &context, request);
+
+    thread = QThread::create([this](){
+        grpc::ByteBuffer response;
+        QByteArray data;
+        grpc::Status status;
+
+        while (reader->Read(&response)) {
+            status = parseByteBuffer(response, data);
+
+            if (!status.ok()) {
+                this->status = {
+                    (QGrpcStatus::StatusCode) status.error_code(),
+                    status.error_message().c_str()
+                };
+
+                return; // exit thread
+            }
+
+            emit this->dataReady(data);
+        }
+
+        status = reader->Finish();
+
+        this->status = {
+            (QGrpcStatus::StatusCode) status.error_code(),
+            status.error_message().c_str()
+        };
+
+        return; // exit thread
+    });
+
+    connect(thread, &QThread::finished, this, &QGrpcChannelSubscription::finished);
+}
+
+void QGrpcChannelSubscription::start()
+{
+    thread->start();
+}
+
+QGrpcChannelSubscription::~QGrpcChannelSubscription()
+{
+    cancel();
+    thread->wait();
+    thread->deleteLater();
+
+    if (reader != nullptr) {
+        delete reader;
+    }
+}
+
+void QGrpcChannelSubscription::cancel() {
+    // TODO: check thread safety
+    qProtoDebug() << "Subscription thread terminated";
+    context.TryCancel();
+}
+
+QGrpcChannelCall::QGrpcChannelCall(grpc::Channel *channel, const QString &method, const QByteArray &data, QObject *parent) : QObject(parent) {
+    grpc::ByteBuffer request;
+    parseQByteArray(data, request);
+
+    thread = QThread::create([this, request, channel, method](){
+        grpc::ByteBuffer response;
+        QByteArray data;
+        grpc::Status status;
+
+        status = grpc::internal::BlockingUnaryCall(channel,
+            grpc::internal::RpcMethod(method.toStdString().c_str(), grpc::internal::RpcMethod::NORMAL_RPC),
+            &context, request, &response
+        );
+
+        if (!status.ok()) {
+            this->status = {
+                static_cast<QGrpcStatus::StatusCode>(status.error_code()),
+                status.error_message().c_str()
+            };
+
+            return; // exit thread
+        }
+
+        status = parseByteBuffer(response, this->response);
+
+        this->status = {
+            static_cast<QGrpcStatus::StatusCode>(status.error_code()),
+            status.error_message().c_str()
+        };
+    });
+
+    connect(thread, &QThread::finished, this, &QGrpcChannelCall::finished);
+}
+
+void QGrpcChannelCall::start()
+{
+    thread->start();
+}
+
+QGrpcChannelCall::~QGrpcChannelCall()
+{
+    cancel();
+    thread->wait();
+    thread->deleteLater();
+}
+
+void QGrpcChannelCall::cancel()
+{
+    // TODO: check thread safety
+    qProtoDebug() << "Call thread terminated";
+    context.TryCancel();
+}
+
+QGrpcChannelPrivate::QGrpcChannelPrivate(const QUrl &url, std::shared_ptr<grpc::ChannelCredentials> credentials)
+{
+    m_channel = grpc::CreateChannel(url.toString().toStdString(), credentials);
+}
+
+QGrpcChannelPrivate::~QGrpcChannelPrivate()
+{
+}
+
+void QGrpcChannelPrivate::call(const QString &method, const QString &service, const QByteArray &args, QGrpcAsyncReply *reply)
+{
+    QString rpcName = QString("/%1/%2").arg(service).arg(method);
+
+    std::shared_ptr<QGrpcChannelCall> call;
+    std::shared_ptr<QMetaObject::Connection> connection(new QMetaObject::Connection);
+    std::shared_ptr<QMetaObject::Connection> abortConnection(new QMetaObject::Connection);
+
+    call.reset(
+        new QGrpcChannelCall(m_channel.get(), rpcName, args, reply),
+        [](QGrpcChannelCall * c) { c->deleteLater(); }
+    );
+
+    *connection = QObject::connect(call.get(), &QGrpcChannelCall::finished, reply, [call, reply, connection, abortConnection](){
+        if (call->status.code() == QGrpcStatus::Ok) {
+            reply->setData(call->response);
+            reply->finished();
+        } else {
+            reply->setData({});
+            reply->error(call->status);
+        }
+
+        QObject::disconnect(*connection);
+        QObject::disconnect(*abortConnection);
+    });
+
+    *abortConnection = QObject::connect(reply, &QGrpcAsyncReply::error, call.get(), [call, connection, abortConnection](const QGrpcStatus &status){
+        if (status.code() == QGrpcStatus::Aborted) {
+            QObject::disconnect(*connection);
+            QObject::disconnect(*abortConnection);
+        }
+    });
+
+    call->start();
+}
+
+QGrpcStatus QGrpcChannelPrivate::call(const QString &method, const QString &service, const QByteArray &args, QByteArray &ret)
+{
+    QEventLoop loop;
+
+    QString rpcName = QString("/%1/%2").arg(service).arg(method);
+    QGrpcChannelCall call(m_channel.get(), rpcName, args);
+
+    QObject::connect(&call, &QGrpcChannelCall::finished, &loop, &QEventLoop::quit);
+
+    call.start();
+    loop.exec();
+
+    ret = call.response;
+    return call.status;
+}
+
+void QGrpcChannelPrivate::subscribe(QGrpcSubscription *subscription, const QString &service, QAbstractGrpcClient *client)
+{
+    assert(subscription != nullptr);
+
+    QString rpcName = QString("/%1/%2").arg(service).arg(subscription->method());
+
+    std::shared_ptr<QGrpcChannelSubscription> sub;
+    std::shared_ptr<QMetaObject::Connection> abortConnection(new QMetaObject::Connection);
+    std::shared_ptr<QMetaObject::Connection> readConnection(new QMetaObject::Connection);
+    std::shared_ptr<QMetaObject::Connection> clientConnection(new QMetaObject::Connection);
+    std::shared_ptr<QMetaObject::Connection> connection(new QMetaObject::Connection);
+
+    sub.reset(
+        new QGrpcChannelSubscription(m_channel.get(), rpcName, subscription->arg(), subscription),
+        [](QGrpcChannelSubscription * sub) { sub->deleteLater(); }
+    );
+
+    *readConnection = QObject::connect(sub.get(), &QGrpcChannelSubscription::dataReady, subscription, [subscription](const QByteArray &data) {
+        subscription->handler(data);
+    });
+
+    *connection = QObject::connect(sub.get(), &QGrpcChannelSubscription::finished, subscription, [sub, subscription, readConnection, abortConnection, service, connection, clientConnection](){
+        qProtoDebug() << "Subscription ended with server closing connection";
+
+        QObject::disconnect(*connection);
+        QObject::disconnect(*readConnection);
+        QObject::disconnect(*abortConnection);
+        QObject::disconnect(*clientConnection);
+
+        if (sub->status.code() != QGrpcStatus::Ok)
+        {
+            subscription->error(sub->status);
+        }
+    });
+
+    *abortConnection = QObject::connect(subscription, &QGrpcSubscription::finished, sub.get(), [connection, abortConnection, readConnection, sub, clientConnection] {
+        qProtoDebug() << "Subscription client was finished";
+
+        QObject::disconnect(*connection);
+        QObject::disconnect(*readConnection);
+        QObject::disconnect(*abortConnection);
+        QObject::disconnect(*clientConnection);
+
+        sub->cancel();
+    });
+
+    *clientConnection = QObject::connect(client, &QAbstractGrpcClient::destroyed, sub.get(), [readConnection, connection, abortConnection, sub, clientConnection](){
+        qProtoDebug() << "Grpc client was destroyed";
+
+        QObject::disconnect(*connection);
+        QObject::disconnect(*readConnection);
+        QObject::disconnect(*abortConnection);
+        QObject::disconnect(*clientConnection);
+
+        sub->cancel();
+    });
+
+    sub->start();
+}
+
+QGrpcChannel::QGrpcChannel(const QUrl &url, std::shared_ptr<grpc::ChannelCredentials> credentials) : QAbstractGrpcChannel()
+  , dPtr(std::make_unique<QGrpcChannelPrivate>(url, credentials))
+{
+}
+
+QGrpcChannel::~QGrpcChannel()
+{
+}
+
+QGrpcStatus QGrpcChannel::call(const QString &method, const QString &service, const QByteArray &args, QByteArray &ret)
+{
+    return dPtr->call(method, service, args, ret);
+}
+
+void QGrpcChannel::call(const QString &method, const QString &service, const QByteArray &args, QGrpcAsyncReply *reply)
+{
+    dPtr->call(method, service, args, reply);
+}
+
+void QGrpcChannel::subscribe(QGrpcSubscription *subscription, const QString &service, QAbstractGrpcClient *client)
+{
+    dPtr->subscribe(subscription, service, client);
+}
+
+std::shared_ptr<QAbstractProtobufSerializer> QGrpcChannel::serializer() const
+{
+    //TODO: make selection based on credentials or channel settings
+    return QProtobufSerializerRegistry::instance().getSerializer("protobuf");
+}
+
+}

+ 70 - 0
src/grpc/qgrpcchannel.h

@@ -0,0 +1,70 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Giulio Girardi <giulio.girardi@protechgroup.it>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once //QGrpcChannel
+
+#include <QUrl>
+
+#include <grpcpp/security/credentials.h>
+
+#include <memory>
+
+#include "qabstractgrpcchannel.h"
+
+namespace QtProtobuf {
+
+class QAbstractGrpcCredentials;
+struct QGrpcChannelPrivate;
+
+/*!
+ * \ingroup QtGrpc
+ * \brief The QGrpcChannel class is a gRPC-cpp native api implementation of QAbstractGrpcChannel interface
+ * \details QGrpcChannel accepts the same grpc::ChannelCredentials type that is
+ *          required by native-api grpc::CreateChannel.
+ *          See https://grpc.github.io/grpc/cpp/classgrpc__impl_1_1_channel_credentials.html.
+ */
+class Q_GRPC_EXPORT QGrpcChannel final : public QAbstractGrpcChannel
+{
+public:
+    /*!
+     * \brief QGrpcChannel constructs QGrpcChannel
+     * \param name uri used to establish channel connection
+     * \param credentials grpc credientials object
+     */
+    QGrpcChannel(const QUrl &name, std::shared_ptr<grpc::ChannelCredentials> credentials);
+    ~QGrpcChannel();
+
+    QGrpcStatus call(const QString &method, const QString &service, const QByteArray &args, QByteArray &ret) override;
+    void call(const QString &method, const QString &service, const QByteArray &args, QtProtobuf::QGrpcAsyncReply *reply) override;
+    void subscribe(QGrpcSubscription *subscription, const QString &service, QAbstractGrpcClient *client) override;
+    std::shared_ptr<QAbstractProtobufSerializer> serializer() const override;
+
+private:
+    Q_DISABLE_COPY_MOVE(QGrpcChannel)
+
+    std::unique_ptr<QGrpcChannelPrivate> dPtr;
+};
+
+}

+ 107 - 0
src/grpc/qgrpcchannel_p.h

@@ -0,0 +1,107 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Giulio Girardi <giulio.girardi@protechgroup.it>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <QEventLoop>
+#include <QThread>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/sync_stream_impl.h>
+#include <grpcpp/security/credentials.h>
+
+#include "qabstractgrpccredentials.h"
+#include "qgrpcasyncreply.h"
+#include "qgrpcsubscription.h"
+#include "qabstractgrpcclient.h"
+#include "qgrpccredentials.h"
+#include "qprotobufserializerregistry_p.h"
+#include "qtprotobuflogging.h"
+
+namespace QtProtobuf {
+
+//! \private
+class QGrpcChannelSubscription : public QObject {
+    //! \private
+    Q_OBJECT;
+
+public:
+    QGrpcChannelSubscription(grpc::Channel *channel, const QString &method, const QByteArray &data, QObject *parent = nullptr);
+    ~QGrpcChannelSubscription();
+
+    void cancel();
+    void start();
+
+signals:
+    void dataReady(const QByteArray &data);
+    void finished();
+
+public:
+    QGrpcStatus status;
+
+private:
+    QThread *thread;
+    grpc::ClientContext context;
+    grpc_impl::ClientReader<grpc::ByteBuffer> *reader = nullptr;
+};
+
+//! \private
+class QGrpcChannelCall : public QObject {
+    //! \private
+    Q_OBJECT;
+
+public:
+    QGrpcChannelCall(grpc::Channel *channel, const QString &method, const QByteArray &data, QObject *parent = nullptr);
+    ~QGrpcChannelCall();
+
+    void cancel();
+    void start();
+
+signals:
+    void finished();
+
+public:
+    QGrpcStatus status;
+    QByteArray response;
+
+private:
+    QThread *thread;
+    grpc::ClientContext context;
+};
+
+//! \private
+struct QGrpcChannelPrivate {
+    //! \private
+    std::shared_ptr<grpc::Channel> m_channel;
+
+    QGrpcChannelPrivate(const QUrl &url, std::shared_ptr<grpc::ChannelCredentials> credentials);
+    ~QGrpcChannelPrivate();
+
+    void call(const QString &method, const QString &service, const QByteArray &args, QGrpcAsyncReply *reply);
+    QGrpcStatus call(const QString &method, const QString &service, const QByteArray &args, QByteArray &ret);
+    void subscribe(QGrpcSubscription *subscription, const QString &service, QAbstractGrpcClient *client);
+};
+
+};

+ 5 - 0
tests/test_grpc/CMakeLists.txt

@@ -11,6 +11,11 @@ add_test_target(TARGET qtgrpc_secure_test
 add_target_windeployqt(TARGET qtgrpc_secure_test
     QML_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    target_compile_definitions(qtgrpc_test PRIVATE QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    target_compile_definitions(qtgrpc_secure_test PRIVATE QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+endif()
+
 # servers
 add_subdirectory(echoserver)
 add_subdirectory(secureechoserver)

+ 144 - 102
tests/test_grpc/clienttest.cpp

@@ -25,6 +25,9 @@
 
 #include "testservice_grpc.qpb.h"
 #include <QGrpcHttp2Channel>
+#ifdef QT_PROTOBUF_NATIVE_GRPC_CHANNEL
+#include <QGrpcChannel>
+#endif
 #include <QGrpcCredentials>
 #include <QGrpcInsecureCredentials>
 
@@ -34,27 +37,65 @@
 #include <QThread>
 
 #include <QCoreApplication>
+
 #include <gtest/gtest.h>
+#include <gtest/gtest-param-test.h>
 
 #include <qprotobufserializer.h>
 
 using namespace qtprotobufnamespace::tests;
 using namespace QtProtobuf;
 
-class ClientTest : public ::testing::Test
+typedef TestServiceClient* createTestServiceClientFunc();
+
+class ClientTest : public ::testing::TestWithParam<createTestServiceClientFunc*>
 {
+public:
+    static TestServiceClient * createHttp2Client() {
+        auto *c = new TestServiceClient();
+        c->attachChannel(std::make_shared<QGrpcHttp2Channel>(ClientTest::m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials())); \
+        return c;
+    }
+
+#ifdef QT_PROTOBUF_NATIVE_GRPC_CHANNEL
+    static TestServiceClient * createGrpcSocketClient() {
+        auto *c = new TestServiceClient();
+        c->attachChannel(std::make_shared<QGrpcChannel>(ClientTest::m_echoServerSocket, grpc::InsecureChannelCredentials())); \
+        return c;
+    }
+
+    static TestServiceClient * createGrpcHttpClient() {
+        auto *c = new TestServiceClient();
+        c->attachChannel(std::make_shared<QGrpcChannel>(ClientTest::m_echoServerAddressNative, grpc::InsecureChannelCredentials())); \
+        return c;
+    }
+#endif
+
+    static createTestServiceClientFunc *clientCreators[];
+
 protected:
     static void SetUpTestCase() {
         QtProtobuf::qRegisterProtobufTypes();
     }
     static QCoreApplication m_app;
     static int m_argc;
-    static QUrl m_echoServerAddress;
+    static const QUrl m_echoServerAddress;
+    static const QString m_echoServerSocket;
+    static const QString m_echoServerAddressNative;
 };
 
 int ClientTest::m_argc(0);
 QCoreApplication ClientTest::m_app(m_argc, nullptr);
-QUrl ClientTest::m_echoServerAddress("http://localhost:50051", QUrl::StrictMode);
+const QUrl ClientTest::m_echoServerAddress("http://localhost:50051", QUrl::StrictMode);
+const QString ClientTest::m_echoServerAddressNative("localhost:50051");
+const QString ClientTest::m_echoServerSocket("unix:///tmp/test.sock");
+createTestServiceClientFunc* ClientTest::clientCreators[]{
+    ClientTest::createHttp2Client,
+#ifdef QT_PROTOBUF_NATIVE_GRPC_CHANNEL
+    ClientTest::createGrpcHttpClient,
+    ClientTest::createGrpcSocketClient,
+#endif
+};
 
 TEST_F(ClientTest, CheckMethodsGeneration)
 {
@@ -69,28 +110,27 @@ TEST_F(ClientTest, CheckMethodsGeneration)
     delete result;
 }
 
-TEST_F(ClientTest, StringEchoTest)
+TEST_P(ClientTest, StringEchoTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request;
     QPointer<SimpleStringMessage> result(new SimpleStringMessage);
     request.setTestFieldString("Hello beach!");
-    ASSERT_TRUE(testClient.testMethod(request, result) == QGrpcStatus::Ok);
+    ASSERT_TRUE(testClient->testMethod(request, result) == QGrpcStatus::Ok);
     ASSERT_STREQ(result->testFieldString().toStdString().c_str(), "Hello beach!");
     delete result;
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoAsyncTest)
+TEST_P(ClientTest, StringEchoAsyncTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request;
     SimpleStringMessage result;
     request.setTestFieldString("Hello beach!");
     QEventLoop waiter;
 
-    QGrpcAsyncReplyShared reply = testClient.testMethod(request);
+    QGrpcAsyncReplyShared reply = testClient->testMethod(request);
     QObject::connect(reply.get(), &QGrpcAsyncReply::finished, &m_app, [reply, &result, &waiter]() {
         result = reply->read<SimpleStringMessage>();
         reply->deleteLater();
@@ -99,34 +139,35 @@ TEST_F(ClientTest, StringEchoAsyncTest)
 
     waiter.exec();
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Hello beach!");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoAsync2Test)
+TEST_P(ClientTest, StringEchoAsync2Test)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Hello beach!");
     QEventLoop waiter;
-    testClient.testMethod(request, &m_app, [&result, &waiter](QGrpcAsyncReplyShared reply) {
+    testClient->testMethod(request, &m_app, [&result, &waiter](QGrpcAsyncReplyShared reply) {
         result = reply->read<SimpleStringMessage>();
         waiter.quit();
     });
 
     waiter.exec();
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Hello beach!");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoImmediateAsyncAbortTest)
+
+TEST_P(ClientTest, StringEchoImmediateAsyncAbortTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("sleep");
     QEventLoop waiter;
-    QGrpcAsyncReplyShared reply = testClient.testMethod(request);
+    QGrpcAsyncReplyShared reply = testClient->testMethod(request);
 
     result.setTestFieldString("Result not changed by echo");
     QObject::connect(reply.get(), &QGrpcAsyncReply::finished, &m_app, [&waiter, &result, reply]() {
@@ -141,7 +182,7 @@ TEST_F(ClientTest, StringEchoImmediateAsyncAbortTest)
     });
 
     QGrpcStatus::StatusCode clientStatus = QGrpcStatus::StatusCode::Ok;
-    QObject::connect(&testClient, &TestServiceClient::error, [&clientStatus](const QGrpcStatus &status) {
+    QObject::connect(testClient, &TestServiceClient::error, [&clientStatus](const QGrpcStatus &status) {
         clientStatus = status.code();
         std::cerr << status.code() << ":" << status.message().toStdString();
     });
@@ -153,21 +194,21 @@ TEST_F(ClientTest, StringEchoImmediateAsyncAbortTest)
     ASSERT_EQ(clientStatus, QGrpcStatus::StatusCode::Aborted);
     ASSERT_EQ(asyncStatus, QGrpcStatus::StatusCode::Aborted);
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Result not changed by echo");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoDeferredAsyncAbortTest)
+TEST_P(ClientTest, StringEchoDeferredAsyncAbortTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("sleep");
     QEventLoop waiter;
-    QGrpcAsyncReplyShared reply = testClient.testMethod(request);
+    QGrpcAsyncReplyShared reply = testClient->testMethod(request);
 
     result.setTestFieldString("Result not changed by echo");
     bool errorCalled = false;
-    reply = testClient.testMethod(request);
+    reply = testClient->testMethod(request);
     QObject::connect(reply.get(), &QGrpcAsyncReply::finished, &m_app, [reply, &result, &waiter]() {
         result = reply->read<SimpleStringMessage>();
         waiter.quit();
@@ -183,12 +224,12 @@ TEST_F(ClientTest, StringEchoDeferredAsyncAbortTest)
 
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Result not changed by echo");
     ASSERT_TRUE(errorCalled);
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoStreamTest)
+TEST_P(ClientTest, StringEchoStreamTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
@@ -196,7 +237,7 @@ TEST_F(ClientTest, StringEchoStreamTest)
     QEventLoop waiter;
 
     int i = 0;
-    auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
     QObject::connect(subscription.get(), &QGrpcSubscription::updated, &m_app, [&result, &i, &waiter, subscription]() {
         SimpleStringMessage ret = subscription->read<SimpleStringMessage>();
 
@@ -215,12 +256,12 @@ TEST_F(ClientTest, StringEchoStreamTest)
 
     ASSERT_EQ(i, 4);
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Stream1Stream2Stream3Stream4");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoStreamAbortTest)
+TEST_P(ClientTest, StringEchoStreamAbortTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
@@ -228,7 +269,7 @@ TEST_F(ClientTest, StringEchoStreamAbortTest)
     QEventLoop waiter;
 
     int i = 0;
-    auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
     QObject::connect(subscription.get(), &QGrpcSubscription::updated, &m_app, [&result, &i, &waiter, subscription]() {
         SimpleStringMessage ret = subscription->read<SimpleStringMessage>();
         ++i;
@@ -246,12 +287,12 @@ TEST_F(ClientTest, StringEchoStreamAbortTest)
 
     ASSERT_EQ(i, 3);
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Stream1Stream2Stream3");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoStreamAbortByTimerTest)
+TEST_P(ClientTest, StringEchoStreamAbortByTimerTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
@@ -260,7 +301,7 @@ TEST_F(ClientTest, StringEchoStreamAbortByTimerTest)
 
 
     int i = 0;
-    auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
     QTimer::singleShot(3500, subscription.get(), [subscription]() {
         subscription->cancel();
     });
@@ -289,12 +330,12 @@ TEST_F(ClientTest, StringEchoStreamAbortByTimerTest)
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Stream1Stream2Stream3");
     ASSERT_TRUE(isFinished);
     ASSERT_TRUE(!isError);
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
+TEST_P(ClientTest, StringEchoStreamTestRetUpdates)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request;
     QPointer<SimpleStringMessage> result(new SimpleStringMessage);
 
@@ -302,7 +343,7 @@ TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
 
     QEventLoop waiter;
 
-    testClient.subscribeTestMethodServerStreamUpdates(request, result);
+    testClient->subscribeTestMethodServerStreamUpdates(request, result);
 
     int i = 0;
     QObject::connect(result.data(), &SimpleStringMessage::testFieldStringChanged, &m_app, [&i]() {
@@ -315,13 +356,13 @@ TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
     ASSERT_EQ(i, 4);
     ASSERT_STREQ(result->testFieldString().toStdString().c_str(), "Stream4");
     delete result;
+    testClient->deleteLater();
 }
 
 
-TEST_F(ClientTest, HugeBlobEchoStreamTest)
+TEST_P(ClientTest, HugeBlobEchoStreamTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     BlobMessage result;
     BlobMessage request;
     QFile testFile("testfile");
@@ -331,7 +372,7 @@ TEST_F(ClientTest, HugeBlobEchoStreamTest)
     QByteArray dataHash = QCryptographicHash::hash(request.testBytes(), QCryptographicHash::Sha256);
     QEventLoop waiter;
 
-    auto subscription = testClient.subscribeTestMethodBlobServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodBlobServerStreamUpdates(request);
 
     QObject::connect(subscription.get(), &QGrpcSubscription::updated, &m_app, [&result, &waiter, subscription]() {
         BlobMessage ret = subscription->read<BlobMessage>();
@@ -345,18 +386,18 @@ TEST_F(ClientTest, HugeBlobEchoStreamTest)
 
     QByteArray returnDataHash = QCryptographicHash::hash(result.testBytes(), QCryptographicHash::Sha256);
     ASSERT_TRUE(returnDataHash == dataHash);
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StatusMessageAsyncTest)
+TEST_P(ClientTest, StatusMessageAsyncTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request(QString{"Some status message"});
     QGrpcStatus::StatusCode asyncStatus = QGrpcStatus::StatusCode::Ok;
     QEventLoop waiter;
     QString statusMessage;
 
-    QGrpcAsyncReplyShared reply = testClient.testMethodStatusMessage(request);
+    QGrpcAsyncReplyShared reply = testClient->testMethodStatusMessage(request);
     QObject::connect(reply.get(), &QGrpcAsyncReply::error, [&asyncStatus, &waiter, &statusMessage](const QGrpcStatus &status) {
         asyncStatus = status.code();
         statusMessage = status.message();
@@ -367,68 +408,69 @@ TEST_F(ClientTest, StatusMessageAsyncTest)
     waiter.exec();
 
     ASSERT_STREQ(statusMessage.toStdString().c_str(), request.testFieldString().toStdString().c_str());
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StatusMessageClientAsyncTest)
+TEST_P(ClientTest, StatusMessageClientAsyncTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request(QString{"Some status message"});
     QGrpcStatus::StatusCode asyncStatus = QGrpcStatus::StatusCode::Ok;
     QEventLoop waiter;
     QString statusMessage;
 
-    QObject::connect(&testClient, &TestServiceClient::error, [&asyncStatus, &waiter, &statusMessage](const QGrpcStatus &status) {
+    QObject::connect(testClient, &TestServiceClient::error, [&asyncStatus, &waiter, &statusMessage](const QGrpcStatus &status) {
         asyncStatus = status.code();
         statusMessage = status.message();
         waiter.quit();
     });
 
-    testClient.testMethodStatusMessage(request);
+    testClient->testMethodStatusMessage(request);
 
     QTimer::singleShot(20000, &waiter, &QEventLoop::quit);
     waiter.exec();
 
     ASSERT_STREQ(statusMessage.toStdString().c_str(), request.testFieldString().toStdString().c_str());
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StatusMessageClientSyncTest)
+TEST_P(ClientTest, StatusMessageClientSyncTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request(QString{"Some status message"});
     QPointer<SimpleStringMessage> ret(new SimpleStringMessage);
     QGrpcStatus::StatusCode asyncStatus = QGrpcStatus::StatusCode::Ok;
     QEventLoop waiter;
     QString statusMessage;
 
-    QObject::connect(&testClient, &TestServiceClient::error, [&asyncStatus, &waiter, &statusMessage](const QGrpcStatus &status) {
+    QObject::connect(testClient, &TestServiceClient::error, [&asyncStatus, &waiter, &statusMessage](const QGrpcStatus &status) {
         asyncStatus = status.code();
         statusMessage = status.message();
         waiter.quit();
     });
 
-    testClient.testMethodStatusMessage(request, ret);
+    testClient->testMethodStatusMessage(request, ret);
     QTimer::singleShot(20000, &waiter, &QEventLoop::quit);
     waiter.exec();
 
     ASSERT_STREQ(statusMessage.toStdString().c_str(), request.testFieldString().toStdString().c_str());
     delete ret;
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StatusMessageClientSyncTestReturnedStatus)
+TEST_P(ClientTest, StatusMessageClientSyncTestReturnedStatus)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request(QString{"Some status message"});
     QPointer<SimpleStringMessage> ret(new SimpleStringMessage);
     QEventLoop waiter;
     QString statusMessage;
 
-    QGrpcStatus status = testClient.testMethodStatusMessage(request, ret);
+    QGrpcStatus status = testClient->testMethodStatusMessage(request, ret);
 
     ASSERT_STREQ(status.message().toStdString().c_str(), request.testFieldString().toStdString().c_str());
     delete ret;
+    testClient->deleteLater();
 }
 
 
@@ -468,11 +510,10 @@ TEST_F(ClientTest, ClientSyncTestUnattachedChannelSignal)
     delete ret;
 }
 
-TEST_F(ClientTest, AsyncReplySubscribeTest)
+TEST_P(ClientTest, AsyncReplySubscribeTest)
 {
+    auto testClient = (*GetParam())();
     QTimer callTimeout;
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
     SimpleStringMessage request(QString{"Some status message"});
     QGrpcStatus::StatusCode asyncStatus = QGrpcStatus::StatusCode::Ok;
     QEventLoop waiter;
@@ -480,7 +521,7 @@ TEST_F(ClientTest, AsyncReplySubscribeTest)
 
     QObject::connect(&callTimeout, &QTimer::timeout, &waiter, &QEventLoop::quit);
     callTimeout.setInterval(5000);
-    auto reply = testClient.testMethodStatusMessage(request);
+    auto reply = testClient->testMethodStatusMessage(request);
 
     reply->subscribe(&m_app, []() {
         ASSERT_TRUE(false);
@@ -500,7 +541,7 @@ TEST_F(ClientTest, AsyncReplySubscribeTest)
     SimpleStringMessage result;
     request.setTestFieldString("Hello beach!");
 
-    reply = testClient.testMethod(request);
+    reply = testClient->testMethod(request);
     reply->subscribe(&m_app, [reply, &result, &waiter]() {
         result = reply->read<SimpleStringMessage>();
         waiter.quit();
@@ -514,7 +555,7 @@ TEST_F(ClientTest, AsyncReplySubscribeTest)
     result.setTestFieldString("");
     request.setTestFieldString("Hello beach1!");
 
-    reply = testClient.testMethod(request);
+    reply = testClient->testMethod(request);
     reply->subscribe(&m_app, [reply, &result, &waiter]() {
         result = reply->read<SimpleStringMessage>();
         waiter.quit();
@@ -526,19 +567,19 @@ TEST_F(ClientTest, AsyncReplySubscribeTest)
     waiter.exec();
     callTimeout.stop();
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), request.testFieldString().toStdString().c_str());
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, MultipleSubscriptionsTest)
+TEST_P(ClientTest, MultipleSubscriptionsTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     QEventLoop waiter;
     request.setTestFieldString("Stream");
 
-    auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
-    auto subscriptionNext = testClient.subscribeTestMethodServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
+    auto subscriptionNext = testClient->subscribeTestMethodServerStreamUpdates(request);
 
     ASSERT_EQ(subscription, subscriptionNext);
 
@@ -555,18 +596,18 @@ TEST_F(ClientTest, MultipleSubscriptionsTest)
 
     ASSERT_EQ(i, 4);
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Stream1Stream2Stream3Stream4");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, MultipleSubscriptionsCancelTest)
+TEST_P(ClientTest, MultipleSubscriptionsCancelTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
 
-    auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
-    auto subscriptionNext = testClient.subscribeTestMethodServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
+    auto subscriptionNext = testClient->subscribeTestMethodServerStreamUpdates(request);
 
     ASSERT_EQ(subscription, subscriptionNext);
 
@@ -585,10 +626,10 @@ TEST_F(ClientTest, MultipleSubscriptionsCancelTest)
     ASSERT_TRUE(isFinished);
     ASSERT_TRUE(isFinishedNext);
 
-    subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
+    subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
     ASSERT_NE(subscription, subscriptionNext);
 
-    subscriptionNext = testClient.subscribeTestMethodServerStreamUpdates(request);
+    subscriptionNext = testClient->subscribeTestMethodServerStreamUpdates(request);
 
     ASSERT_EQ(subscription, subscriptionNext);
 
@@ -606,29 +647,29 @@ TEST_F(ClientTest, MultipleSubscriptionsCancelTest)
 
     ASSERT_TRUE(isFinished);
     ASSERT_TRUE(isFinishedNext);
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, NonCompatibleArgRetTest)
+TEST_P(ClientTest, NonCompatibleArgRetTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+    auto testClient = (*GetParam())();
     SimpleIntMessage request(2048);
     QPointer<SimpleStringMessage> result(new SimpleStringMessage);
-    ASSERT_TRUE(testClient.testMethodNonCompatibleArgRet(request, result) == QGrpcStatus::Ok);
+    ASSERT_TRUE(testClient->testMethodNonCompatibleArgRet(request, result) == QGrpcStatus::Ok);
     ASSERT_STREQ(result->testFieldString().toStdString().c_str(), "2048");
     delete result;
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoThreadTest)
+TEST_P(ClientTest, StringEchoThreadTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request;
     QPointer<SimpleStringMessage> result(new SimpleStringMessage);
     request.setTestFieldString("Hello beach from thread!");
     bool ok = false;
     std::shared_ptr<QThread> thread(QThread::create([&](){
-        ok = testClient.testMethod(request, result) == QGrpcStatus::Ok;
+        ok = testClient->testMethod(request, result) == QGrpcStatus::Ok;
     }));
 
     thread->start();
@@ -644,7 +685,7 @@ TEST_F(ClientTest, StringEchoThreadTest)
     result = new SimpleStringMessage();
     ok = false;
     thread.reset(QThread::create([&](){
-        ok = testClient.testMethod(request, result) == QGrpcStatus::Ok;
+        ok = testClient->testMethod(request, result) == QGrpcStatus::Ok;
     }));
 
     thread->start();
@@ -653,13 +694,13 @@ TEST_F(ClientTest, StringEchoThreadTest)
     wait.exec();
 
     ASSERT_TRUE(!ok);
+    testClient->deleteLater();
 }
 
 
-TEST_F(ClientTest, StringEchoAsyncThreadTest)
+TEST_P(ClientTest, StringEchoAsyncThreadTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage request;
     SimpleStringMessage result;
     request.setTestFieldString("Hello beach from thread!");
@@ -669,7 +710,7 @@ TEST_F(ClientTest, StringEchoAsyncThreadTest)
     std::shared_ptr<QThread> thread(QThread::create([&](){
         QEventLoop waiter;
         QThread *validThread = QThread::currentThread();
-        QGrpcAsyncReplyShared reply = testClient.testMethod(request);
+        QGrpcAsyncReplyShared reply = testClient->testMethod(request);
         QObject::connect(reply.get(), &QObject::destroyed, [&replyDestroyed]{replyDestroyed = true;});
         QObject::connect(reply.get(), &QGrpcAsyncReply::finished, &waiter, [reply, &result, &waiter, &threadsOk, validThread]() {
             threadsOk &= reply->thread() != QThread::currentThread();
@@ -688,12 +729,12 @@ TEST_F(ClientTest, StringEchoAsyncThreadTest)
     ASSERT_TRUE(replyDestroyed);
     ASSERT_TRUE(threadsOk);
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Hello beach from thread!");
+    testClient->deleteLater();
 }
 
-TEST_F(ClientTest, StringEchoStreamThreadTest)
+TEST_P(ClientTest, StringEchoStreamThreadTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));
+    auto testClient = (*GetParam())();
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
@@ -703,7 +744,7 @@ TEST_F(ClientTest, StringEchoStreamThreadTest)
     std::shared_ptr<QThread> thread(QThread::create([&](){
         QEventLoop waiter;
         QThread *validThread = QThread::currentThread();
-        auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
+        auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
         QObject::connect(subscription.get(), &QGrpcSubscription::updated, &waiter, [&result, &i, &waiter, subscription, &threadsOk, validThread]() {
             SimpleStringMessage ret = subscription->read<SimpleStringMessage>();
             result.setTestFieldString(result.testFieldString() + ret.testFieldString());
@@ -729,6 +770,7 @@ TEST_F(ClientTest, StringEchoStreamThreadTest)
     ASSERT_TRUE(threadsOk);
     ASSERT_EQ(i, 4);
     ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Stream1Stream2Stream3Stream4");
+    testClient->deleteLater();
 }
 
 TEST_F(ClientTest, AttachChannelThreadTest)
@@ -745,10 +787,9 @@ TEST_F(ClientTest, AttachChannelThreadTest)
                  }, ".*");
 }
 
-TEST_F(ClientTest, StreamCancelWhileErrorTimeoutTest)
+TEST_P(ClientTest, StreamCancelWhileErrorTimeoutTest)
 {
-    TestServiceClient testClient;
-    testClient.attachChannel(std::make_shared<QGrpcHttp2Channel>(QUrl("http://localhost:50052", QUrl::StrictMode), QGrpcInsecureCallCredentials() | QGrpcInsecureChannelCredentials()));//Invalid port
+    auto *testClient = (*GetParam())();;
     SimpleStringMessage result;
     SimpleStringMessage request;
     request.setTestFieldString("Stream");
@@ -756,7 +797,7 @@ TEST_F(ClientTest, StreamCancelWhileErrorTimeoutTest)
     QEventLoop waiter;
 
     bool ok = false;
-    auto subscription = testClient.subscribeTestMethodServerStreamUpdates(request);
+    auto subscription = testClient->subscribeTestMethodServerStreamUpdates(request);
     QObject::connect(subscription.get(), &QGrpcSubscription::finished, &m_app, [&ok, &waiter]() {
         ok = true;
         waiter.quit();
@@ -770,3 +811,4 @@ TEST_F(ClientTest, StreamCancelWhileErrorTimeoutTest)
     ASSERT_TRUE(ok);
 }
 
+INSTANTIATE_TEST_SUITE_P(ClientTest, ClientTest, ::testing::ValuesIn(ClientTest::clientCreators));

+ 2 - 0
tests/test_grpc/echoserver/main.cpp

@@ -76,10 +76,12 @@ public:
 int main(int, char *[])
 {
     std::string server_address("localhost:50051");
+    std::string socket_path("unix://tmp/test.sock");
     SimpleTestImpl service;
 
     grpc::ServerBuilder builder;
     builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
+    builder.AddListeningPort(socket_path, grpc::InsecureServerCredentials());
     builder.RegisterService(&service);
     std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
     std::cout << "Server listening on " << server_address << std::endl;

+ 3 - 0
tests/test_grpc/secureechoserver/main.cpp

@@ -79,6 +79,9 @@ int main(int, char *[])
     std::string server_uri("localhost:60051");
     builder.AddListeningPort(server_uri, grpc::SslServerCredentials(opts));
 
+    std::string socket_uri("unix://tmp/test.sock");
+    builder.AddListeningPort(socket_uri, grpc::SslServerCredentials(opts));
+
     SimpleTestImpl service;
     builder.RegisterService(&service);
 

+ 26 - 1
tests/test_grpc/sslclienttest.cpp

@@ -26,6 +26,9 @@
 #include "testservice_grpc.qpb.h"
 
 #include <QGrpcHttp2Channel>
+#ifdef QT_PROTOBUF_NATIVE_GRPC_CHANNEL
+#include <QGrpcChannel>
+#endif
 #include <QGrpcSslCredentials>
 #include <QGrpcInsecureCredentials>
 
@@ -55,7 +58,7 @@ protected:
 int ClientTest::m_argc(0);
 QCoreApplication ClientTest::m_app(m_argc, nullptr);
 
-TEST_F(ClientTest, IncorrectSecureCredentialsTest)
+TEST_F(ClientTest, Http2ChannelIncorrectSecureCredentialsTest)
 {
     //Prepare ssl configuration
     QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
@@ -72,3 +75,25 @@ TEST_F(ClientTest, IncorrectSecureCredentialsTest)
     std::unique_ptr<SimpleStringMessage> result = std::make_unique<SimpleStringMessage>();
     EXPECT_FALSE(testClient.testMethod(SimpleStringMessage{"Hello beach!"}, QPointer<SimpleStringMessage>(result.get())) == QGrpcStatus::Ok);
 }
+
+#ifdef QT_PROTOBUF_NATIVE_GRPC_CHANNEL
+
+TEST_F(ClientTest, GrpcHttpChannelIncorrectSecureCredentialsTest)
+{
+    TestServiceClient testClient;
+    testClient.attachChannel(std::make_shared<QtProtobuf::QGrpcChannel>(QUrl("localhost:60051"), grpc::SslCredentials(grpc::SslCredentialsOptions())));
+
+    std::unique_ptr<SimpleStringMessage> result = std::make_unique<SimpleStringMessage>();
+    EXPECT_FALSE(testClient.testMethod(SimpleStringMessage{"Hello beach!"}, QPointer<SimpleStringMessage>(result.get())) == QGrpcStatus::Ok);
+}
+
+TEST_F(ClientTest, GrpcSocketChannelIncorrectSecureCredentialsTest)
+{
+    TestServiceClient testClient;
+    testClient.attachChannel(std::make_shared<QtProtobuf::QGrpcChannel>(QUrl("unix:///tmp/test.sock"), grpc::SslCredentials(grpc::SslCredentialsOptions())));
+
+    std::unique_ptr<SimpleStringMessage> result = std::make_unique<SimpleStringMessage>();
+    EXPECT_FALSE(testClient.testMethod(SimpleStringMessage{"Hello beach!"}, QPointer<SimpleStringMessage>(result.get())) == QGrpcStatus::Ok);
+}
+
+#endif

+ 54 - 13
tests/test_grpc_qml/CMakeLists.txt

@@ -1,5 +1,3 @@
-set(TARGET qtgrpc_qml_test)
-
 find_package(Qt5 COMPONENTS Core Quick Network Test QuickTest REQUIRED)
 find_package(QtProtobufProject COMPONENTS QtGrpc REQUIRED)
 
@@ -7,19 +5,35 @@ include(${QT_PROTOBUF_CMAKE_DIR}/QtProtobufTest.cmake)
 
 set(CMAKE_AUTOMOC OFF)
 
-file(GLOB SOURCES main.cpp)
 file(GLOB QML_FILES qml/tst_grpc.qml)
 
 qt5_wrap_cpp(MOC_SOURCES test.h)
 
-add_executable(${TARGET} ${MOC_SOURCES} ${SOURCES} ${QML_FILES})
-target_link_libraries(${TARGET} PRIVATE Qt5::Qml Qt5::Quick Qt5::Test Qt5::QuickTest QtProtobufProject::QtGrpc)
+add_executable(qtgrpc_qml_test_http2 ${MOC_SOURCES} http2.cpp ${QML_FILES})
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    add_executable(qtgrpc_qml_test_grpc_http ${MOC_SOURCES} grpc_http.cpp ${QML_FILES})
+    add_executable(qtgrpc_qml_test_grpc_socket ${MOC_SOURCES} grpc_socket.cpp ${QML_FILES})
+endif()
+
+target_link_libraries(qtgrpc_qml_test_http2 PRIVATE Qt5::Qml Qt5::Quick Qt5::Test Qt5::QuickTest QtProtobufProject::QtGrpc)
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    target_link_libraries(qtgrpc_qml_test_grpc_http PRIVATE Qt5::Qml Qt5::Quick Qt5::Test Qt5::QuickTest QtProtobufProject::QtGrpc)
+    target_link_libraries(qtgrpc_qml_test_grpc_socket PRIVATE Qt5::Qml Qt5::Quick Qt5::Test Qt5::QuickTest QtProtobufProject::QtGrpc)
+endif()
 
 if(QT_PROTOBUF_STATIC)
-    target_link_libraries(${TARGET} PRIVATE ${PROTOBUF_QUICK_PLUGIN_NAME} ${GRPC_QUICK_PLUGIN_NAME})
+    target_link_libraries(qtgrpc_qml_test_http2 PRIVATE ${PROTOBUF_QUICK_PLUGIN_NAME} ${GRPC_QUICK_PLUGIN_NAME})
+    if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+        target_link_libraries(qtgrpc_qml_test_grpc_http PRIVATE ${PROTOBUF_QUICK_PLUGIN_NAME} ${GRPC_QUICK_PLUGIN_NAME})
+        target_link_libraries(qtgrpc_qml_test_grpc_socket PRIVATE ${PROTOBUF_QUICK_PLUGIN_NAME} ${GRPC_QUICK_PLUGIN_NAME})
+    endif()
 endif()
 
-qtprotobuf_link_target(${TARGET} qtgrpc_test_qtprotobuf_gen)
+qtprotobuf_link_target(qtgrpc_qml_test_http2 qtgrpc_test_qtprotobuf_gen)
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    qtprotobuf_link_target(qtgrpc_qml_test_grpc_http qtgrpc_test_qtprotobuf_gen)
+    qtprotobuf_link_target(qtgrpc_qml_test_grpc_socket qtgrpc_test_qtprotobuf_gen)
+endif()
 
 if(UNIX)
     set(TEST_DRIVER_NAME "test_driver.sh")
@@ -28,17 +42,44 @@ elseif(WIN32)
 endif()
 
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test_grpc/${TEST_DRIVER_NAME}.in ${TEST_DRIVER_NAME} @ONLY)
-add_test(NAME ${TARGET}
-         COMMAND ${TEST_DRIVER_NAME} $<TARGET_FILE:${TARGET}> $<TARGET_FILE:echoserver> $<TARGET_FILE_NAME:${TARGET}> $<TARGET_FILE_NAME:echoserver>
+add_test(NAME qtgrpc_qml_test_http2
+         COMMAND ${TEST_DRIVER_NAME} $<TARGET_FILE:qtgrpc_qml_test_http2> $<TARGET_FILE:echoserver> $<TARGET_FILE_NAME:qtgrpc_qml_test_http2> $<TARGET_FILE_NAME:echoserver>
 )
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    add_test(NAME qtgrpc_qml_test_grpc_http
+            COMMAND ${TEST_DRIVER_NAME} $<TARGET_FILE:qtgrpc_qml_test_grpc_http> $<TARGET_FILE:echoserver> $<TARGET_FILE_NAME:qtgrpc_qml_test_grpc_http> $<TARGET_FILE_NAME:echoserver>
+    )
+    add_test(NAME qtgrpc_qml_test_grpc_socket
+            COMMAND ${TEST_DRIVER_NAME} $<TARGET_FILE:qtgrpc_qml_test_grpc_socket> $<TARGET_FILE:echoserver> $<TARGET_FILE_NAME:qtgrpc_qml_test_grpc_socket> $<TARGET_FILE_NAME:echoserver>
+    )
+endif()
+
+add_target_qml(TARGET qtgrpc_qml_test_http2 QML_FILES ${QML_FILES})
+add_target_windeployqt(TARGET qtgrpc_qml_test_http2 QML_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qml)
+if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+    add_target_qml(TARGET qtgrpc_qml_test_grpc_http QML_FILES ${QML_FILES})
+    add_target_windeployqt(TARGET qtgrpc_qml_test_grpc_http QML_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qml)
+    add_target_qml(TARGET qtgrpc_qml_test_grpc_socket QML_FILES ${QML_FILES})
+    add_target_windeployqt(TARGET qtgrpc_qml_test_grpc_socket QML_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qml)
+endif()
 
-add_target_qml(TARGET ${TARGET} QML_FILES ${QML_FILES})
-add_target_windeployqt(TARGET ${TARGET} QML_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qml)
 
 if(WIN32)
-    set_tests_properties(${TARGET} PROPERTIES
+    set_tests_properties(qtgrpc_qml_test_http2 PROPERTIES
         ENVIRONMENT QML2_IMPORT_PATH=$<TARGET_FILE_DIR:${PROTOBUF_QUICK_PLUGIN_NAME}>/..\;$<TARGET_FILE_DIR:${GRPC_QUICK_PLUGIN_NAME}>/..)
+    if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+        set_tests_properties(qtgrpc_qml_test_grpc_http PROPERTIES
+            ENVIRONMENT QML2_IMPORT_PATH=$<TARGET_FILE_DIR:${PROTOBUF_QUICK_PLUGIN_NAME}>/..\;$<TARGET_FILE_DIR:${GRPC_QUICK_PLUGIN_NAME}>/..)
+        set_tests_properties(qtgrpc_qml_test_grpc_socket PROPERTIES
+            ENVIRONMENT QML2_IMPORT_PATH=$<TARGET_FILE_DIR:${PROTOBUF_QUICK_PLUGIN_NAME}>/..\;$<TARGET_FILE_DIR:${GRPC_QUICK_PLUGIN_NAME}>/..)
+    endif()
 else()
-    set_tests_properties(${TARGET} PROPERTIES
+    set_tests_properties(qtgrpc_qml_test_http2 PROPERTIES
         ENVIRONMENT QML2_IMPORT_PATH=$<TARGET_FILE_DIR:${PROTOBUF_QUICK_PLUGIN_NAME}>/..:$<TARGET_FILE_DIR:${GRPC_QUICK_PLUGIN_NAME}>/..)
+    if (QT_PROTOBUF_NATIVE_GRPC_CHANNEL)
+        set_tests_properties(qtgrpc_qml_test_grpc_http PROPERTIES
+            ENVIRONMENT QML2_IMPORT_PATH=$<TARGET_FILE_DIR:${PROTOBUF_QUICK_PLUGIN_NAME}>/..:$<TARGET_FILE_DIR:${GRPC_QUICK_PLUGIN_NAME}>/..)
+        set_tests_properties(qtgrpc_qml_test_grpc_socket PROPERTIES
+            ENVIRONMENT QML2_IMPORT_PATH=$<TARGET_FILE_DIR:${PROTOBUF_QUICK_PLUGIN_NAME}>/..:$<TARGET_FILE_DIR:${GRPC_QUICK_PLUGIN_NAME}>/..)
+    endif()
 endif()

+ 38 - 0
tests/test_grpc_qml/grpc_http.cpp

@@ -0,0 +1,38 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "test.h"
+
+#include <QGrpcChannel>
+
+const QString echoServerAddressNative("localhost:50051");
+
+int main(int argc, char **argv)
+{
+	QTEST_SET_MAIN_SOURCE_PATH
+
+	TestSetup setup(std::make_shared<QGrpcChannel>(echoServerAddressNative, grpc::InsecureChannelCredentials()));
+	return quick_test_main_with_setup(argc, argv, "qtgrpc_qml_test_native_socket", nullptr, &setup);
+}

+ 10 - 2
tests/test_grpc_qml/main.cpp → tests/test_grpc_qml/grpc_socket.cpp

@@ -25,6 +25,14 @@
 
 #include "test.h"
 
-QUrl TestSetup::m_echoServerAddress("http://localhost:50051", QUrl::StrictMode);
+#include <QGrpcChannel>
 
-QUICK_TEST_MAIN_WITH_SETUP(qtgrpc_qml_test, TestSetup)
+const QString echoServerSocket("unix:///tmp/test.sock");
+
+int main(int argc, char **argv)
+{
+	QTEST_SET_MAIN_SOURCE_PATH
+
+	TestSetup setup(std::make_shared<QGrpcChannel>(echoServerSocket, grpc::InsecureChannelCredentials()));
+	return quick_test_main_with_setup(argc, argv, "qtgrpc_qml_test_native_socket", nullptr, &setup);
+}

+ 36 - 0
tests/test_grpc_qml/http2.cpp

@@ -0,0 +1,36 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of QtProtobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "test.h"
+
+const QUrl echoServerAddress("http://localhost:50051", QUrl::StrictMode);
+
+int main(int argc, char **argv)
+{
+	QTEST_SET_MAIN_SOURCE_PATH
+
+	TestSetup setup(std::make_shared<QGrpcHttp2Channel>(echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+	return quick_test_main_with_setup(argc, argv, "qtgrpc_qml_test_http2", nullptr, &setup);
+}

+ 5 - 4
tests/test_grpc_qml/test.h

@@ -31,7 +31,9 @@
 #include <QGrpcHttp2Channel>
 #include <QGrpcInsecureCredentials>
 #include <QMetaObject>
+#include <memory>
 
+#include "qabstractgrpcchannel.h"
 #include "testservice_grpc.qpb.h"
 
 using namespace qtprotobufnamespace::tests;
@@ -40,14 +42,13 @@ using namespace QtProtobuf;
 class TestSetup : public QObject {
     Q_OBJECT
 public:
-    static QUrl m_echoServerAddress;
-    TestSetup() {
+    TestSetup(std::shared_ptr<QAbstractGrpcChannel> channel) {
         QtProtobuf::qRegisterProtobufTypes();
         Q_PROTOBUF_IMPORT_QUICK_PLUGIN()
         Q_GRPC_IMPORT_QUICK_PLUGIN()
-        qmlRegisterSingletonType<TestServiceClient>("qtprotobufnamespace.tests", 1, 0, "TestServiceClient", [](QQmlEngine *engine, QJSEngine *) -> QObject *{
+        qmlRegisterSingletonType<TestServiceClient>("qtprotobufnamespace.tests", 1, 0, "TestServiceClient", [channel](QQmlEngine *engine, QJSEngine *) -> QObject *{
             static TestServiceClient clientInstance;
-            clientInstance.attachChannel(std::make_shared<QGrpcHttp2Channel>(m_echoServerAddress, QGrpcInsecureChannelCredentials() | QGrpcInsecureCallCredentials()));
+            clientInstance.attachChannel(channel);
             engine->setObjectOwnership(&clientInstance, QQmlEngine::CppOwnership);
             return &clientInstance;
         });