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 <cinttypes> 17 #include <mutex> 18 19 #include "pw_rpc/channel.h" 20 #include "pw_rpc/client_server.h" 21 #include "pw_rpc/internal/fake_channel_output.h" 22 #include "pw_rpc/internal/lock.h" 23 #include "pw_span/span.h" 24 #include "pw_status/status.h" 25 26 namespace pw::rpc { 27 namespace internal { 28 29 // Expands on a Fake Channel Output implementation to allow for forwarding of 30 // packets. 31 template <typename FakeChannelOutputImpl, 32 size_t kOutputSize, 33 size_t kMaxPackets, 34 size_t kPayloadsBufferSizeBytes> 35 class ForwardingChannelOutput : public ChannelOutput { 36 public: MaximumTransmissionUnit()37 size_t MaximumTransmissionUnit() override { 38 return output_.MaximumTransmissionUnit(); 39 } 40 Send(span<const std::byte> buffer)41 Status Send(span<const std::byte> buffer) override { 42 return output_.Send(buffer); 43 } 44 45 // Returns true if new packets were available to forward ForwardNextPacket(ClientServer & client_server)46 bool ForwardNextPacket(ClientServer& client_server) { 47 std::array<std::byte, kOutputSize> packet_buffer; 48 Result<ConstByteSpan> result = EncodeNextUnsentPacket(packet_buffer); 49 if (!result.ok()) { 50 return false; 51 } 52 ++sent_packets_; 53 const auto process_result = client_server.ProcessPacket(*result); 54 PW_ASSERT(process_result.ok()); 55 return true; 56 } 57 58 protected: ForwardingChannelOutput()59 constexpr ForwardingChannelOutput() 60 : ChannelOutput("testing::FakeChannelOutput") {} 61 62 FakeChannelOutputImpl output_; 63 64 // Functions are virtual to allow for their override in threaded version, so 65 // threading protection can be added. PacketCount()66 virtual size_t PacketCount() const { return output_.total_packets(); } 67 EncodeNextUnsentPacket(std::array<std::byte,kPayloadsBufferSizeBytes> & packet_buffer)68 virtual Result<ConstByteSpan> EncodeNextUnsentPacket( 69 std::array<std::byte, kPayloadsBufferSizeBytes>& packet_buffer) { 70 std::lock_guard lock(output_.mutex_); 71 const auto& packets = output_.packets(); 72 if (packets.size() <= sent_packets_) { 73 return Status::NotFound(); 74 } 75 return packets[sent_packets_].Encode(packet_buffer); 76 } 77 78 uint16_t sent_packets_ = 0; 79 }; 80 81 // Provides a testing context with a real client and server 82 template <typename ForwardingChannelOutputImpl, 83 size_t kOutputSize = 128, 84 size_t kMaxPackets = 16, 85 size_t kPayloadsBufferSizeBytes = 128> 86 class ClientServerTestContext { 87 public: channel()88 const pw::rpc::Channel& channel() { return channel_; } client()89 Client& client() { return client_server_.client(); } server()90 Server& server() { return client_server_.server(); } 91 92 // Should be called after each rpc call to synchronously forward all queued 93 // messages. Otherwise this function can be ignored. ForwardNewPackets()94 void ForwardNewPackets() { 95 while (channel_output_.ForwardNextPacket(client_server_)) { 96 } 97 } 98 99 protected: ClientServerTestContext()100 explicit ClientServerTestContext() 101 : channel_(Channel::Create<1>(&channel_output_)), 102 client_server_({&channel_, 1}) {} 103 104 ~ClientServerTestContext() = default; 105 106 ForwardingChannelOutputImpl channel_output_; 107 108 private: 109 pw::rpc::Channel channel_; 110 ClientServer client_server_; 111 }; 112 113 } // namespace internal 114 } // namespace pw::rpc 115