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