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