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