1 //
2 // Copyright 2023 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "test/cpp/interop/pre_stop_hook_server.h"
18
19 #include <thread>
20
21 #include <gmock/gmock.h>
22 #include <gtest/gtest.h>
23
24 #include "absl/strings/str_format.h"
25
26 #include <grpc/grpc.h>
27 #include <grpcpp/grpcpp.h>
28 #include <grpcpp/support/status.h>
29
30 #include "src/core/lib/gprpp/sync.h"
31 #include "src/proto/grpc/testing/empty.pb.h"
32 #include "src/proto/grpc/testing/messages.pb.h"
33 #include "src/proto/grpc/testing/test.grpc.pb.h"
34 #include "test/core/util/port.h"
35 #include "test/core/util/test_config.h"
36
37 namespace grpc {
38 namespace testing {
39 namespace {
40
41 struct CallInfo {
42 public:
43 ClientContext context;
44 Empty request;
45 Empty response;
46
WaitForStatusgrpc::testing::__anonec4ebb110111::CallInfo47 absl::optional<Status> WaitForStatus(
48 absl::Duration timeout = absl::Seconds(1)) {
49 grpc_core::MutexLock lock(&mu);
50 cv.WaitWithTimeout(&mu, timeout);
51 return status_;
52 }
53
SetStatusgrpc::testing::__anonec4ebb110111::CallInfo54 void SetStatus(const Status& status) {
55 grpc_core::MutexLock lock(&mu);
56 status_ = status;
57 cv.SignalAll();
58 }
59
60 private:
61 grpc_core::Mutex mu;
62 grpc_core::CondVar cv;
63 absl::optional<Status> status_;
64 };
65
ServerLoop(HookServiceImpl * service,int port,Server ** server,grpc_core::Mutex * mu,grpc_core::CondVar * condition)66 void ServerLoop(HookServiceImpl* service, int port, Server** server,
67 grpc_core::Mutex* mu, grpc_core::CondVar* condition) {
68 ServerBuilder builder;
69 builder.AddListeningPort(absl::StrFormat("0.0.0.0:%d", port),
70 grpc::InsecureServerCredentials());
71 builder.RegisterService(service);
72 auto s = builder.BuildAndStart();
73 {
74 grpc_core::MutexLock lock(mu);
75 *server = s.get();
76 condition->SignalAll();
77 }
78 s->Wait();
79 }
80
TEST(StandalonePreStopHookServer,StartDoRequestStop)81 TEST(StandalonePreStopHookServer, StartDoRequestStop) {
82 int port = grpc_pick_unused_port_or_die();
83 PreStopHookServerManager server;
84 Status start_status = server.Start(port, 15);
85 ASSERT_TRUE(start_status.ok()) << start_status.error_message();
86 auto channel = CreateChannel(absl::StrFormat("127.0.0.1:%d", port),
87 InsecureChannelCredentials());
88 ASSERT_TRUE(channel);
89 CallInfo info;
90 HookService::Stub stub(std::move(channel));
91 stub.async()->Hook(&info.context, &info.request, &info.response,
92 [&info](const Status& status) { info.SetStatus(status); });
93 ASSERT_TRUE(server.TestOnlyExpectRequests(1));
94 server.Return(StatusCode::INTERNAL, "Just a test");
95 auto status = info.WaitForStatus();
96 ASSERT_TRUE(status.has_value());
97 EXPECT_EQ(status->error_code(), StatusCode::INTERNAL);
98 EXPECT_EQ(status->error_message(), "Just a test");
99 }
100
TEST(StandalonePreStopHookServer,StartServerWhileAlreadyRunning)101 TEST(StandalonePreStopHookServer, StartServerWhileAlreadyRunning) {
102 int port = grpc_pick_unused_port_or_die();
103 PreStopHookServerManager server;
104 Status status = server.Start(port, 15);
105 ASSERT_TRUE(status.ok()) << status.error_message();
106 status = server.Start(port, 15);
107 ASSERT_EQ(status.error_code(), StatusCode::ALREADY_EXISTS)
108 << status.error_message();
109 }
110
TEST(StandalonePreStopHookServer,StopServerWhileRequestPending)111 TEST(StandalonePreStopHookServer, StopServerWhileRequestPending) {
112 int port = grpc_pick_unused_port_or_die();
113 PreStopHookServerManager server;
114 Status start_status = server.Start(port, 15);
115 ASSERT_TRUE(start_status.ok()) << start_status.error_message();
116 auto channel = CreateChannel(absl::StrFormat("127.0.0.1:%d", port),
117 InsecureChannelCredentials());
118 ASSERT_TRUE(channel);
119 CallInfo info;
120 HookService::Stub stub(std::move(channel));
121 stub.async()->Hook(&info.context, &info.request, &info.response,
122 [&info](const Status& status) { info.SetStatus(status); });
123 ASSERT_TRUE(server.TestOnlyExpectRequests(1));
124 ASSERT_TRUE(server.Stop().ok());
125 auto status = info.WaitForStatus();
126 ASSERT_TRUE(status.has_value());
127 EXPECT_EQ(status->error_code(), StatusCode::ABORTED);
128 }
129
TEST(StandalonePreStopHookServer,MultipleRequests)130 TEST(StandalonePreStopHookServer, MultipleRequests) {
131 int port = grpc_pick_unused_port_or_die();
132 PreStopHookServerManager server;
133 Status start_status = server.Start(port, 15);
134 ASSERT_TRUE(start_status.ok()) << start_status.error_message();
135 auto channel = CreateChannel(absl::StrFormat("127.0.0.1:%d", port),
136 InsecureChannelCredentials());
137 ASSERT_TRUE(channel);
138 HookService::Stub stub(std::move(channel));
139 CallInfo info1, info2, info3;
140 server.Return(StatusCode::INTERNAL, "First");
141 stub.async()->Hook(&info1.context, &info1.request, &info1.response,
142 [&](const Status& status) { info1.SetStatus(status); });
143 auto status = info1.WaitForStatus();
144 ASSERT_TRUE(status.has_value());
145 EXPECT_EQ(status->error_code(), StatusCode::INTERNAL);
146 EXPECT_EQ(status->error_message(), "First");
147 stub.async()->Hook(&info2.context, &info2.request, &info2.response,
148 [&](const Status& status) { info2.SetStatus(status); });
149 ASSERT_TRUE(server.TestOnlyExpectRequests(1, absl::Milliseconds(500)));
150 stub.async()->Hook(&info3.context, &info3.request, &info3.response,
151 [&](const Status& status) { info3.SetStatus(status); });
152 server.Return(StatusCode::RESOURCE_EXHAUSTED, "Second");
153 server.Return(StatusCode::DEADLINE_EXCEEDED, "Third");
154 status = info2.WaitForStatus();
155 ASSERT_TRUE(status.has_value());
156 EXPECT_EQ(status->error_code(), StatusCode::RESOURCE_EXHAUSTED);
157 EXPECT_EQ(status->error_message(), "Second");
158 status = info3.WaitForStatus();
159 ASSERT_TRUE(status.has_value());
160 EXPECT_EQ(status->error_code(), StatusCode::DEADLINE_EXCEEDED);
161 EXPECT_EQ(status->error_message(), "Third");
162 }
163
TEST(StandalonePreStopHookServer,StopServerThatNotStarted)164 TEST(StandalonePreStopHookServer, StopServerThatNotStarted) {
165 PreStopHookServerManager server;
166 Status status = server.Stop();
167 EXPECT_EQ(status.error_code(), StatusCode::UNAVAILABLE)
168 << status.error_message();
169 }
170
TEST(StandalonePreStopHookServer,SetStatusBeforeRequestReceived)171 TEST(StandalonePreStopHookServer, SetStatusBeforeRequestReceived) {
172 int port = grpc_pick_unused_port_or_die();
173 PreStopHookServerManager server;
174 Status start_status = server.Start(port, 15);
175 server.Return(StatusCode::INTERNAL, "Just a test");
176 ASSERT_TRUE(start_status.ok()) << start_status.error_message();
177 auto channel = CreateChannel(absl::StrFormat("127.0.0.1:%d", port),
178 InsecureChannelCredentials());
179 ASSERT_TRUE(channel);
180 HookService::Stub stub(std::move(channel));
181 CallInfo info;
182 auto status = stub.Hook(&info.context, info.request, &info.response);
183 EXPECT_EQ(status.error_code(), StatusCode::INTERNAL);
184 EXPECT_EQ(status.error_message(), "Just a test");
185 }
186
TEST(PreStopHookService,StartDoRequestStop)187 TEST(PreStopHookService, StartDoRequestStop) {
188 int port = grpc_pick_unused_port_or_die();
189 grpc_core::Mutex mu;
190 grpc_core::CondVar condition;
191 Server* server = nullptr;
192 HookServiceImpl service;
193 std::thread server_thread(ServerLoop, &service, port, &server, &mu,
194 &condition);
195 {
196 grpc_core::MutexLock lock(&mu);
197 while (server == nullptr) {
198 condition.Wait(&mu);
199 }
200 }
201 auto channel = CreateChannel(absl::StrFormat("127.0.0.1:%d", port),
202 InsecureChannelCredentials());
203 ASSERT_TRUE(channel);
204 CallInfo infos[3];
205 HookService::Stub stub(std::move(channel));
206 stub.async()->Hook(
207 &infos[0].context, &infos[0].request, &infos[0].response,
208 [&infos](const Status& status) { infos[0].SetStatus(status); });
209 stub.async()->Hook(
210 &infos[1].context, &infos[1].request, &infos[1].response,
211 [&infos](const Status& status) { infos[1].SetStatus(status); });
212 ASSERT_TRUE(service.TestOnlyExpectRequests(2, absl::Milliseconds(100)));
213 ClientContext ctx;
214 SetReturnStatusRequest request;
215 request.set_grpc_code_to_return(StatusCode::INTERNAL);
216 request.set_grpc_status_description("Just a test");
217 Empty response;
218 ASSERT_EQ(stub.SetReturnStatus(&ctx, request, &response).error_code(),
219 StatusCode::OK);
220 auto status = infos[0].WaitForStatus();
221 ASSERT_TRUE(status.has_value());
222 EXPECT_EQ(status->error_code(), StatusCode::INTERNAL);
223 EXPECT_EQ(status->error_message(), "Just a test");
224 status = infos[1].WaitForStatus();
225 ASSERT_TRUE(status.has_value());
226 EXPECT_EQ(status->error_code(), StatusCode::INTERNAL);
227 EXPECT_EQ(status->error_message(), "Just a test");
228 status = stub.Hook(&infos[2].context, infos[2].request, &infos[2].response);
229 ASSERT_TRUE(status.has_value());
230 EXPECT_EQ(status->error_code(), StatusCode::INTERNAL);
231 EXPECT_EQ(status->error_message(), "Just a test");
232 CallInfo reset_call_info;
233 ASSERT_TRUE(stub.ClearReturnStatus(&reset_call_info.context,
234 reset_call_info.request,
235 &reset_call_info.response)
236 .ok());
237 CallInfo call_hangs;
238 stub.async()->Hook(
239 &call_hangs.context, &call_hangs.request, &call_hangs.response,
240 [&](const Status& status) { call_hangs.SetStatus(status); });
241 ASSERT_TRUE(service.TestOnlyExpectRequests(1, absl::Milliseconds(100)));
242 status = call_hangs.WaitForStatus(absl::Milliseconds(100));
243 EXPECT_FALSE(status.has_value()) << status->error_message();
244 service.Stop();
245 EXPECT_EQ(call_hangs.WaitForStatus().value_or(Status::CANCELLED).error_code(),
246 StatusCode::ABORTED);
247 server->Shutdown();
248 server_thread.join();
249 }
250
251 } // namespace
252 } // namespace testing
253 } // namespace grpc
254
main(int argc,char ** argv)255 int main(int argc, char** argv) {
256 ::testing::InitGoogleTest(&argc, argv);
257 grpc::testing::TestEnvironment env(&argc, argv);
258 auto result = RUN_ALL_TESTS();
259 return result;
260 }
261