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 #include "src/core/client_channel/retry_interceptor.h"
16
17 #include <grpc/grpc.h>
18
19 #include <atomic>
20 #include <memory>
21 #include <queue>
22
23 #include "absl/strings/string_view.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "src/core/lib/resource_quota/resource_quota.h"
27 #include "test/core/call/yodel/yodel_test.h"
28
29 namespace grpc_core {
30
31 using EventEngine = grpc_event_engine::experimental::EventEngine;
32
33 namespace {
34 const absl::string_view kTestPath = "/test_method";
35 } // namespace
36
37 class RetryInterceptorTest : public YodelTest {
38 protected:
39 using YodelTest::YodelTest;
40
InitInterceptor(const ChannelArgs & args)41 void InitInterceptor(const ChannelArgs& args) {
42 CHECK(destination_under_test_ == nullptr);
43 InterceptionChainBuilder builder(args, nullptr, nullptr);
44 builder.Add<RetryInterceptor>();
45 destination_under_test_ = builder.Build(call_destination_).value();
46 }
47
MakeClientInitialMetadata()48 ClientMetadataHandle MakeClientInitialMetadata() {
49 auto client_initial_metadata =
50 Arena::MakePooledForOverwrite<ClientMetadata>();
51 client_initial_metadata->Set(HttpPathMetadata(),
52 Slice::FromCopiedString(kTestPath));
53 return client_initial_metadata;
54 }
55
MakeCall(ClientMetadataHandle client_initial_metadata)56 CallInitiatorAndHandler MakeCall(
57 ClientMetadataHandle client_initial_metadata) {
58 auto arena = call_arena_allocator_->MakeArena();
59 arena->SetContext<EventEngine>(event_engine().get());
60 return MakeCallPair(std::move(client_initial_metadata), std::move(arena));
61 }
62
TickUntilCallStarted()63 CallHandler TickUntilCallStarted() {
64 auto poll = [this]() -> Poll<CallHandler> {
65 auto handler = call_destination_->PopHandler();
66 if (handler.has_value()) return std::move(*handler);
67 return Pending();
68 };
69 return TickUntil(absl::FunctionRef<Poll<CallHandler>()>(poll));
70 }
71
destination_under_test()72 UnstartedCallDestination& destination_under_test() {
73 CHECK(destination_under_test_ != nullptr);
74 return *destination_under_test_;
75 }
76
77 private:
78 class TestCallDestination final : public UnstartedCallDestination {
79 public:
StartCall(UnstartedCallHandler unstarted_call_handler)80 void StartCall(UnstartedCallHandler unstarted_call_handler) override {
81 handlers_.push(unstarted_call_handler.StartCall());
82 }
83
PopHandler()84 absl::optional<CallHandler> PopHandler() {
85 if (handlers_.empty()) return absl::nullopt;
86 auto handler = std::move(handlers_.front());
87 handlers_.pop();
88 return handler;
89 }
90
Orphaned()91 void Orphaned() override {}
92
93 private:
94 std::queue<CallHandler> handlers_;
95 };
96
InitCoreConfiguration()97 void InitCoreConfiguration() override {}
98
Shutdown()99 void Shutdown() override {
100 call_destination_.reset();
101 destination_under_test_.reset();
102 call_arena_allocator_.reset();
103 }
104
105 RefCountedPtr<TestCallDestination> call_destination_ =
106 MakeRefCounted<TestCallDestination>();
107 RefCountedPtr<UnstartedCallDestination> destination_under_test_;
108 RefCountedPtr<CallArenaAllocator> call_arena_allocator_ =
109 MakeRefCounted<CallArenaAllocator>(
110 ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator(
111 "test"),
112 1024);
113 };
114
115 #define RETRY_INTERCEPTOR_TEST(name) YODEL_TEST(RetryInterceptorTest, name)
116
RETRY_INTERCEPTOR_TEST(NoOp)117 RETRY_INTERCEPTOR_TEST(NoOp) {
118 InitInterceptor(ChannelArgs());
119 destination_under_test();
120 }
121
RETRY_INTERCEPTOR_TEST(CreateCall)122 RETRY_INTERCEPTOR_TEST(CreateCall) {
123 InitInterceptor(ChannelArgs());
124 auto call = MakeCall(MakeClientInitialMetadata());
125 SpawnTestSeq(
126 call.initiator, "initiator",
127 [this, handler = std::move(call.handler)]() {
128 destination_under_test().StartCall(handler);
129 },
130 [call_initiator = call.initiator]() mutable { call_initiator.Cancel(); });
131 WaitForAllPendingWork();
132 }
133
RETRY_INTERCEPTOR_TEST(StartCall)134 RETRY_INTERCEPTOR_TEST(StartCall) {
135 InitInterceptor(ChannelArgs());
136 auto call = MakeCall(MakeClientInitialMetadata());
137 SpawnTestSeq(call.initiator, "initiator",
138 [this, handler = std::move(call.handler)]() {
139 destination_under_test().StartCall(handler);
140 });
141 auto handler = TickUntilCallStarted();
142 SpawnTestSeq(
143 call.initiator, "cancel",
144 [call_initiator = call.initiator]() mutable { call_initiator.Cancel(); });
145 WaitForAllPendingWork();
146 }
147
148 // TODO(roth, ctiller): more tests
149
150 } // namespace grpc_core
151