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