Added unit tests for gRPC support in libprocess.

We tested the following 6 scenarios when making gRPC calls:

1. The server responds with an OK.
2. The server responds with a failure.
3. The client discards the call before the server starts.
4. The client discards the call when the server is processing it.
5. The client makes mulitple concurrent calls.
6. The client shuts down during a call.
7. The server is unreachable.
8. The server dose not respond in time.

Review: https://reviews.apache.org/r/61098


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f2b16cab
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f2b16cab
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f2b16cab

Branch: refs/heads/master
Commit: f2b16cab5449a3a6def8cb16a88d2252984c9cfa
Parents: 7ce4053
Author: Chun-Hung Hsiao <chhs...@mesosphere.io>
Authored: Mon Jul 24 17:05:56 2017 -0700
Committer: Jie Yu <yujie....@gmail.com>
Committed: Thu Aug 10 16:55:05 2017 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/tests/grpc_tests.cpp   | 367 ++++++++++++++++++++
 3rdparty/libprocess/src/tests/grpc_tests.proto |  35 ++
 2 files changed, 402 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f2b16cab/3rdparty/libprocess/src/tests/grpc_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/grpc_tests.cpp 
b/3rdparty/libprocess/src/tests/grpc_tests.cpp
new file mode 100644
index 0000000..a11b6f0
--- /dev/null
+++ b/3rdparty/libprocess/src/tests/grpc_tests.cpp
@@ -0,0 +1,367 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include <process/future.hpp>
+#include <process/grpc.hpp>
+#include <process/gtest.hpp>
+
+#include <stout/path.hpp>
+#include <stout/try.hpp>
+
+#include <stout/tests/utils.hpp>
+
+#include "grpc_tests.pb.h"
+#include "grpc_tests.grpc.pb.h"
+
+namespace client = process::grpc::client;
+
+using std::shared_ptr;
+using std::string;
+using std::unique_ptr;
+
+using ::grpc::InsecureServerCredentials;
+using ::grpc::Server;
+using ::grpc::ServerBuilder;
+using ::grpc::ServerContext;
+using ::grpc::Status;
+
+using process::Future;
+using process::Promise;
+
+using process::grpc::Channel;
+
+using testing::_;
+using testing::DoAll;
+using testing::InvokeWithoutArgs;
+using testing::Return;
+
+namespace tests {
+
+class PingPongServer : public PingPong::Service
+{
+public:
+  PingPongServer()
+  {
+    EXPECT_CALL(*this, Send(_, _, _))
+      .WillRepeatedly(Return(Status::OK));
+  }
+
+  MOCK_METHOD3(Send, Status(ServerContext*, const Ping* ping, Pong* pong));
+
+  Try<Nothing> Startup(const string& address)
+  {
+    ServerBuilder builder;
+    builder.AddListeningPort(address, InsecureServerCredentials());
+    builder.RegisterService(this);
+
+    server = builder.BuildAndStart();
+    if (!server) {
+      return Error("Unable to start a gRPC server.");
+    }
+
+    return Nothing();
+  }
+
+  Try<Nothing> Shutdown()
+  {
+    server->Shutdown();
+    server->Wait();
+
+    return Nothing();
+  }
+
+private:
+  unique_ptr<Server> server;
+};
+
+
+class GRPCClientTest : public TemporaryDirectoryTest {
+protected:
+  string server_address() const
+  {
+    // TODO(chhsiao): Use in-process tranport instead of a Unix domain
+    // socket once gRPC supports it for Windows support.
+    // https://github.com/grpc/grpc/pull/11145
+    return "unix://" + path::join(sandbox.get(), "socket");
+  }
+};
+
+
+// This test verifies that a gRPC future is properly set once the
+// given call is processed by the server.
+TEST_F(GRPCClientTest, Success)
+{
+  PingPongServer server;
+  ASSERT_SOME(server.Startup(server_address()));
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+
+  AWAIT_EXPECT_READY(pong);
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+
+// This test verifies that we can have multiple outstanding gRPC calls.
+TEST_F(GRPCClientTest, ConcurrentRPCs)
+{
+  PingPongServer server;
+  ASSERT_SOME(server.Startup(server_address()));
+
+  shared_ptr<Promise<Nothing>> processed1(new Promise<Nothing>());
+  shared_ptr<Promise<Nothing>> processed2(new Promise<Nothing>());
+  shared_ptr<Promise<Nothing>> processed3(new Promise<Nothing>());
+  shared_ptr<Promise<Nothing>> pinged(new Promise<Nothing>());
+
+  EXPECT_CALL(server, Send(_, _, _))
+    .WillOnce(DoAll(
+      InvokeWithoutArgs([=] {
+        processed1->set(Nothing());
+        AWAIT_READY(pinged->future());
+      }),
+      Return(Status::OK)))
+    .WillOnce(DoAll(
+      InvokeWithoutArgs([=] {
+        processed2->set(Nothing());
+        AWAIT_READY(pinged->future());
+      }),
+      Return(Status::OK)))
+    .WillOnce(DoAll(
+      InvokeWithoutArgs([=] {
+        processed3->set(Nothing());
+        AWAIT_READY(pinged->future());
+      }),
+      Return(Status::OK)));
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  Future<Pong> pong1 = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+  Future<Pong> pong2 = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+  Future<Pong> pong3 = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+
+  AWAIT_READY(processed1->future());
+  AWAIT_READY(processed2->future());
+  AWAIT_READY(processed3->future());
+
+  pinged->set(Nothing());
+
+  AWAIT_EXPECT_READY(pong1);
+  AWAIT_EXPECT_READY(pong2);
+  AWAIT_EXPECT_READY(pong3);
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+
+// This test verifies that a gRPC future fails when the server responds
+// with a status other than OK for the given call.
+TEST_F(GRPCClientTest, Failed)
+{
+  PingPongServer server;
+
+  EXPECT_CALL(server, Send(_, _, _))
+    .WillOnce(Return(Status::CANCELLED));
+
+  ASSERT_SOME(server.Startup(server_address()));
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+
+  AWAIT_EXPECT_FAILED(pong);
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+
+// This test verifies that a gRPC future can be discarded before the
+// server processes the given call.
+TEST_F(GRPCClientTest, DiscardedBeforeServerStarted)
+{
+  PingPongServer server;
+
+  EXPECT_CALL(server, Send(_, _, _))
+    .Times(0);
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+  pong.discard();
+
+  ASSERT_SOME(server.Startup(server_address()));
+
+  AWAIT_EXPECT_DISCARDED(pong);
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+
+// This test verifies that a gRPC future can be discarded before and
+// when the server is processing the given call.
+TEST_F(GRPCClientTest, DiscardedWhenServerProcessing)
+{
+  PingPongServer server;
+
+  shared_ptr<Promise<Nothing>> processed(new Promise<Nothing>());
+  shared_ptr<Promise<Nothing>> discarded(new Promise<Nothing>());
+
+  EXPECT_CALL(server, Send(_, _, _))
+    .WillOnce(DoAll(
+        InvokeWithoutArgs([=] {
+          processed->set(Nothing());
+          AWAIT_READY(discarded->future());
+        }),
+        Return(Status::OK)));
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  ASSERT_SOME(server.Startup(server_address()));
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+  AWAIT_READY(processed->future());
+
+  pong.discard();
+  discarded->set(Nothing());
+
+  AWAIT_EXPECT_DISCARDED(pong);
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+
+// This test verifies that a gRPC future fails properly when the runtime
+// is shut down before the server responds.
+TEST_F(GRPCClientTest, ClientShutdown)
+{
+  PingPongServer server;
+
+  shared_ptr<Promise<Nothing>> processed(new Promise<Nothing>());
+  shared_ptr<Promise<Nothing>> shutdown(new Promise<Nothing>());
+
+  EXPECT_CALL(server, Send(_, _, _))
+    .WillOnce(DoAll(
+        InvokeWithoutArgs([=] {
+          processed->set(Nothing());
+          AWAIT_READY(shutdown->future());
+        }),
+        Return(Status::OK)));
+
+  ASSERT_SOME(server.Startup(server_address()));
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+
+  AWAIT_READY(processed->future());
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  shutdown->set(Nothing());
+  AWAIT_EXPECT_FAILED(pong);
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+
+// This test verifies that a gRPC future fails when it is unable to
+// connect to the server.
+TEST_F(GRPCClientTest, ServerUnreachable)
+{
+  client::Runtime runtime;
+  Channel channel("nosuchhost");
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  AWAIT_EXPECT_FAILED(pong);
+}
+
+
+// This test verifies that a gRPC future fails properly when the server
+// hangs when processing the given call.
+TEST_F(GRPCClientTest, ServerTimeout)
+{
+  PingPongServer server;
+
+  shared_ptr<Promise<Nothing>> done(new Promise<Nothing>());
+
+  EXPECT_CALL(server, Send(_, _, _))
+    .WillOnce(DoAll(
+        InvokeWithoutArgs([=] {
+          AWAIT_READY(done->future());
+        }),
+        Return(Status::OK)));
+
+  ASSERT_SOME(server.Startup(server_address()));
+
+  client::Runtime runtime;
+  Channel channel(server_address());
+
+  Future<Pong> pong = runtime.call(
+      channel, GRPC_RPC(PingPong, Send), Ping());
+
+  // TODO(chhsiao): The gRPC library returns a failure after the default
+  // timeout (5 seconds) is passed, no matter when the `CompletionQueue`
+  // is shut down. The timeout should be lowered once we support it.
+  AWAIT_EXPECT_FAILED(pong);
+  done->set(Nothing());
+
+  runtime.terminate();
+  AWAIT_ASSERT_READY(runtime.wait());
+
+  ASSERT_SOME(server.Shutdown());
+}
+
+} // namespace tests {

http://git-wip-us.apache.org/repos/asf/mesos/blob/f2b16cab/3rdparty/libprocess/src/tests/grpc_tests.proto
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/grpc_tests.proto 
b/3rdparty/libprocess/src/tests/grpc_tests.proto
new file mode 100644
index 0000000..c461842
--- /dev/null
+++ b/3rdparty/libprocess/src/tests/grpc_tests.proto
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package tests;
+
+// NOTE: The generated headers for this file have been included in the
+// tests folder to simplify the build process (no need to have protoc
+// available to compile this file). As a result, if there are any
+// changes to this file, the headers must be re-generated and committed
+// alongside changes to this file.
+
+message Ping {}
+
+
+message Pong {}
+
+
+service PingPong {
+  rpc Send(Ping) returns (Pong) {}
+}

Reply via email to