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 92 private: 93 template <size_t, size_t> 94 friend class PwpbFakeChannelOutput; 95 96 template <typename... Args> PwpbPayloadsView(const PwpbSerde & serde,Args &&...args)97 PwpbPayloadsView(const PwpbSerde& serde, Args&&... args) 98 : view_(args...), serde_(serde) {} 99 100 PayloadsView view_; 101 PwpbSerde serde_; 102 }; 103 104 // A ChannelOutput implementation that stores the outgoing payloads and status. 105 template <size_t kMaxPackets, size_t kPayloadsBufferSizeBytes = 128> 106 class PwpbFakeChannelOutput final 107 : public internal::test::FakeChannelOutputBuffer<kMaxPackets, 108 kPayloadsBufferSizeBytes> { 109 private: 110 template <auto kMethod> 111 using Request = typename internal::MethodInfo<kMethod>::Request; 112 template <auto kMethod> 113 using Response = typename internal::MethodInfo<kMethod>::Response; 114 115 public: 116 PwpbFakeChannelOutput() = default; 117 118 // Iterates over request payloads from request or client stream packets. 119 // 120 // !!! WARNING !!! 121 // 122 // Access to the FakeChannelOutput through the PwpbPayloadsView is NOT 123 // synchronized! The PwpbPayloadsView is immediately invalidated if any 124 // thread accesses the FakeChannelOutput. 125 template <auto kMethod> 126 PwpbPayloadsView<Request<kMethod>> requests( 127 uint32_t channel_id = Channel::kUnassignedChannelId) const 128 PW_NO_LOCK_SAFETY_ANALYSIS { 129 constexpr internal::pwpb::PacketType packet_type = 130 HasClientStream(internal::MethodInfo<kMethod>::kType) 131 ? internal::pwpb::PacketType::CLIENT_STREAM 132 : internal::pwpb::PacketType::REQUEST; 133 return PwpbPayloadsView<Request<kMethod>>( 134 internal::MethodInfo<kMethod>::serde().request(), 135 internal::test::FakeChannelOutputBuffer< 136 kMaxPackets, 137 kPayloadsBufferSizeBytes>::packets(), 138 packet_type, 139 packet_type, 140 channel_id, 141 internal::MethodInfo<kMethod>::kServiceId, 142 internal::MethodInfo<kMethod>::kMethodId); 143 } 144 145 // Iterates over response payloads from response or server stream packets. 146 // 147 // !!! WARNING !!! 148 // 149 // Access to the FakeChannelOutput through the PwpbPayloadsView is NOT 150 // synchronized! The PwpbPayloadsView is immediately invalidated if any 151 // thread accesses the FakeChannelOutput. 152 template <auto kMethod> 153 PwpbPayloadsView<Response<kMethod>> responses( 154 uint32_t channel_id = Channel::kUnassignedChannelId) const 155 PW_NO_LOCK_SAFETY_ANALYSIS { 156 constexpr internal::pwpb::PacketType packet_type = 157 HasServerStream(internal::MethodInfo<kMethod>::kType) 158 ? internal::pwpb::PacketType::SERVER_STREAM 159 : internal::pwpb::PacketType::RESPONSE; 160 return PwpbPayloadsView<Response<kMethod>>( 161 internal::MethodInfo<kMethod>::serde().response(), 162 internal::test::FakeChannelOutputBuffer< 163 kMaxPackets, 164 kPayloadsBufferSizeBytes>::packets(), 165 packet_type, 166 packet_type, 167 channel_id, 168 internal::MethodInfo<kMethod>::kServiceId, 169 internal::MethodInfo<kMethod>::kMethodId); 170 } 171 172 template <auto kMethod> last_response()173 Response<kMethod> last_response() const { 174 std::lock_guard lock(internal::test::FakeChannelOutput::mutex()); 175 PwpbPayloadsView<Response<kMethod>> payloads = responses<kMethod>(); 176 PW_ASSERT(!payloads.empty()); 177 return payloads.back(); 178 } 179 180 private: 181 template <typename, auto, uint32_t, size_t, size_t> 182 friend class internal::test::pwpb::PwpbInvocationContext; 183 template <typename, size_t, size_t, size_t> 184 friend class internal::ForwardingChannelOutput; 185 186 using internal::test::FakeChannelOutput::last_packet; 187 188 // !!! WARNING !!! 189 // 190 // Access to the FakeChannelOutput through the PwpbPayloadsView is NOT 191 // synchronized! The PwpbPayloadsView is immediately invalidated if any 192 // thread accesses the FakeChannelOutput. 193 template <typename T> payload_structs(const PwpbSerde & serde,MethodType type,uint32_t channel_id,uint32_t service_id,uint32_t method_id)194 PwpbPayloadsView<T> payload_structs(const PwpbSerde& serde, 195 MethodType type, 196 uint32_t channel_id, 197 uint32_t service_id, 198 uint32_t method_id) const 199 PW_NO_LOCK_SAFETY_ANALYSIS { 200 return PwpbPayloadsView<T>(serde, 201 internal::test::FakeChannelOutputBuffer< 202 kMaxPackets, 203 kPayloadsBufferSizeBytes>::packets(), 204 type, 205 channel_id, 206 service_id, 207 method_id); 208 } 209 }; 210 211 } // namespace pw::rpc 212