diff options
Diffstat (limited to 'test/cpp/end2end/client_fork_test.cc')
-rw-r--r-- | test/cpp/end2end/client_fork_test.cc | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/test/cpp/end2end/client_fork_test.cc b/test/cpp/end2end/client_fork_test.cc new file mode 100644 index 0000000000..df473fecfe --- /dev/null +++ b/test/cpp/end2end/client_fork_test.cc @@ -0,0 +1,172 @@ +// Copyright 2022 The gRPC Authors +// +// Licensed 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 <grpc/support/port_platform.h> + +#ifndef GRPC_ENABLE_FORK_SUPPORT +// No-op for builds without fork support. +int main(int /* argc */, char** /* argv */) { return 0; } +#else // GRPC_ENABLE_FORK_SUPPORT + +#include <signal.h> + +#include <gtest/gtest.h> + +#include "absl/strings/str_cat.h" + +#include <grpc/fork.h> +#include <grpc/grpc.h> +#include <grpc/support/log.h> +#include <grpc/support/time.h> +#include <grpcpp/channel.h> +#include <grpcpp/client_context.h> +#include <grpcpp/create_channel.h> +#include <grpcpp/server.h> +#include <grpcpp/server_builder.h> +#include <grpcpp/server_context.h> + +#include "src/core/lib/gprpp/fork.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" +#include "test/cpp/util/test_config.h" + +namespace grpc { +namespace testing { +namespace { + +class ServiceImpl final : public EchoTestService::Service { + Status BidiStream( + ServerContext* /*context*/, + ServerReaderWriter<EchoResponse, EchoRequest>* stream) override { + EchoRequest request; + EchoResponse response; + while (stream->Read(&request)) { + gpr_log(GPR_INFO, "recv msg %s", request.message().c_str()); + response.set_message(request.message()); + stream->Write(response); + gpr_log(GPR_INFO, "wrote msg %s", response.message().c_str()); + } + return Status::OK; + } +}; + +std::unique_ptr<EchoTestService::Stub> MakeStub(const std::string& addr) { + return EchoTestService::NewStub( + grpc::CreateChannel(addr, InsecureChannelCredentials())); +} + +TEST(ClientForkTest, ClientCallsBeforeAndAfterForkSucceed) { + grpc_core::Fork::Enable(true); + + int port = grpc_pick_unused_port_or_die(); + std::string addr = absl::StrCat("localhost:", port); + + pid_t server_pid = fork(); + switch (server_pid) { + case -1: // fork failed + GTEST_FAIL() << "failure forking"; + case 0: // post-fork child + { + ServiceImpl impl; + grpc::ServerBuilder builder; + builder.AddListeningPort(addr, grpc::InsecureServerCredentials()); + builder.RegisterService(&impl); + std::unique_ptr<Server> server(builder.BuildAndStart()); + server->Wait(); + return; + } + default: // post-fork parent + break; + } + + // Do a round trip before we fork. + // NOTE: without this scope, test running with the epoll1 poller will fail. + { + std::unique_ptr<EchoTestService::Stub> stub = MakeStub(addr); + EchoRequest request; + EchoResponse response; + ClientContext context; + context.set_wait_for_ready(true); + + auto stream = stub->BidiStream(&context); + + request.set_message("Hello"); + ASSERT_TRUE(stream->Write(request)); + ASSERT_TRUE(stream->Read(&response)); + ASSERT_EQ(response.message(), request.message()); + } + // Fork and do round trips in the post-fork parent and child. + pid_t child_client_pid = fork(); + switch (child_client_pid) { + case -1: // fork failed + GTEST_FAIL() << "fork failed"; + case 0: // post-fork child + { + gpr_log(GPR_DEBUG, "In post-fork child"); + EchoRequest request; + EchoResponse response; + ClientContext context; + context.set_wait_for_ready(true); + + std::unique_ptr<EchoTestService::Stub> stub = MakeStub(addr); + auto stream = stub->BidiStream(&context); + + request.set_message("Hello again from child"); + ASSERT_TRUE(stream->Write(request)); + ASSERT_TRUE(stream->Read(&response)); + ASSERT_EQ(response.message(), request.message()); + exit(0); + } + default: // post-fork parent + { + gpr_log(GPR_DEBUG, "In post-fork parent"); + EchoRequest request; + EchoResponse response; + ClientContext context; + context.set_wait_for_ready(true); + + std::unique_ptr<EchoTestService::Stub> stub = MakeStub(addr); + auto stream = stub->BidiStream(&context); + + request.set_message("Hello again from parent"); + EXPECT_TRUE(stream->Write(request)); + EXPECT_TRUE(stream->Read(&response)); + EXPECT_EQ(response.message(), request.message()); + + // Wait for the post-fork child to exit; ensure it exited cleanly. + int child_status; + ASSERT_EQ(waitpid(child_client_pid, &child_status, 0), child_client_pid) + << "failed to get status of child client"; + ASSERT_EQ(WEXITSTATUS(child_status), 0) << "child did not exit cleanly"; + } + } + + kill(server_pid, SIGINT); +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + grpc::testing::InitTest(&argc, &argv, true); + grpc::testing::TestEnvironment env(&argc, argv); + grpc_init(); + int res = RUN_ALL_TESTS(); + grpc_shutdown(); + return res; +} +#endif // GRPC_ENABLE_FORK_SUPPORT |