• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <iterator>
18 #include <limits>
19 #include <mutex>
20 
21 #include "pw_bytes/span.h"
22 #include "pw_containers/vector.h"
23 #include "pw_function/function.h"
24 #include "pw_rpc/channel.h"
25 #include "pw_rpc/internal/lock.h"
26 #include "pw_rpc/internal/method_info.h"
27 #include "pw_rpc/internal/packet.h"
28 #include "pw_rpc/method_type.h"
29 #include "pw_rpc/payloads_view.h"
30 #include "pw_sync/lock_annotations.h"
31 
32 namespace pw::rpc {
33 namespace internal {
34 
35 // Forward declare for a friend statement.
36 template <class, size_t, size_t, size_t>
37 class ForwardingChannelOutput;
38 
39 }  // namespace internal
40 }  // namespace pw::rpc
41 
42 namespace pw::rpc {
43 
44 class FakeServer;
45 
46 namespace internal::test {
47 
48 // A ChannelOutput implementation that stores outgoing packets.
49 class FakeChannelOutput : public ChannelOutput {
50  public:
51   FakeChannelOutput(const FakeChannelOutput&) = delete;
52   FakeChannelOutput(FakeChannelOutput&&) = delete;
53 
54   FakeChannelOutput& operator=(const FakeChannelOutput&) = delete;
55   FakeChannelOutput& operator=(FakeChannelOutput&&) = delete;
56 
last_status()57   Status last_status() const PW_LOCKS_EXCLUDED(mutex_) {
58     std::lock_guard lock(mutex_);
59     PW_ASSERT(total_response_packets_ > 0);
60     return packets_.back().status();
61   }
62 
63   // Returns a view of the payloads seen for this RPC.
64   //
65   // !!! WARNING !!!
66   //
67   // Access to the FakeChannelOutput through the PayloadsView is NOT
68   // synchronized! The PayloadsView is immediately invalidated if any thread
69   // accesses the FakeChannelOutput.
70   template <auto kMethod>
71   PayloadsView payloads(uint32_t channel_id = Channel::kUnassignedChannelId)
PW_LOCKS_EXCLUDED(mutex_)72       const PW_LOCKS_EXCLUDED(mutex_) {
73     std::lock_guard lock(mutex_);
74     return PayloadsView(packets_,
75                         MethodInfo<kMethod>::kType,
76                         channel_id,
77                         MethodInfo<kMethod>::kServiceId,
78                         MethodInfo<kMethod>::kMethodId);
79   }
80 
payloads(MethodType type,uint32_t channel_id,uint32_t service_id,uint32_t method_id)81   PayloadsView payloads(MethodType type,
82                         uint32_t channel_id,
83                         uint32_t service_id,
84                         uint32_t method_id) const PW_LOCKS_EXCLUDED(mutex_) {
85     std::lock_guard lock(mutex_);
86     return PayloadsView(packets_, type, channel_id, service_id, method_id);
87   }
88 
89   // Returns a number of the payloads seen for this RPC.
90   template <auto kMethod>
91   size_t total_payloads(uint32_t channel_id = Channel::kUnassignedChannelId)
PW_LOCKS_EXCLUDED(mutex_)92       const PW_LOCKS_EXCLUDED(mutex_) {
93     std::lock_guard lock(mutex_);
94     return PayloadsView(packets_,
95                         MethodInfo<kMethod>::kType,
96                         channel_id,
97                         MethodInfo<kMethod>::kServiceId,
98                         MethodInfo<kMethod>::kMethodId)
99         .size();
100   }
101 
102   // Returns a number of the payloads seen for this RPC.
total_payloads(MethodType type,uint32_t channel_id,uint32_t service_id,uint32_t method_id)103   size_t total_payloads(MethodType type,
104                         uint32_t channel_id,
105                         uint32_t service_id,
106                         uint32_t method_id) const PW_LOCKS_EXCLUDED(mutex_) {
107     std::lock_guard lock(mutex_);
108     return PayloadsView(packets_, type, channel_id, service_id, method_id)
109         .size();
110   }
111 
112   // Returns a view of the final statuses seen for this RPC. Only relevant for
113   // checking packets sent by a server.
114   //
115   // !!! WARNING !!!
116   //
117   // Access to the FakeChannelOutput through the StatusView is NOT
118   // synchronized! The StatusView is immediately invalidated if any thread
119   // accesses the FakeChannelOutput.
120   template <auto kMethod>
121   StatusView completions(uint32_t channel_id = Channel::kUnassignedChannelId)
PW_LOCKS_EXCLUDED(mutex_)122       const PW_LOCKS_EXCLUDED(mutex_) {
123     std::lock_guard lock(mutex_);
124     return StatusView(packets_,
125                       internal::pwpb::PacketType::RESPONSE,
126                       internal::pwpb::PacketType::RESPONSE,
127                       channel_id,
128                       MethodInfo<kMethod>::kServiceId,
129                       MethodInfo<kMethod>::kMethodId);
130   }
131 
132   // Returns a view of the pw_rpc server or client errors seen for this RPC.
133   //
134   // !!! WARNING !!!
135   //
136   // Access to the FakeChannelOutput through the StatusView is NOT
137   // synchronized! The StatusView is immediately invalidated if any thread
138   // accesses the FakeChannelOutput.
139   template <auto kMethod>
140   StatusView errors(uint32_t channel_id = Channel::kUnassignedChannelId) const
PW_LOCKS_EXCLUDED(mutex_)141       PW_LOCKS_EXCLUDED(mutex_) {
142     std::lock_guard lock(mutex_);
143     return StatusView(packets_,
144                       internal::pwpb::PacketType::CLIENT_ERROR,
145                       internal::pwpb::PacketType::SERVER_ERROR,
146                       channel_id,
147                       MethodInfo<kMethod>::kServiceId,
148                       MethodInfo<kMethod>::kMethodId);
149   }
150 
151   // Returns a view of the client stream end packets seen for this RPC. Only
152   // relevant for checking packets sent by a client.
153   template <auto kMethod>
154   size_t client_stream_end_packets(
155       uint32_t channel_id = Channel::kUnassignedChannelId) const
PW_LOCKS_EXCLUDED(mutex_)156       PW_LOCKS_EXCLUDED(mutex_) {
157     std::lock_guard lock(mutex_);
158     return internal::test::PacketsView(
159                packets_,
160                internal::test::PacketFilter(
161                    internal::pwpb::PacketType::CLIENT_STREAM_END,
162                    internal::pwpb::PacketType::CLIENT_STREAM_END,
163                    channel_id,
164                    MethodInfo<kMethod>::kServiceId,
165                    MethodInfo<kMethod>::kMethodId))
166         .size();
167   }
168 
169   // The maximum number of packets this FakeChannelOutput can store. Attempting
170   // to store more packets than this is an error.
max_packets()171   size_t max_packets() const PW_LOCKS_EXCLUDED(mutex_) {
172     std::lock_guard lock(mutex_);
173     return packets_.max_size();
174   }
175 
176   // The total number of packets that have been sent.
total_packets()177   size_t total_packets() const PW_LOCKS_EXCLUDED(mutex_) {
178     std::lock_guard lock(mutex_);
179     return packets_.size();
180   }
181 
182   // Set to true if a RESPONSE packet is seen.
done()183   bool done() const PW_LOCKS_EXCLUDED(mutex_) {
184     std::lock_guard lock(mutex_);
185     return total_response_packets_ > 0;
186   }
187 
188   // Clears and resets the FakeChannelOutput.
189   void clear() PW_LOCKS_EXCLUDED(mutex_);
190 
191   // Returns `status` for all future Send calls. Enables packet processing if
192   // `status` is OK.
set_send_status(Status status)193   void set_send_status(Status status) PW_LOCKS_EXCLUDED(mutex_) {
194     std::lock_guard lock(mutex_);
195     send_status_ = status;
196     return_after_packet_count_ = status.ok() ? -1 : 0;
197   }
198 
199   // Returns `status` once after the specified positive number of packets.
set_send_status(Status status,int return_after_packet_count)200   void set_send_status(Status status, int return_after_packet_count)
201       PW_LOCKS_EXCLUDED(mutex_) {
202     std::lock_guard lock(mutex_);
203     PW_ASSERT(!status.ok());
204     PW_ASSERT(return_after_packet_count > 0);
205     send_status_ = status;
206     return_after_packet_count_ = return_after_packet_count;
207   }
208 
209   // Logs which packets have been sent for debugging purposes.
210   void LogPackets() const PW_LOCKS_EXCLUDED(mutex_);
211 
212   // Processes buffer according to packet type and `return_after_packet_count_`
213   // value as follows:
214   // When positive, returns `send_status_` once,
215   // When equals 0, returns `send_status_` in all future calls,
216   // When negative, ignores `send_status_` processes buffer.
Send(ConstByteSpan buffer)217   Status Send(ConstByteSpan buffer) final PW_LOCKS_EXCLUDED(mutex_) {
218     std::lock_guard lock(mutex_);
219     const Status status = HandlePacket(buffer);
220     if (on_send_ != nullptr) {
221       on_send_(buffer, status);
222     }
223     return status;
224   }
225 
226   // Gives access to the last received internal::Packet. This is hidden by the
227   // raw/Nanopb implementations, since it gives access to an internal class.
last_packet()228   const Packet& last_packet() const PW_LOCKS_EXCLUDED(mutex_) {
229     std::lock_guard lock(mutex_);
230     PW_ASSERT(!packets_.empty());
231     return packets_.back();
232   }
233 
234   // The on_send callback is called every time Send() is called. It is passed
235   // the contents of the packet and the status to be returned from Send().
236   //
237   // DANGER: Do NOT call any FakeChannelOutput functions or functions that call
238   // FakeChannelOutput functions. That will result in infinite recursion or
239   // deadlocks.
set_on_send(Function<void (ConstByteSpan,Status)> && on_send)240   void set_on_send(Function<void(ConstByteSpan, Status)>&& on_send)
241       PW_LOCKS_EXCLUDED(mutex_) {
242     std::lock_guard lock(mutex_);
243     on_send_ = std::move(on_send);
244   }
245 
246  protected:
FakeChannelOutput(Vector<Packet> & packets,Vector<std::byte> & payloads)247   FakeChannelOutput(Vector<Packet>& packets, Vector<std::byte>& payloads)
248       : ChannelOutput("pw::rpc::internal::test::FakeChannelOutput"),
249         packets_(packets),
250         payloads_(payloads) {}
251 
packets()252   const Vector<Packet>& packets() const PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
253     return packets_;
254   }
255 
mutex()256   RpcLock& mutex() const { return mutex_; }
257 
258  private:
259   friend class rpc::FakeServer;
260   template <class, size_t, size_t, size_t>
261   friend class internal::ForwardingChannelOutput;
262 
263   Status HandlePacket(ConstByteSpan buffer) PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
264   void CopyPayloadToBuffer(Packet& packet) PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
265 
266   int return_after_packet_count_ PW_GUARDED_BY(mutex_) = -1;
267   unsigned total_response_packets_ PW_GUARDED_BY(mutex_) = 0;
268 
269   Vector<Packet>& packets_ PW_GUARDED_BY(mutex_);
270   Vector<std::byte>& payloads_ PW_GUARDED_BY(mutex_);
271   Status send_status_ PW_GUARDED_BY(mutex_) = OkStatus();
272   Function<void(ConstByteSpan, Status)> on_send_ PW_GUARDED_BY(mutex_);
273 
274   mutable RpcLock mutex_;
275 };
276 
277 // Adds the packet output buffer to a FakeChannelOutput.
278 template <size_t kMaxPackets, size_t kPayloadsBufferSizeBytes>
279 class FakeChannelOutputBuffer : public FakeChannelOutput {
280  protected:
FakeChannelOutputBuffer()281   FakeChannelOutputBuffer()
282       : FakeChannelOutput(packets_array_, payloads_array_), payloads_array_{} {}
283 
284   Vector<std::byte, kPayloadsBufferSizeBytes> payloads_array_;
285   Vector<Packet, kMaxPackets> packets_array_;
286 };
287 
288 }  // namespace internal::test
289 }  // namespace pw::rpc
290