1 // Copyright 2024 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 #ifndef GRPC_TEST_CORE_CALL_BATCH_BUILDER_H 16 #define GRPC_TEST_CORE_CALL_BATCH_BUILDER_H 17 18 #include "absl/strings/str_cat.h" 19 #include "gtest/gtest.h" 20 #include "src/core/lib/slice/slice.h" 21 #include "test/core/end2end/cq_verifier.h" 22 23 namespace grpc_core { 24 25 using ByteBufferUniquePtr = 26 std::unique_ptr<grpc_byte_buffer, void (*)(grpc_byte_buffer*)>; 27 ByteBufferUniquePtr ByteBufferFromSlice(Slice slice); 28 29 absl::optional<std::string> FindInMetadataArray(const grpc_metadata_array& md, 30 absl::string_view key); 31 32 // Receiving container for incoming metadata. 33 class IncomingMetadata final : public CqVerifier::SuccessfulStateString { 34 public: 35 IncomingMetadata() = default; ~IncomingMetadata()36 ~IncomingMetadata() { 37 if (metadata_ != nullptr) grpc_metadata_array_destroy(metadata_.get()); 38 } 39 40 // Lookup a metadata value by key. 41 absl::optional<std::string> Get(absl::string_view key) const; 42 43 // Make a GRPC_RECV_INITIAL_METADATA op - intended for the framework, not 44 // for tests. 45 grpc_op MakeOp(); 46 47 std::string GetSuccessfulStateString() override; 48 49 private: 50 std::unique_ptr<grpc_metadata_array> metadata_ = 51 std::make_unique<grpc_metadata_array>(grpc_metadata_array{0, 0, nullptr}); 52 }; 53 54 // Receiving container for one incoming message. 55 class IncomingMessage final : public CqVerifier::SuccessfulStateString { 56 public: 57 IncomingMessage() = default; 58 IncomingMessage(const IncomingMessage&) = delete; 59 IncomingMessage& operator=(const IncomingMessage&) = delete; ~IncomingMessage()60 ~IncomingMessage() { 61 if (payload_ != nullptr) grpc_byte_buffer_destroy(payload_); 62 } 63 64 // Get the payload of the message - concatenated together into a string for 65 // easy verification. 66 std::string payload() const; 67 // Check if the message is the end of the stream. is_end_of_stream()68 bool is_end_of_stream() const { return payload_ == nullptr; } 69 // Get the type of the message. byte_buffer_type()70 grpc_byte_buffer_type byte_buffer_type() const { return payload_->type; } 71 // Get the compression algorithm used for the message. compression()72 grpc_compression_algorithm compression() const { 73 return payload_->data.raw.compression; 74 } 75 std::string GetSuccessfulStateString() override; 76 77 // Make a GRPC_OP_RECV_MESSAGE op - intended for the framework, not for 78 // tests. 79 grpc_op MakeOp(); 80 81 // Accessor for CoreEnd2endTest::IncomingCall - get a pointer to the 82 // underlying payload. 83 // We don't want to use this in tests directly. raw_payload_ptr()84 grpc_byte_buffer** raw_payload_ptr() { return &payload_; } 85 86 private: 87 grpc_byte_buffer* payload_ = nullptr; 88 }; 89 90 // Receiving container for incoming status on the client from the server. 91 class IncomingStatusOnClient final : public CqVerifier::SuccessfulStateString { 92 public: 93 IncomingStatusOnClient() = default; 94 IncomingStatusOnClient(const IncomingStatusOnClient&) = delete; 95 IncomingStatusOnClient& operator=(const IncomingStatusOnClient&) = delete; 96 IncomingStatusOnClient(IncomingStatusOnClient&& other) noexcept = default; 97 IncomingStatusOnClient& operator=(IncomingStatusOnClient&& other) noexcept = 98 default; ~IncomingStatusOnClient()99 ~IncomingStatusOnClient() { 100 if (data_ != nullptr) { 101 grpc_metadata_array_destroy(&data_->trailing_metadata); 102 gpr_free(const_cast<char*>(data_->error_string)); 103 } 104 } 105 106 // Get the status code. status()107 grpc_status_code status() const { return data_->status; } 108 // Get the status details. message()109 std::string message() const { 110 return std::string(data_->status_details.as_string_view()); 111 } 112 // Get the error string. error_string()113 std::string error_string() const { 114 return data_->error_string == nullptr ? "" : data_->error_string; 115 } 116 // Get a trailing metadata value by key. 117 absl::optional<std::string> GetTrailingMetadata(absl::string_view key) const; 118 119 std::string GetSuccessfulStateString() override; 120 121 // Make a GRPC_OP_RECV_STATUS_ON_CLIENT op - intended for the framework, not 122 // for tests. 123 grpc_op MakeOp(); 124 125 private: 126 struct Data { 127 grpc_metadata_array trailing_metadata{0, 0, nullptr}; 128 grpc_status_code status; 129 Slice status_details; 130 const char* error_string = nullptr; 131 }; 132 std::unique_ptr<Data> data_ = std::make_unique<Data>(); 133 }; 134 135 // Receiving container for incoming status on the server from the client. 136 class IncomingCloseOnServer final : public CqVerifier::SuccessfulStateString { 137 public: 138 IncomingCloseOnServer() = default; 139 IncomingCloseOnServer(const IncomingCloseOnServer&) = delete; 140 IncomingCloseOnServer& operator=(const IncomingCloseOnServer&) = delete; 141 142 // Get the cancellation bit. was_cancelled()143 bool was_cancelled() const { return cancelled_ != 0; } 144 145 // Make a GRPC_OP_RECV_CLOSE_ON_SERVER op - intended for the framework, not 146 // for tests. 147 grpc_op MakeOp(); 148 GetSuccessfulStateString()149 std::string GetSuccessfulStateString() override { 150 return absl::StrCat("close_on_server: cancelled=", cancelled_); 151 } 152 153 private: 154 int cancelled_; 155 }; 156 157 // Build one batch. Returned from NewBatch (use that to instantiate this!) 158 // Upon destruction of the BatchBuilder, the batch will be executed with any 159 // added batches. 160 class BatchBuilder { 161 public: BatchBuilder(grpc_call * call,CqVerifier * cq_verifier,int tag)162 BatchBuilder(grpc_call* call, CqVerifier* cq_verifier, int tag) 163 : call_(call), tag_(tag), cq_verifier_(cq_verifier) { 164 cq_verifier_->ClearSuccessfulStateStrings(CqVerifier::tag(tag_)); 165 } 166 ~BatchBuilder(); 167 168 BatchBuilder(const BatchBuilder&) = delete; 169 BatchBuilder& operator=(const BatchBuilder&) = delete; 170 BatchBuilder(BatchBuilder&&) noexcept = default; 171 172 // Add a GRPC_OP_SEND_INITIAL_METADATA op. 173 // Optionally specify flags, compression level. 174 BatchBuilder& SendInitialMetadata( 175 std::initializer_list<std::pair<absl::string_view, absl::string_view>> md, 176 uint32_t flags = 0, 177 absl::optional<grpc_compression_level> compression_level = absl::nullopt); 178 179 // Add a GRPC_OP_SEND_MESSAGE op. 180 BatchBuilder& SendMessage(Slice payload, uint32_t flags = 0); 181 BatchBuilder& SendMessage(absl::string_view payload, uint32_t flags = 0) { 182 return SendMessage(Slice::FromCopiedString(payload), flags); 183 } 184 185 // Add a GRPC_OP_SEND_CLOSE_FROM_CLIENT op. 186 BatchBuilder& SendCloseFromClient(); 187 188 // Add a GRPC_OP_SEND_STATUS_FROM_SERVER op. 189 BatchBuilder& SendStatusFromServer( 190 grpc_status_code status, absl::string_view message, 191 std::initializer_list<std::pair<absl::string_view, absl::string_view>> 192 md); 193 194 // Add a GRPC_OP_RECV_INITIAL_METADATA op. RecvInitialMetadata(IncomingMetadata & md)195 BatchBuilder& RecvInitialMetadata(IncomingMetadata& md) { 196 cq_verifier_->AddSuccessfulStateString(CqVerifier::tag(tag_), &md); 197 ops_.emplace_back(md.MakeOp()); 198 return *this; 199 } 200 201 // Add a GRPC_OP_RECV_MESSAGE op. RecvMessage(IncomingMessage & msg)202 BatchBuilder& RecvMessage(IncomingMessage& msg) { 203 cq_verifier_->AddSuccessfulStateString(CqVerifier::tag(tag_), &msg); 204 ops_.emplace_back(msg.MakeOp()); 205 return *this; 206 } 207 208 // Add a GRPC_OP_RECV_STATUS_ON_CLIENT op. RecvStatusOnClient(IncomingStatusOnClient & status)209 BatchBuilder& RecvStatusOnClient(IncomingStatusOnClient& status) { 210 cq_verifier_->AddSuccessfulStateString(CqVerifier::tag(tag_), &status); 211 ops_.emplace_back(status.MakeOp()); 212 return *this; 213 } 214 215 // Add a GRPC_OP_RECV_CLOSE_ON_SERVER op. RecvCloseOnServer(IncomingCloseOnServer & close)216 BatchBuilder& RecvCloseOnServer(IncomingCloseOnServer& close) { 217 cq_verifier_->AddSuccessfulStateString(CqVerifier::tag(tag_), &close); 218 ops_.emplace_back(close.MakeOp()); 219 return *this; 220 } 221 222 private: 223 // We need to track little bits of memory up until the batch is executed. 224 // One Thing is one such block of memory. 225 // We specialize it with SpecificThing to track a specific type of memory. 226 // These get placed on things_ and deleted when the batch is executed. 227 class Thing { 228 public: 229 virtual ~Thing() = default; 230 }; 231 template <typename T> 232 class SpecificThing final : public Thing { 233 public: 234 template <typename... Args> SpecificThing(Args &&...args)235 explicit SpecificThing(Args&&... args) : t_(std::forward<Args>(args)...) {} 236 SpecificThing() = default; 237 get()238 T& get() { return t_; } 239 240 private: 241 T t_; 242 }; 243 244 // Make a thing of type T, and return a reference to it. 245 template <typename T, typename... Args> Make(Args &&...args)246 T& Make(Args&&... args) { 247 things_.emplace_back(new SpecificThing<T>(std::forward<Args>(args)...)); 248 return static_cast<SpecificThing<T>*>(things_.back().get())->get(); 249 } 250 251 grpc_call* call_; 252 const int tag_; 253 std::vector<grpc_op> ops_; 254 std::vector<std::unique_ptr<Thing>> things_; 255 CqVerifier* const cq_verifier_; 256 }; 257 258 } // namespace grpc_core 259 260 #endif // GRPC_TEST_CORE_CALL_BATCH_BUILDER_H 261