xref: /aosp_15_r20/external/grpc-grpc/test/core/transport/test_suite/test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "test/core/transport/test_suite/test.h"
16 
17 #include <initializer_list>
18 
19 #include "absl/random/random.h"
20 
21 namespace grpc_core {
22 
23 ///////////////////////////////////////////////////////////////////////////////
24 // TransportTestRegistry
25 
Get()26 TransportTestRegistry& TransportTestRegistry::Get() {
27   static TransportTestRegistry* registry = new TransportTestRegistry();
28   return *registry;
29 }
30 
RegisterTest(absl::string_view name,absl::AnyInvocable<TransportTest * (std::unique_ptr<TransportFixture>,const fuzzing_event_engine::Actions &,absl::BitGenRef)const> create)31 void TransportTestRegistry::RegisterTest(
32     absl::string_view name,
33     absl::AnyInvocable<TransportTest*(std::unique_ptr<TransportFixture>,
34                                       const fuzzing_event_engine::Actions&,
35                                       absl::BitGenRef) const>
36         create) {
37   if (absl::StartsWith(name, "DISABLED_")) return;
38   tests_.push_back({name, std::move(create)});
39 }
40 
41 ///////////////////////////////////////////////////////////////////////////////
42 // TransportTest
43 
RunTest()44 void TransportTest::RunTest() {
45   TestImpl();
46   EXPECT_EQ(pending_actions_.size(), 0)
47       << "There are still pending actions: did you forget to call "
48          "WaitForAllPendingWork()?";
49   transport_pair_.client.reset();
50   transport_pair_.server.reset();
51   event_engine_->TickUntilIdle();
52   event_engine_->UnsetGlobalHooks();
53 }
54 
SetServerAcceptor()55 void TransportTest::SetServerAcceptor() {
56   transport_pair_.server->server_transport()->SetAcceptor(&acceptor_);
57 }
58 
CreateCall()59 CallInitiator TransportTest::CreateCall() {
60   auto call = MakeCall(event_engine_.get(), Arena::Create(1024, &allocator_));
61   call.handler.SpawnInfallible("start-call", [this, handler = call.handler]() {
62     transport_pair_.client->client_transport()->StartCall(handler);
63     return Empty{};
64   });
65   return std::move(call.initiator);
66 }
67 
TickUntilServerCall()68 CallHandler TransportTest::TickUntilServerCall() {
69   WatchDog watchdog(this);
70   for (;;) {
71     auto handler = acceptor_.PopHandler();
72     if (handler.has_value()) return std::move(*handler);
73     event_engine_->Tick();
74   }
75 }
76 
WaitForAllPendingWork()77 void TransportTest::WaitForAllPendingWork() {
78   WatchDog watchdog(this);
79   while (!pending_actions_.empty()) {
80     if (pending_actions_.front()->IsDone()) {
81       pending_actions_.pop();
82       continue;
83     }
84     event_engine_->Tick();
85   }
86 }
87 
Timeout()88 void TransportTest::Timeout() {
89   std::vector<std::string> lines;
90   lines.emplace_back("Timeout waiting for pending actions to complete");
91   while (!pending_actions_.empty()) {
92     auto action = std::move(pending_actions_.front());
93     pending_actions_.pop();
94     if (action->IsDone()) continue;
95     absl::string_view state_name =
96         transport_test_detail::ActionState::StateString(action->Get());
97     absl::string_view file_name = action->file();
98     auto pos = file_name.find_last_of('/');
99     if (pos != absl::string_view::npos) {
100       file_name = file_name.substr(pos + 1);
101     }
102     lines.emplace_back(absl::StrCat("  ", state_name, " ", action->name(), " [",
103                                     action->step(), "]: ", file_name, ":",
104                                     action->line()));
105   }
106   Crash(absl::StrJoin(lines, "\n"));
107 }
108 
RandomString(int min_length,int max_length,absl::string_view character_set)109 std::string TransportTest::RandomString(int min_length, int max_length,
110                                         absl::string_view character_set) {
111   std::string out;
112   int length = absl::LogUniform<int>(rng_, min_length, max_length + 1);
113   for (int i = 0; i < length; ++i) {
114     out.push_back(
115         character_set[absl::Uniform<uint8_t>(rng_, 0, character_set.size())]);
116   }
117   return out;
118 }
119 
RandomStringFrom(std::initializer_list<absl::string_view> choices)120 std::string TransportTest::RandomStringFrom(
121     std::initializer_list<absl::string_view> choices) {
122   size_t idx = absl::Uniform<size_t>(rng_, 0, choices.size());
123   auto it = choices.begin();
124   for (size_t i = 0; i < idx; ++i) ++it;
125   return std::string(*it);
126 }
127 
RandomMetadataKey()128 std::string TransportTest::RandomMetadataKey() {
129   if (absl::Bernoulli(rng_, 0.1)) {
130     return RandomStringFrom({
131         ":path",
132         ":method",
133         ":status",
134         ":authority",
135         ":scheme",
136     });
137   }
138   std::string out;
139   do {
140     out = RandomString(1, 128, "abcdefghijklmnopqrstuvwxyz-_");
141   } while (absl::EndsWith(out, "-bin"));
142   return out;
143 }
144 
RandomMetadataValue(absl::string_view key)145 std::string TransportTest::RandomMetadataValue(absl::string_view key) {
146   if (key == ":method") {
147     return RandomStringFrom({"GET", "POST", "PUT"});
148   }
149   if (key == ":status") {
150     return absl::StrCat(absl::Uniform<int>(rng_, 100, 600));
151   }
152   if (key == ":scheme") {
153     return RandomStringFrom({"http", "https"});
154   }
155   if (key == "te") {
156     return "trailers";
157   }
158   static const NoDestruct<std::string> kChars{[]() {
159     std::string out;
160     for (char c = 32; c < 127; c++) out.push_back(c);
161     return out;
162   }()};
163   return RandomString(0, 128, *kChars);
164 }
165 
RandomMetadataBinaryKey()166 std::string TransportTest::RandomMetadataBinaryKey() {
167   return RandomString(1, 128, "abcdefghijklmnopqrstuvwxyz-_") + "-bin";
168 }
169 
RandomMetadataBinaryValue()170 std::string TransportTest::RandomMetadataBinaryValue() {
171   static const NoDestruct<std::string> kChars{[]() {
172     std::string out;
173     for (int c = 0; c < 256; c++) {
174       out.push_back(static_cast<char>(static_cast<uint8_t>(c)));
175     }
176     return out;
177   }()};
178   return RandomString(0, 4096, *kChars);
179 }
180 
181 std::vector<std::pair<std::string, std::string>>
RandomMetadata()182 TransportTest::RandomMetadata() {
183   size_t size = 0;
184   const size_t max_size = absl::LogUniform<size_t>(rng_, 64, 8000);
185   std::vector<std::pair<std::string, std::string>> out;
186   for (;;) {
187     std::string key;
188     std::string value;
189     if (absl::Bernoulli(rng_, 0.1)) {
190       key = RandomMetadataBinaryKey();
191       value = RandomMetadataBinaryValue();
192     } else {
193       key = RandomMetadataKey();
194       value = RandomMetadataValue(key);
195     }
196     bool include = true;
197     for (size_t i = 0; i < out.size(); ++i) {
198       if (out[i].first == key) {
199         include = false;
200         break;
201       }
202     }
203     if (!include) continue;
204     size_t this_size = 32 + key.size() + value.size();
205     if (size + this_size > max_size) {
206       if (out.empty()) continue;
207       break;
208     }
209     size += this_size;
210     out.emplace_back(std::move(key), std::move(value));
211   }
212   return out;
213 }
214 
RandomMessage()215 std::string TransportTest::RandomMessage() {
216   static const NoDestruct<std::string> kChars{[]() {
217     std::string out;
218     for (int c = 0; c < 256; c++) {
219       out.push_back(static_cast<char>(static_cast<uint8_t>(c)));
220     }
221     return out;
222   }()};
223   return RandomString(0, 1024 * 1024, *kChars);
224 }
225 
226 ///////////////////////////////////////////////////////////////////////////////
227 // TransportTest::Acceptor
228 
CreateArena()229 Arena* TransportTest::Acceptor::CreateArena() {
230   return Arena::Create(1024, allocator_);
231 }
232 
CreateCall(ClientMetadata &,Arena * arena)233 absl::StatusOr<CallInitiator> TransportTest::Acceptor::CreateCall(
234     ClientMetadata&, Arena* arena) {
235   auto call = MakeCall(event_engine_, arena);
236   handlers_.push(std::move(call.handler));
237   return std::move(call.initiator);
238 }
239 
PopHandler()240 absl::optional<CallHandler> TransportTest::Acceptor::PopHandler() {
241   if (!handlers_.empty()) {
242     auto handler = std::move(handlers_.front());
243     handlers_.pop();
244     return handler;
245   }
246   return absl::nullopt;
247 }
248 
249 ///////////////////////////////////////////////////////////////////////////////
250 // ActionState
251 
252 namespace transport_test_detail {
253 
ActionState(NameAndLocation name_and_location)254 ActionState::ActionState(NameAndLocation name_and_location)
255     : name_and_location_(name_and_location), state_(kNotCreated) {}
256 
IsDone()257 bool ActionState::IsDone() {
258   switch (state_) {
259     case kNotCreated:
260     case kNotStarted:
261     case kStarted:
262       return false;
263     case kDone:
264     case kCancelled:
265       return true;
266   }
267 }
268 
269 }  // namespace transport_test_detail
270 
271 }  // namespace grpc_core
272