• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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