xref: /aosp_15_r20/external/pigweed/pw_rpc/pwpb/public/pw_rpc/pwpb/fake_channel_output.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 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 <cstddef>
17 #include <mutex>
18 
19 #include "pw_containers/wrapped_iterator.h"
20 #include "pw_rpc/internal/fake_channel_output.h"
21 #include "pw_rpc/internal/lock.h"
22 #include "pw_rpc/pwpb/internal/common.h"
23 #include "pw_rpc/pwpb/internal/method.h"
24 
25 namespace pw::rpc {
26 namespace internal {
27 
28 // Forward declare for a friend statement.
29 template <typename, size_t, size_t, size_t>
30 class ForwardingChannelOutput;
31 
32 }  // namespace internal
33 }  // namespace pw::rpc
34 
35 namespace pw::rpc {
36 namespace internal::test::pwpb {
37 
38 // Forward declare for a friend statement.
39 template <typename, auto, uint32_t, size_t, size_t>
40 class PwpbInvocationContext;
41 
42 }  // namespace internal::test::pwpb
43 
44 // PwpbPayloadsView supports iterating over payloads as decoded pw_protobuf
45 // request or response message structs.
46 template <typename Payload>
47 class PwpbPayloadsView {
48  public:
49   class iterator : public containers::WrappedIterator<iterator,
50                                                       PayloadsView::iterator,
51                                                       Payload> {
52    public:
53     // Access the payload (rather than packet) with operator*.
54     Payload operator*() const {
55       Payload payload{};
56       PW_ASSERT(serde_
57                     .Decode(containers::WrappedIterator<iterator,
58                                                         PayloadsView::iterator,
59                                                         Payload>::value(),
60                             payload)
61                     .ok());
62       return payload;
63     }
64 
65    private:
66     friend class PwpbPayloadsView;
67 
iterator(const PayloadsView::iterator & it,const PwpbSerde & serde)68     constexpr iterator(const PayloadsView::iterator& it, const PwpbSerde& serde)
69         : containers::
70               WrappedIterator<iterator, PayloadsView::iterator, Payload>(it),
71           serde_(serde) {}
72 
73     PwpbSerde serde_;
74   };
75 
76   Payload operator[](size_t index) const {
77     Payload payload{};
78     PW_ASSERT(serde_.Decode(view_[index], payload).ok());
79     return payload;
80   }
81 
size()82   size_t size() const { return view_.size(); }
empty()83   bool empty() const { return view_.empty(); }
84 
85   // Returns the first/last payload for the RPC. size() must be > 0.
front()86   Payload front() const { return *begin(); }
back()87   Payload back() const { return *std::prev(end()); }
88 
begin()89   iterator begin() const { return iterator(view_.begin(), serde_); }
end()90   iterator end() const { return iterator(view_.end(), serde_); }
91 
payloads()92   PayloadsView& payloads() { return view_; }
serde()93   PwpbSerde& serde() { return serde_; }
94 
95  private:
96   template <size_t, size_t>
97   friend class PwpbFakeChannelOutput;
98 
99   template <typename... Args>
PwpbPayloadsView(const PwpbSerde & serde,Args &&...args)100   PwpbPayloadsView(const PwpbSerde& serde, Args&&... args)
101       : view_(args...), serde_(serde) {}
102 
103   PayloadsView view_;
104   PwpbSerde serde_;
105 };
106 
107 // A ChannelOutput implementation that stores the outgoing payloads and status.
108 template <size_t kMaxPackets, size_t kPayloadsBufferSizeBytes = 128>
109 class PwpbFakeChannelOutput final
110     : public internal::test::FakeChannelOutputBuffer<kMaxPackets,
111                                                      kPayloadsBufferSizeBytes> {
112  private:
113   template <auto kMethod>
114   using Request = typename internal::MethodInfo<kMethod>::Request;
115   template <auto kMethod>
116   using Response = typename internal::MethodInfo<kMethod>::Response;
117 
118  public:
119   PwpbFakeChannelOutput() = default;
120 
121   // Iterates over request payloads from request or client stream packets.
122   //
123   // !!! WARNING !!!
124   //
125   // Access to the FakeChannelOutput through the PwpbPayloadsView is NOT
126   // synchronized! The PwpbPayloadsView is immediately invalidated if any
127   // thread accesses the FakeChannelOutput.
128   template <auto kMethod>
129   PwpbPayloadsView<Request<kMethod>> requests(
130       uint32_t channel_id = Channel::kUnassignedChannelId) const
131       PW_NO_LOCK_SAFETY_ANALYSIS {
132     constexpr internal::pwpb::PacketType packet_type =
133         HasClientStream(internal::MethodInfo<kMethod>::kType)
134             ? internal::pwpb::PacketType::CLIENT_STREAM
135             : internal::pwpb::PacketType::REQUEST;
136     return PwpbPayloadsView<Request<kMethod>>(
137         internal::MethodInfo<kMethod>::serde().request(),
138         internal::test::FakeChannelOutputBuffer<
139             kMaxPackets,
140             kPayloadsBufferSizeBytes>::packets(),
141         packet_type,
142         packet_type,
143         channel_id,
144         internal::MethodInfo<kMethod>::kServiceId,
145         internal::MethodInfo<kMethod>::kMethodId);
146   }
147 
148   // Iterates over response payloads from response or server stream packets.
149   //
150   // !!! WARNING !!!
151   //
152   // Access to the FakeChannelOutput through the PwpbPayloadsView is NOT
153   // synchronized! The PwpbPayloadsView is immediately invalidated if any
154   // thread accesses the FakeChannelOutput.
155   template <auto kMethod>
156   PwpbPayloadsView<Response<kMethod>> responses(
157       uint32_t channel_id = Channel::kUnassignedChannelId) const
158       PW_NO_LOCK_SAFETY_ANALYSIS {
159     constexpr internal::pwpb::PacketType packet_type =
160         HasServerStream(internal::MethodInfo<kMethod>::kType)
161             ? internal::pwpb::PacketType::SERVER_STREAM
162             : internal::pwpb::PacketType::RESPONSE;
163     return PwpbPayloadsView<Response<kMethod>>(
164         internal::MethodInfo<kMethod>::serde().response(),
165         internal::test::FakeChannelOutputBuffer<
166             kMaxPackets,
167             kPayloadsBufferSizeBytes>::packets(),
168         packet_type,
169         packet_type,
170         channel_id,
171         internal::MethodInfo<kMethod>::kServiceId,
172         internal::MethodInfo<kMethod>::kMethodId);
173   }
174 
175   template <auto kMethod>
last_response()176   Response<kMethod> last_response() const {
177     std::lock_guard lock(internal::test::FakeChannelOutput::mutex());
178     PwpbPayloadsView<Response<kMethod>> payloads = responses<kMethod>();
179     PW_ASSERT(!payloads.empty());
180     return payloads.back();
181   }
182 
183  private:
184   template <typename, auto, uint32_t, size_t, size_t>
185   friend class internal::test::pwpb::PwpbInvocationContext;
186   template <typename, size_t, size_t, size_t>
187   friend class internal::ForwardingChannelOutput;
188 
189   using internal::test::FakeChannelOutput::last_packet;
190 
191   // !!! WARNING !!!
192   //
193   // Access to the FakeChannelOutput through the PwpbPayloadsView is NOT
194   // synchronized! The PwpbPayloadsView is immediately invalidated if any
195   // thread accesses the FakeChannelOutput.
196   template <typename T>
payload_structs(const PwpbSerde & serde,MethodType type,uint32_t channel_id,uint32_t service_id,uint32_t method_id)197   PwpbPayloadsView<T> payload_structs(const PwpbSerde& serde,
198                                       MethodType type,
199                                       uint32_t channel_id,
200                                       uint32_t service_id,
201                                       uint32_t method_id) const
202       PW_NO_LOCK_SAFETY_ANALYSIS {
203     return PwpbPayloadsView<T>(serde,
204                                internal::test::FakeChannelOutputBuffer<
205                                    kMaxPackets,
206                                    kPayloadsBufferSizeBytes>::packets(),
207                                type,
208                                channel_id,
209                                service_id,
210                                method_id);
211   }
212 };
213 
214 }  // namespace pw::rpc
215