xref: /aosp_15_r20/external/pigweed/pw_rpc/public/pw_rpc/payloads_view.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 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 
15 // The classes in this file facilitate testing RPC services. The main class is
16 // PayloadsView, which iterates over the payloads sent by an RPC service or
17 // client. This allows verifying that code that invokes RPCs or an RPC service
18 // implementation sends the expected requests or responses.
19 //
20 // This code is inteded for testing, not for deployment.
21 #pragma once
22 
23 #include <tuple>
24 
25 #include "pw_containers/filtered_view.h"
26 #include "pw_containers/vector.h"
27 #include "pw_containers/wrapped_iterator.h"
28 #include "pw_rpc/channel.h"
29 #include "pw_rpc/internal/method_info.h"
30 #include "pw_rpc/internal/packet.h"
31 #include "pw_rpc/method_type.h"
32 
33 namespace pw::rpc {
34 namespace internal::test {
35 
36 class FakeChannelOutput;
37 
38 // Finds packets of a specified type for a particular method.
39 class PacketFilter {
40  public:
41   // Use Channel::kUnassignedChannelId to ignore the channel.
PacketFilter(internal::pwpb::PacketType packet_type_1,internal::pwpb::PacketType packet_type_2,uint32_t channel_id,uint32_t service_id,uint32_t method_id)42   constexpr PacketFilter(internal::pwpb::PacketType packet_type_1,
43                          internal::pwpb::PacketType packet_type_2,
44                          uint32_t channel_id,
45                          uint32_t service_id,
46                          uint32_t method_id)
47       : packet_type_1_(packet_type_1),
48         packet_type_2_(packet_type_2),
49         channel_id_(channel_id),
50         service_id_(service_id),
51         method_id_(method_id) {}
52 
operator()53   constexpr bool operator()(const Packet& packet) const {
54     return (packet.type() == packet_type_1_ ||
55             packet.type() == packet_type_2_) &&
56            (channel_id_ == Channel::kUnassignedChannelId ||
57             packet.channel_id() == channel_id_) &&
58            packet.service_id() == service_id_ &&
59            packet.method_id() == method_id_;
60   }
61 
62  private:
63   // Support filtering on two packet types to handle reading both client and
64   // server streams for bidirectional streams.
65   internal::pwpb::PacketType packet_type_1_;
66   internal::pwpb::PacketType packet_type_2_;
67   uint32_t channel_id_;
68   uint32_t service_id_;
69   uint32_t method_id_;
70 };
71 
72 using PacketsView = containers::FilteredView<Vector<Packet>, PacketFilter>;
73 
74 }  // namespace internal::test
75 
76 // Returns the payloads for a particular RPC in a Vector of RPC packets.
77 //
78 // Adapts a FilteredView of packets to return payloads instead of packets.
79 class PayloadsView {
80  public:
81   class iterator : public containers::WrappedIterator<
82                        iterator,
83                        internal::test::PacketsView::iterator,
84                        ConstByteSpan> {
85    public:
86     constexpr iterator() = default;
87 
88     // Access the payload (rather than packet) with operator* and operator->.
89     const ConstByteSpan& operator*() const { return value().payload(); }
90     const ConstByteSpan* operator->() const { return &value().payload(); }
91 
92    private:
93     friend class PayloadsView;
94 
iterator(const internal::test::PacketsView::iterator & it)95     constexpr iterator(const internal::test::PacketsView::iterator& it)
96         : containers::WrappedIterator<iterator,
97                                       internal::test::PacketsView::iterator,
98                                       ConstByteSpan>(it) {}
99   };
100 
101   using const_iterator = iterator;
102 
103   const ConstByteSpan& operator[](size_t index) const {
104     auto it = begin();
105     std::advance(it, index);
106     return *it;
107   }
108 
109   // Number of payloads for the specified RPC.
size()110   size_t size() const { return view_.size(); }
111 
empty()112   bool empty() const { return begin() == end(); }
113 
114   // Returns the first/last payload for the RPC. size() must be > 0.
front()115   const ConstByteSpan& front() const { return *begin(); }
back()116   const ConstByteSpan& back() const { return *std::prev(end()); }
117 
begin()118   iterator begin() const { return iterator(view_.begin()); }
end()119   iterator end() const { return iterator(view_.end()); }
120 
121  private:
122   friend class internal::test::FakeChannelOutput;
123 
124   template <typename>
125   friend class NanopbPayloadsView;
126 
127   template <typename>
128   friend class PwpbPayloadsView;
129 
130   template <auto kMethod>
131   using MethodInfo = internal::MethodInfo<kMethod>;
132 
133   using PacketType = internal::pwpb::PacketType;
134 
135   template <auto kMethod>
For(const Vector<internal::Packet> & packets,uint32_t channel_id)136   static constexpr PayloadsView For(const Vector<internal::Packet>& packets,
137                                     uint32_t channel_id) {
138     constexpr auto kTypes = PacketTypesWithPayload(MethodInfo<kMethod>::kType);
139     return PayloadsView(packets,
140                         std::get<0>(kTypes),
141                         std::get<1>(kTypes),
142                         channel_id,
143                         MethodInfo<kMethod>::kServiceId,
144                         MethodInfo<kMethod>::kMethodId);
145   }
146 
PayloadsView(const Vector<internal::Packet> & packets,MethodType method_type,uint32_t channel_id,uint32_t service_id,uint32_t method_id)147   constexpr PayloadsView(const Vector<internal::Packet>& packets,
148                          MethodType method_type,
149                          uint32_t channel_id,
150                          uint32_t service_id,
151                          uint32_t method_id)
152       : PayloadsView(packets,
153                      std::get<0>(PacketTypesWithPayload(method_type)),
154                      std::get<1>(PacketTypesWithPayload(method_type)),
155                      channel_id,
156                      service_id,
157                      method_id) {}
158 
PayloadsView(const Vector<internal::Packet> & packets,PacketType packet_type_1,PacketType packet_type_2,uint32_t channel_id,uint32_t service_id,uint32_t method_id)159   constexpr PayloadsView(const Vector<internal::Packet>& packets,
160                          PacketType packet_type_1,
161                          PacketType packet_type_2,
162                          uint32_t channel_id,
163                          uint32_t service_id,
164                          uint32_t method_id)
165       : view_(packets,
166               internal::test::PacketFilter(packet_type_1,
167                                            packet_type_2,
168                                            channel_id,
169                                            service_id,
170                                            method_id)) {}
171 
PacketTypesWithPayload(MethodType method_type)172   static constexpr std::tuple<PacketType, PacketType> PacketTypesWithPayload(
173       MethodType method_type) {
174     switch (method_type) {
175       case MethodType::kUnary:
176         return {PacketType::REQUEST, PacketType::RESPONSE};
177       case MethodType::kServerStreaming:
178         return {PacketType::REQUEST, PacketType::SERVER_STREAM};
179       case MethodType::kClientStreaming:
180         return {PacketType::CLIENT_STREAM, PacketType::RESPONSE};
181       case MethodType::kBidirectionalStreaming:
182         return {PacketType::CLIENT_STREAM, PacketType::SERVER_STREAM};
183     }
184 
185 // Workaround for GCC 8 bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86678
186 #if defined(__GNUC__) && __GNUC__ < 9
187     return {};
188 #else
189     PW_ASSERT(false);
190 #endif  // defined(__GNUC__) && __GNUC__ < 9
191   }
192 
193   internal::test::PacketsView view_;
194 };
195 
196 // Class for iterating over RPC statuses associated witha particular RPC. This
197 // is used to iterate over the user RPC statuses and or protocol errors for a
198 // particular RPC.
199 class StatusView {
200  public:
201   class iterator : public containers::WrappedIterator<
202                        iterator,
203                        internal::test::PacketsView::iterator,
204                        Status> {
205    public:
206     constexpr iterator() = default;
207 
208     // Access the status (rather than packet) with operator* and operator->.
209     const Status& operator*() const { return value().status(); }
210     const Status* operator->() const { return &value().status(); }
211 
212    private:
213     friend class StatusView;
214 
iterator(const internal::test::PacketsView::iterator & it)215     constexpr iterator(const internal::test::PacketsView::iterator& it)
216         : containers::WrappedIterator<iterator,
217                                       internal::test::PacketsView::iterator,
218                                       Status>(it) {}
219   };
220 
221   using const_iterator = iterator;
222 
223   const Status& operator[](size_t index) const {
224     auto it = begin();
225     std::advance(it, index);
226     return *it;
227   }
228 
229   // Number of statuses in this view.
size()230   size_t size() const { return view_.size(); }
empty()231   bool empty() const { return begin() == end(); }
232 
233   // Returns the first/last payload for the RPC. size() must be > 0.
front()234   const Status& front() const { return *begin(); }
back()235   const Status& back() const { return *std::prev(end()); }
236 
begin()237   iterator begin() const { return iterator(view_.begin()); }
end()238   iterator end() const { return iterator(view_.end()); }
239 
240  private:
241   friend class internal::test::FakeChannelOutput;
242 
243   template <auto kMethod>
244   using MethodInfo = internal::MethodInfo<kMethod>;
245 
246   using PacketType = internal::pwpb::PacketType;
247 
StatusView(const Vector<internal::Packet> & packets,PacketType packet_type_1,PacketType packet_type_2,uint32_t channel_id,uint32_t service_id,uint32_t method_id)248   constexpr StatusView(const Vector<internal::Packet>& packets,
249                        PacketType packet_type_1,
250                        PacketType packet_type_2,
251                        uint32_t channel_id,
252                        uint32_t service_id,
253                        uint32_t method_id)
254       : view_(packets,
255               internal::test::PacketFilter(packet_type_1,
256                                            packet_type_2,
257                                            channel_id,
258                                            service_id,
259                                            method_id)) {}
260 
261   internal::test::PacketsView view_;
262 };
263 
264 }  // namespace pw::rpc
265