xref: /aosp_15_r20/external/pigweed/pw_rpc/raw/public/pw_rpc/raw/test_method_context.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <type_traits>
17 
18 #include "pw_assert/assert.h"
19 #include "pw_containers/vector.h"
20 #include "pw_preprocessor/arguments.h"
21 #include "pw_rpc/channel.h"
22 #include "pw_rpc/internal/hash.h"
23 #include "pw_rpc/internal/method_lookup.h"
24 #include "pw_rpc/internal/packet.h"
25 #include "pw_rpc/internal/test_method_context.h"
26 #include "pw_rpc/raw/fake_channel_output.h"
27 #include "pw_rpc/raw/internal/method.h"
28 
29 namespace pw::rpc {
30 
31 // Declares a context object that may be used to invoke an RPC. The context is
32 // declared with the name of the implemented service and the method to invoke.
33 // The RPC can then be invoked with the call method.
34 //
35 // For a unary RPC, context.call(request) returns the status, and the response
36 // struct can be accessed via context.response().
37 //
38 //   PW_RAW_TEST_METHOD_CONTEXT(my::CoolService, TheMethod) context;
39 //   EXPECT_EQ(OkStatus(), context.call(encoded_request).status());
40 //   EXPECT_EQ(0,
41 //             std::memcmp(encoded_response,
42 //                         context.response().data(),
43 //                         sizeof(encoded_response)));
44 //
45 // For a server streaming RPC, context.call(request) invokes the method. As in a
46 // normal RPC, the method completes when the ServerWriter's Finish method is
47 // called (or it goes out of scope).
48 //
49 //   PW_RAW_TEST_METHOD_CONTEXT(my::CoolService, TheStreamingMethod) context;
50 //   context.call(encoded_response);
51 //
52 //   EXPECT_TRUE(context.done());  // Check that the RPC completed
53 //   EXPECT_EQ(OkStatus(), context.status());  // Check the status
54 //
55 //   EXPECT_EQ(3u, context.responses().size());
56 //   ByteSpan& response = context.responses()[0];  // check individual responses
57 //
58 //   for (ByteSpan& response : context.responses()) {
59 //     // iterate over the responses
60 //   }
61 //
62 // PW_RAW_TEST_METHOD_CONTEXT forwards its constructor arguments to the
63 // underlying service. For example:
64 //
65 //   PW_RAW_TEST_METHOD_CONTEXT(MyService, Go) context(service, args);
66 //
67 // PW_RAW_TEST_METHOD_CONTEXT takes one optional arguments:
68 //
69 //   size_t kMaxPackets: maximum packets to store
70 //
71 // Example:
72 //
73 //   PW_RAW_TEST_METHOD_CONTEXT(MyService, BestMethod, 3, 256) context;
74 //   ASSERT_EQ(3u, context.responses().max_size());
75 //
76 #define PW_RAW_TEST_METHOD_CONTEXT(service, method, ...)             \
77   ::pw::rpc::RawTestMethodContext<service,                           \
78                                   &service::method,                  \
79                                   ::pw::rpc::internal::Hash(#method) \
80                                       PW_COMMA_ARGS(__VA_ARGS__)>
81 template <typename Service,
82           auto kMethod,
83           uint32_t kMethodId,
84           size_t kMaxPackets = 6>
85 class RawTestMethodContext;
86 
87 // Internal classes that implement RawTestMethodContext.
88 namespace internal::test::raw {
89 
90 inline constexpr size_t kPayloadsBufferSizeBytes = 256;
91 
92 // Collects everything needed to invoke a particular RPC.
93 template <typename Service,
94           auto kMethod,
95           uint32_t kMethodId,
96           size_t kMaxPackets>
97 class RawInvocationContext
98     : public InvocationContext<
99           RawFakeChannelOutput<kMaxPackets, kPayloadsBufferSizeBytes>,
100           Service,
101           kMethodId> {
102  public:
103   // Gives access to the RPC's most recent response.
response()104   const ConstByteSpan& response() const { return Base::responses().back(); }
105 
106  protected:
107   template <typename... Args>
RawInvocationContext(Args &&...args)108   RawInvocationContext(Args&&... args)
109       : Base(MethodLookup::GetRawMethod<Service, kMethodId>(),
110              MethodTraits<decltype(kMethod)>::kType,
111              std::forward<Args>(args)...) {}
112 
113  private:
114   using Base = InvocationContext<
115       RawFakeChannelOutput<kMaxPackets, kPayloadsBufferSizeBytes>,
116       Service,
117       kMethodId>;
118 };
119 
120 // Method invocation context for a unary RPC. Returns the status in call() and
121 // provides the response through the response() method.
122 template <typename Service, auto kMethod, uint32_t kMethodId>
123 class UnaryContext
124     : public RawInvocationContext<Service, kMethod, kMethodId, 1> {
125   using Base = RawInvocationContext<Service, kMethod, kMethodId, 1>;
126 
127  public:
128   template <typename... Args>
UnaryContext(Args &&...args)129   UnaryContext(Args&&... args) : Base(std::forward<Args>(args)...) {}
130 
131   // Invokes the RPC with the provided request. Returns RPC's StatusWithSize.
132   template <size_t kSynchronousResponseBufferSizeBytes = 64>
call(ConstByteSpan request)133   auto call(ConstByteSpan request) {
134     if constexpr (MethodTraits<decltype(kMethod)>::kSynchronous) {
135       Base::output().clear();
136 
137       auto responder = Base::template GetResponder<RawUnaryResponder>();
138       std::byte response[kSynchronousResponseBufferSizeBytes] = {};
139       auto sws = CallMethodImplFunction<kMethod>(
140           Base::service(), request, span(response));
141       PW_ASSERT(responder.Finish(span(response).first(sws.size()), sws.status())
142                     .ok());
143       return sws;
144     } else {
145       Base::template call<kMethod, RawUnaryResponder>(request);
146     }
147   }
148 };
149 
150 // Method invocation context for a server streaming RPC.
151 template <typename Service,
152           auto kMethod,
153           uint32_t kMethodId,
154           size_t kMaxPackets>
155 class ServerStreamingContext
156     : public RawInvocationContext<Service, kMethod, kMethodId, kMaxPackets> {
157   using Base = RawInvocationContext<Service, kMethod, kMethodId, kMaxPackets>;
158 
159  public:
160   template <typename... Args>
ServerStreamingContext(Args &&...args)161   ServerStreamingContext(Args&&... args) : Base(std::forward<Args>(args)...) {}
162 
163   // Invokes the RPC with the provided request.
call(ConstByteSpan request)164   void call(ConstByteSpan request) {
165     Base::template call<kMethod, RawServerWriter>(request);
166   }
167 
168   // Returns a server writer which writes responses into the context's buffer.
169   // This should not be called alongside call(); use one or the other.
writer()170   RawServerWriter writer() {
171     return Base::template GetResponder<RawServerWriter>();
172   }
173 };
174 
175 // Method invocation context for a client streaming RPC.
176 template <typename Service,
177           auto kMethod,
178           uint32_t kMethodId,
179           size_t kMaxPackets>
180 class ClientStreamingContext
181     : public RawInvocationContext<Service, kMethod, kMethodId, kMaxPackets> {
182   using Base = RawInvocationContext<Service, kMethod, kMethodId, kMaxPackets>;
183 
184  public:
185   template <typename... Args>
ClientStreamingContext(Args &&...args)186   ClientStreamingContext(Args&&... args) : Base(std::forward<Args>(args)...) {}
187 
188   // Invokes the RPC.
call()189   void call() { Base::template call<kMethod, RawServerReader>(); }
190 
191   // Returns a reader/writer which writes responses into the context's buffer.
192   // This should not be called alongside call(); use one or the other.
reader()193   RawServerReader reader() {
194     return Base::template GetResponder<RawServerReader>();
195   }
196 
197   // Allow sending client streaming packets.
198   using Base::SendClientStream;
199   using Base::SendClientStreamEnd;
200 };
201 
202 // Method invocation context for a bidirectional streaming RPC.
203 template <typename Service,
204           auto kMethod,
205           uint32_t kMethodId,
206           size_t kMaxPackets>
207 class BidirectionalStreamingContext
208     : public RawInvocationContext<Service, kMethod, kMethodId, kMaxPackets> {
209   using Base = RawInvocationContext<Service, kMethod, kMethodId, kMaxPackets>;
210 
211  public:
212   template <typename... Args>
BidirectionalStreamingContext(Args &&...args)213   BidirectionalStreamingContext(Args&&... args)
214       : Base(std::forward<Args>(args)...) {}
215 
216   // Invokes the RPC.
call()217   void call() { Base::template call<kMethod, RawServerReaderWriter>(); }
218 
219   // Returns a reader/writer which writes responses into the context's buffer.
220   // This should not be called alongside call(); use one or the other.
reader_writer()221   RawServerReaderWriter reader_writer() {
222     return Base::template GetResponder<RawServerReaderWriter>();
223   }
224 
225   // Allow sending client streaming packets.
226   using Base::SendClientStream;
227   using Base::SendClientStreamEnd;
228 };
229 
230 // Alias to select the type of the context object to use based on which type of
231 // RPC it is for.
232 template <typename Service,
233           auto kMethod,
234           uint32_t kMethodId,
235           size_t kMaxPackets>
236 using Context = std::tuple_element_t<
237     static_cast<size_t>(MethodTraits<decltype(kMethod)>::kType),
238     std::tuple<UnaryContext<Service, kMethod, kMethodId>,
239                ServerStreamingContext<Service, kMethod, kMethodId, kMaxPackets>,
240                ClientStreamingContext<Service, kMethod, kMethodId, kMaxPackets>,
241                BidirectionalStreamingContext<Service,
242                                              kMethod,
243                                              kMethodId,
244                                              kMaxPackets>>>;
245 
246 }  // namespace internal::test::raw
247 
248 template <typename Service,
249           auto kMethod,
250           uint32_t kMethodId,
251           size_t kMaxPackets>
252 class RawTestMethodContext
253     : public internal::test::raw::
254           Context<Service, kMethod, kMethodId, kMaxPackets> {
255  public:
256   // Forwards constructor arguments to the service class.
257   template <typename... ServiceArgs>
RawTestMethodContext(ServiceArgs &&...service_args)258   RawTestMethodContext(ServiceArgs&&... service_args)
259       : internal::test::raw::Context<Service, kMethod, kMethodId, kMaxPackets>(
260             std::forward<ServiceArgs>(service_args)...) {}
261 };
262 
263 }  // namespace pw::rpc
264