xref: /aosp_15_r20/external/pigweed/pw_rpc/raw/client_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/client.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <optional>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/internal/client_call.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/internal/packet.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/raw/client_reader_writer.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/raw/client_testing.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker namespace pw::rpc {
26*61c4878aSAndroid Build Coastguard Worker 
27*61c4878aSAndroid Build Coastguard Worker void UnaryMethod();
28*61c4878aSAndroid Build Coastguard Worker void BidirectionalStreamMethod();
29*61c4878aSAndroid Build Coastguard Worker 
30*61c4878aSAndroid Build Coastguard Worker template <>
31*61c4878aSAndroid Build Coastguard Worker struct internal::MethodInfo<UnaryMethod> {
32*61c4878aSAndroid Build Coastguard Worker   static constexpr uint32_t kServiceId = 100;
33*61c4878aSAndroid Build Coastguard Worker   static constexpr uint32_t kMethodId = 200;
34*61c4878aSAndroid Build Coastguard Worker   static constexpr MethodType kType = MethodType::kUnary;
35*61c4878aSAndroid Build Coastguard Worker };
36*61c4878aSAndroid Build Coastguard Worker 
37*61c4878aSAndroid Build Coastguard Worker template <>
38*61c4878aSAndroid Build Coastguard Worker struct internal::MethodInfo<BidirectionalStreamMethod> {
39*61c4878aSAndroid Build Coastguard Worker   static constexpr uint32_t kServiceId = 100;
40*61c4878aSAndroid Build Coastguard Worker   static constexpr uint32_t kMethodId = 300;
41*61c4878aSAndroid Build Coastguard Worker   static constexpr MethodType kType = MethodType::kBidirectionalStreaming;
42*61c4878aSAndroid Build Coastguard Worker };
43*61c4878aSAndroid Build Coastguard Worker 
44*61c4878aSAndroid Build Coastguard Worker namespace {
45*61c4878aSAndroid Build Coastguard Worker 
46*61c4878aSAndroid Build Coastguard Worker // Captures payload from on_next and statuses from on_error and on_completed.
47*61c4878aSAndroid Build Coastguard Worker // Payloads are assumed to be null-terminated strings.
48*61c4878aSAndroid Build Coastguard Worker template <typename CallType>
49*61c4878aSAndroid Build Coastguard Worker struct CallContext {
OnNextpw::rpc::__anon783869c00111::CallContext50*61c4878aSAndroid Build Coastguard Worker   auto OnNext() {
51*61c4878aSAndroid Build Coastguard Worker     return [this](ConstByteSpan string) {
52*61c4878aSAndroid Build Coastguard Worker       payload = reinterpret_cast<const char*>(string.data());
53*61c4878aSAndroid Build Coastguard Worker     };
54*61c4878aSAndroid Build Coastguard Worker   }
55*61c4878aSAndroid Build Coastguard Worker 
UnaryOnCompletedpw::rpc::__anon783869c00111::CallContext56*61c4878aSAndroid Build Coastguard Worker   auto UnaryOnCompleted() {
57*61c4878aSAndroid Build Coastguard Worker     return [this](ConstByteSpan string, Status status) {
58*61c4878aSAndroid Build Coastguard Worker       payload = reinterpret_cast<const char*>(string.data());
59*61c4878aSAndroid Build Coastguard Worker       completed = status;
60*61c4878aSAndroid Build Coastguard Worker     };
61*61c4878aSAndroid Build Coastguard Worker   }
62*61c4878aSAndroid Build Coastguard Worker 
StreamOnCompletedpw::rpc::__anon783869c00111::CallContext63*61c4878aSAndroid Build Coastguard Worker   auto StreamOnCompleted() {
64*61c4878aSAndroid Build Coastguard Worker     return [this](Status status) { completed = status; };
65*61c4878aSAndroid Build Coastguard Worker   }
66*61c4878aSAndroid Build Coastguard Worker 
OnErrorpw::rpc::__anon783869c00111::CallContext67*61c4878aSAndroid Build Coastguard Worker   auto OnError() {
68*61c4878aSAndroid Build Coastguard Worker     return [this](Status status) { error = status; };
69*61c4878aSAndroid Build Coastguard Worker   }
70*61c4878aSAndroid Build Coastguard Worker 
71*61c4878aSAndroid Build Coastguard Worker   CallType call;
72*61c4878aSAndroid Build Coastguard Worker 
73*61c4878aSAndroid Build Coastguard Worker   const char* payload;
74*61c4878aSAndroid Build Coastguard Worker   std::optional<Status> completed;
75*61c4878aSAndroid Build Coastguard Worker   std::optional<Status> error;
76*61c4878aSAndroid Build Coastguard Worker };
77*61c4878aSAndroid Build Coastguard Worker 
78*61c4878aSAndroid Build Coastguard Worker template <auto kMethod, typename Context>
StartUnaryCall(Context & context,std::optional<uint32_t> channel_id=std::nullopt)79*61c4878aSAndroid Build Coastguard Worker CallContext<RawUnaryReceiver> StartUnaryCall(
80*61c4878aSAndroid Build Coastguard Worker     Context& context, std::optional<uint32_t> channel_id = std::nullopt)
81*61c4878aSAndroid Build Coastguard Worker     PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
82*61c4878aSAndroid Build Coastguard Worker   CallContext<RawUnaryReceiver> call_context;
83*61c4878aSAndroid Build Coastguard Worker   call_context.call =
84*61c4878aSAndroid Build Coastguard Worker       internal::UnaryResponseClientCall::Start<RawUnaryReceiver>(
85*61c4878aSAndroid Build Coastguard Worker           context.client(),
86*61c4878aSAndroid Build Coastguard Worker           channel_id.value_or(context.channel().id()),
87*61c4878aSAndroid Build Coastguard Worker           internal::MethodInfo<kMethod>::kServiceId,
88*61c4878aSAndroid Build Coastguard Worker           internal::MethodInfo<kMethod>::kMethodId,
89*61c4878aSAndroid Build Coastguard Worker           call_context.UnaryOnCompleted(),
90*61c4878aSAndroid Build Coastguard Worker           call_context.OnError(),
91*61c4878aSAndroid Build Coastguard Worker           {});
92*61c4878aSAndroid Build Coastguard Worker   return call_context;
93*61c4878aSAndroid Build Coastguard Worker }
94*61c4878aSAndroid Build Coastguard Worker 
95*61c4878aSAndroid Build Coastguard Worker template <auto kMethod, typename Context>
StartStreamCall(Context & context,std::optional<uint32_t> channel_id=std::nullopt)96*61c4878aSAndroid Build Coastguard Worker CallContext<RawClientReaderWriter> StartStreamCall(
97*61c4878aSAndroid Build Coastguard Worker     Context& context, std::optional<uint32_t> channel_id = std::nullopt)
98*61c4878aSAndroid Build Coastguard Worker     PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
99*61c4878aSAndroid Build Coastguard Worker   CallContext<RawClientReaderWriter> call_context;
100*61c4878aSAndroid Build Coastguard Worker   call_context.call =
101*61c4878aSAndroid Build Coastguard Worker       internal::StreamResponseClientCall::Start<RawClientReaderWriter>(
102*61c4878aSAndroid Build Coastguard Worker           context.client(),
103*61c4878aSAndroid Build Coastguard Worker           channel_id.value_or(context.channel().id()),
104*61c4878aSAndroid Build Coastguard Worker           internal::MethodInfo<kMethod>::kServiceId,
105*61c4878aSAndroid Build Coastguard Worker           internal::MethodInfo<kMethod>::kMethodId,
106*61c4878aSAndroid Build Coastguard Worker           call_context.OnNext(),
107*61c4878aSAndroid Build Coastguard Worker           call_context.StreamOnCompleted(),
108*61c4878aSAndroid Build Coastguard Worker           call_context.OnError(),
109*61c4878aSAndroid Build Coastguard Worker           {});
110*61c4878aSAndroid Build Coastguard Worker   return call_context;
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_InvokesUnaryCallbacks)113*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_InvokesUnaryCallbacks) {
114*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
115*61c4878aSAndroid Build Coastguard Worker   CallContext call_context = StartUnaryCall<UnaryMethod>(context);
116*61c4878aSAndroid Build Coastguard Worker 
117*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(call_context.completed, OkStatus());
118*61c4878aSAndroid Build Coastguard Worker 
119*61c4878aSAndroid Build Coastguard Worker   context.server().SendResponse<UnaryMethod>(as_bytes(span("you nary?!?")),
120*61c4878aSAndroid Build Coastguard Worker                                              OkStatus());
121*61c4878aSAndroid Build Coastguard Worker 
122*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(call_context.payload, nullptr);
123*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(call_context.payload, "you nary?!?");
124*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(call_context.completed, OkStatus());
125*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(call_context.call.active());
126*61c4878aSAndroid Build Coastguard Worker }
127*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_NoCallbackSet)128*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_NoCallbackSet) {
129*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
130*61c4878aSAndroid Build Coastguard Worker   CallContext call_context = StartUnaryCall<UnaryMethod>(context);
131*61c4878aSAndroid Build Coastguard Worker   call_context.call.set_on_completed(nullptr);
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(call_context.completed, OkStatus());
134*61c4878aSAndroid Build Coastguard Worker 
135*61c4878aSAndroid Build Coastguard Worker   context.server().SendResponse<UnaryMethod>(as_bytes(span("you nary?!?")),
136*61c4878aSAndroid Build Coastguard Worker                                              OkStatus());
137*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(call_context.call.active());
138*61c4878aSAndroid Build Coastguard Worker }
139*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_InvokesStreamCallbacks)140*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_InvokesStreamCallbacks) {
141*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
142*61c4878aSAndroid Build Coastguard Worker   auto call = StartStreamCall<BidirectionalStreamMethod>(context);
143*61c4878aSAndroid Build Coastguard Worker 
144*61c4878aSAndroid Build Coastguard Worker   context.server().SendServerStream<BidirectionalStreamMethod>(
145*61c4878aSAndroid Build Coastguard Worker       as_bytes(span("<=>")));
146*61c4878aSAndroid Build Coastguard Worker 
147*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(call.payload, nullptr);
148*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(call.payload, "<=>");
149*61c4878aSAndroid Build Coastguard Worker 
150*61c4878aSAndroid Build Coastguard Worker   context.server().SendResponse<BidirectionalStreamMethod>(Status::NotFound());
151*61c4878aSAndroid Build Coastguard Worker 
152*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(call.completed, Status::NotFound());
153*61c4878aSAndroid Build Coastguard Worker }
154*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_UnassignedChannelId_ReturnsDataLoss)155*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_UnassignedChannelId_ReturnsDataLoss) {
156*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
157*61c4878aSAndroid Build Coastguard Worker   auto call_cts = StartStreamCall<BidirectionalStreamMethod>(context);
158*61c4878aSAndroid Build Coastguard Worker 
159*61c4878aSAndroid Build Coastguard Worker   std::byte encoded[64];
160*61c4878aSAndroid Build Coastguard Worker   uint32_t arbitrary_call_id = 24602;
161*61c4878aSAndroid Build Coastguard Worker   Result<span<const std::byte>> result =
162*61c4878aSAndroid Build Coastguard Worker       internal::Packet(
163*61c4878aSAndroid Build Coastguard Worker           internal::pwpb::PacketType::kResponse,
164*61c4878aSAndroid Build Coastguard Worker           Channel::kUnassignedChannelId,
165*61c4878aSAndroid Build Coastguard Worker           internal::MethodInfo<BidirectionalStreamMethod>::kServiceId,
166*61c4878aSAndroid Build Coastguard Worker           internal::MethodInfo<BidirectionalStreamMethod>::kMethodId,
167*61c4878aSAndroid Build Coastguard Worker           arbitrary_call_id)
168*61c4878aSAndroid Build Coastguard Worker           .Encode(encoded);
169*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(result.ok());
170*61c4878aSAndroid Build Coastguard Worker 
171*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.client().ProcessPacket(*result), Status::DataLoss());
172*61c4878aSAndroid Build Coastguard Worker }
173*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_InvokesErrorCallback)174*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_InvokesErrorCallback) {
175*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
176*61c4878aSAndroid Build Coastguard Worker   auto call = StartStreamCall<BidirectionalStreamMethod>(context);
177*61c4878aSAndroid Build Coastguard Worker 
178*61c4878aSAndroid Build Coastguard Worker   context.server().SendServerError<BidirectionalStreamMethod>(
179*61c4878aSAndroid Build Coastguard Worker       Status::Aborted());
180*61c4878aSAndroid Build Coastguard Worker 
181*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(call.error, Status::Aborted());
182*61c4878aSAndroid Build Coastguard Worker }
183*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_SendsClientErrorOnUnregisteredServerStream)184*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_SendsClientErrorOnUnregisteredServerStream) {
185*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
186*61c4878aSAndroid Build Coastguard Worker   context.server().SendServerStream<BidirectionalStreamMethod>({});
187*61c4878aSAndroid Build Coastguard Worker 
188*61c4878aSAndroid Build Coastguard Worker   StatusView errors = context.output().errors<BidirectionalStreamMethod>();
189*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(errors.size(), 1u);
190*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(errors.front(), Status::FailedPrecondition());
191*61c4878aSAndroid Build Coastguard Worker }
192*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_NonServerStreamOnUnregisteredCall_SendsNothing)193*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_NonServerStreamOnUnregisteredCall_SendsNothing) {
194*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
195*61c4878aSAndroid Build Coastguard Worker   context.server().SendServerError<UnaryMethod>(Status::NotFound());
196*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.output().total_packets(), 0u);
197*61c4878aSAndroid Build Coastguard Worker 
198*61c4878aSAndroid Build Coastguard Worker   context.server().SendResponse<UnaryMethod>({}, Status::Unavailable());
199*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.output().total_packets(), 0u);
200*61c4878aSAndroid Build Coastguard Worker }
201*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_ReturnsDataLossOnBadPacket)202*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_ReturnsDataLossOnBadPacket) {
203*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
204*61c4878aSAndroid Build Coastguard Worker 
205*61c4878aSAndroid Build Coastguard Worker   constexpr std::byte bad_packet[]{
206*61c4878aSAndroid Build Coastguard Worker       std::byte{0xab}, std::byte{0xcd}, std::byte{0xef}};
207*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.client().ProcessPacket(bad_packet), Status::DataLoss());
208*61c4878aSAndroid Build Coastguard Worker }
209*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,ProcessPacket_ReturnsInvalidArgumentOnServerPacket)210*61c4878aSAndroid Build Coastguard Worker TEST(Client, ProcessPacket_ReturnsInvalidArgumentOnServerPacket) {
211*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext context;
212*61c4878aSAndroid Build Coastguard Worker 
213*61c4878aSAndroid Build Coastguard Worker   std::byte encoded[64];
214*61c4878aSAndroid Build Coastguard Worker   Result<span<const std::byte>> result =
215*61c4878aSAndroid Build Coastguard Worker       internal::Packet(internal::pwpb::PacketType::REQUEST, 1, 2, 3, 4)
216*61c4878aSAndroid Build Coastguard Worker           .Encode(encoded);
217*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(result.ok());
218*61c4878aSAndroid Build Coastguard Worker 
219*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.client().ProcessPacket(*result), Status::InvalidArgument());
220*61c4878aSAndroid Build Coastguard Worker }
221*61c4878aSAndroid Build Coastguard Worker 
GetChannel(internal::Endpoint & endpoint,uint32_t id)222*61c4878aSAndroid Build Coastguard Worker const internal::ChannelBase* GetChannel(internal::Endpoint& endpoint,
223*61c4878aSAndroid Build Coastguard Worker                                         uint32_t id) {
224*61c4878aSAndroid Build Coastguard Worker   internal::RpcLockGuard lock;
225*61c4878aSAndroid Build Coastguard Worker   return endpoint.GetInternalChannel(id);
226*61c4878aSAndroid Build Coastguard Worker }
227*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,CloseChannel_NoCalls)228*61c4878aSAndroid Build Coastguard Worker TEST(Client, CloseChannel_NoCalls) {
229*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext ctx;
230*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(nullptr, GetChannel(ctx.client(), ctx.kDefaultChannelId));
231*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), ctx.client().CloseChannel(ctx.kDefaultChannelId));
232*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(nullptr, GetChannel(ctx.client(), ctx.kDefaultChannelId));
233*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(ctx.output().total_packets(), 0u);
234*61c4878aSAndroid Build Coastguard Worker }
235*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,CloseChannel_UnknownChannel)236*61c4878aSAndroid Build Coastguard Worker TEST(Client, CloseChannel_UnknownChannel) {
237*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext ctx;
238*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(nullptr, GetChannel(ctx.client(), 13579));
239*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::NotFound(), ctx.client().CloseChannel(13579));
240*61c4878aSAndroid Build Coastguard Worker }
241*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,CloseChannel_CallsErrorCallback)242*61c4878aSAndroid Build Coastguard Worker TEST(Client, CloseChannel_CallsErrorCallback) {
243*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext ctx;
244*61c4878aSAndroid Build Coastguard Worker   CallContext call_ctx = StartUnaryCall<UnaryMethod>(ctx);
245*61c4878aSAndroid Build Coastguard Worker 
246*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(call_ctx.completed, OkStatus());
247*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(1u,
248*61c4878aSAndroid Build Coastguard Worker             static_cast<internal::Endpoint&>(ctx.client()).active_call_count());
249*61c4878aSAndroid Build Coastguard Worker 
250*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), ctx.client().CloseChannel(1));
251*61c4878aSAndroid Build Coastguard Worker 
252*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(0u,
253*61c4878aSAndroid Build Coastguard Worker             static_cast<internal::Endpoint&>(ctx.client()).active_call_count());
254*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(call_ctx.error, Status::Aborted());  // set by the on_error callback
255*61c4878aSAndroid Build Coastguard Worker }
256*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,CloseChannel_ErrorCallbackReusesCallObjectForCallOnClosedChannel)257*61c4878aSAndroid Build Coastguard Worker TEST(Client, CloseChannel_ErrorCallbackReusesCallObjectForCallOnClosedChannel) {
258*61c4878aSAndroid Build Coastguard Worker   struct {
259*61c4878aSAndroid Build Coastguard Worker     RawClientTestContext<> ctx;
260*61c4878aSAndroid Build Coastguard Worker     CallContext<RawUnaryReceiver> call_ctx;
261*61c4878aSAndroid Build Coastguard Worker   } context;
262*61c4878aSAndroid Build Coastguard Worker 
263*61c4878aSAndroid Build Coastguard Worker   context.call_ctx = StartUnaryCall<UnaryMethod>(context.ctx);
264*61c4878aSAndroid Build Coastguard Worker   context.call_ctx.call.set_on_error([&context](Status error) {
265*61c4878aSAndroid Build Coastguard Worker     context.call_ctx = StartUnaryCall<UnaryMethod>(context.ctx, 1);
266*61c4878aSAndroid Build Coastguard Worker     context.call_ctx.error = error;
267*61c4878aSAndroid Build Coastguard Worker   });
268*61c4878aSAndroid Build Coastguard Worker 
269*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), context.ctx.client().CloseChannel(1));
270*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.call_ctx.error, Status::Aborted());
271*61c4878aSAndroid Build Coastguard Worker 
272*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(context.call_ctx.call.active());
273*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(0u,
274*61c4878aSAndroid Build Coastguard Worker             static_cast<internal::Endpoint&>(context.ctx.client())
275*61c4878aSAndroid Build Coastguard Worker                 .active_call_count());
276*61c4878aSAndroid Build Coastguard Worker }
277*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,CloseChannel_ErrorCallbackReusesCallObjectForActiveCall)278*61c4878aSAndroid Build Coastguard Worker TEST(Client, CloseChannel_ErrorCallbackReusesCallObjectForActiveCall) {
279*61c4878aSAndroid Build Coastguard Worker   class ContextWithTwoChannels {
280*61c4878aSAndroid Build Coastguard Worker    public:
281*61c4878aSAndroid Build Coastguard Worker     ContextWithTwoChannels()
282*61c4878aSAndroid Build Coastguard Worker         : channels_{Channel::Create<1>(&channel_output_),
283*61c4878aSAndroid Build Coastguard Worker                     Channel::Create<2>(&channel_output_)},
284*61c4878aSAndroid Build Coastguard Worker           client_(channels_),
285*61c4878aSAndroid Build Coastguard Worker           packet_buffer{},
286*61c4878aSAndroid Build Coastguard Worker           fake_server_(channel_output_, client_, 1, packet_buffer) {}
287*61c4878aSAndroid Build Coastguard Worker 
288*61c4878aSAndroid Build Coastguard Worker     Channel& channel() { return channels_[0]; }
289*61c4878aSAndroid Build Coastguard Worker     Client& client() { return client_; }
290*61c4878aSAndroid Build Coastguard Worker     CallContext<RawUnaryReceiver>& call_ctx() { return call_context_; }
291*61c4878aSAndroid Build Coastguard Worker     RawUnaryReceiver& call() { return call_context_.call; }
292*61c4878aSAndroid Build Coastguard Worker 
293*61c4878aSAndroid Build Coastguard Worker     void StartCall(uint32_t channel_id) {
294*61c4878aSAndroid Build Coastguard Worker       call_context_ = StartUnaryCall<UnaryMethod>(*this, channel_id);
295*61c4878aSAndroid Build Coastguard Worker     }
296*61c4878aSAndroid Build Coastguard Worker 
297*61c4878aSAndroid Build Coastguard Worker    private:
298*61c4878aSAndroid Build Coastguard Worker     RawFakeChannelOutput<10, 256> channel_output_;
299*61c4878aSAndroid Build Coastguard Worker     Channel channels_[2];
300*61c4878aSAndroid Build Coastguard Worker     Client client_;
301*61c4878aSAndroid Build Coastguard Worker     std::byte packet_buffer[64];
302*61c4878aSAndroid Build Coastguard Worker     FakeServer fake_server_;
303*61c4878aSAndroid Build Coastguard Worker 
304*61c4878aSAndroid Build Coastguard Worker     CallContext<RawUnaryReceiver> call_context_;
305*61c4878aSAndroid Build Coastguard Worker   } context;
306*61c4878aSAndroid Build Coastguard Worker 
307*61c4878aSAndroid Build Coastguard Worker   context.StartCall(1);
308*61c4878aSAndroid Build Coastguard Worker   context.call().set_on_error([&context](Status error) {
309*61c4878aSAndroid Build Coastguard Worker     context.StartCall(2);
310*61c4878aSAndroid Build Coastguard Worker     context.call_ctx().error = error;
311*61c4878aSAndroid Build Coastguard Worker   });
312*61c4878aSAndroid Build Coastguard Worker 
313*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), context.client().CloseChannel(1));
314*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(context.call_ctx().error, Status::Aborted());
315*61c4878aSAndroid Build Coastguard Worker 
316*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(context.call().active());
317*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(
318*61c4878aSAndroid Build Coastguard Worker       1u,
319*61c4878aSAndroid Build Coastguard Worker       static_cast<internal::Endpoint&>(context.client()).active_call_count());
320*61c4878aSAndroid Build Coastguard Worker }
321*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,OpenChannel_UnusedSlot)322*61c4878aSAndroid Build Coastguard Worker TEST(Client, OpenChannel_UnusedSlot) {
323*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext ctx;
324*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), ctx.client().CloseChannel(1));
325*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(nullptr, GetChannel(ctx.client(), 9));
326*61c4878aSAndroid Build Coastguard Worker 
327*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), ctx.client().OpenChannel(9, ctx.output()));
328*61c4878aSAndroid Build Coastguard Worker 
329*61c4878aSAndroid Build Coastguard Worker   EXPECT_NE(nullptr, GetChannel(ctx.client(), 9));
330*61c4878aSAndroid Build Coastguard Worker }
331*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,OpenChannel_AlreadyExists)332*61c4878aSAndroid Build Coastguard Worker TEST(Client, OpenChannel_AlreadyExists) {
333*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext ctx;
334*61c4878aSAndroid Build Coastguard Worker   ASSERT_NE(nullptr, GetChannel(ctx.client(), 1));
335*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::AlreadyExists(), ctx.client().OpenChannel(1, ctx.output()));
336*61c4878aSAndroid Build Coastguard Worker }
337*61c4878aSAndroid Build Coastguard Worker 
TEST(Client,OpenChannel_AdditionalSlot)338*61c4878aSAndroid Build Coastguard Worker TEST(Client, OpenChannel_AdditionalSlot) {
339*61c4878aSAndroid Build Coastguard Worker   RawClientTestContext ctx;
340*61c4878aSAndroid Build Coastguard Worker 
341*61c4878aSAndroid Build Coastguard Worker   constexpr Status kExpected =
342*61c4878aSAndroid Build Coastguard Worker       PW_RPC_DYNAMIC_ALLOCATION == 0 ? Status::ResourceExhausted() : OkStatus();
343*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(kExpected, ctx.client().OpenChannel(19823, ctx.output()));
344*61c4878aSAndroid Build Coastguard Worker }
345*61c4878aSAndroid Build Coastguard Worker 
346*61c4878aSAndroid Build Coastguard Worker }  // namespace
347*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::rpc
348