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_function/function.h" 20 #include "pw_rpc/channel.h" 21 #include "pw_rpc/client_server.h" 22 #include "pw_rpc/internal/fake_channel_output.h" 23 #include "pw_rpc/internal/lock.h" 24 #include "pw_rpc/packet_meta.h" 25 #include "pw_span/span.h" 26 #include "pw_status/status.h" 27 28 namespace pw::rpc { 29 using TestPacketProcessor = 30 pw::Function<pw::Status(ClientServer&, pw::ConstByteSpan)>; 31 32 namespace internal { 33 34 // Expands on a Fake Channel Output implementation to allow for forwarding of 35 // packets. 36 template <typename FakeChannelOutputImpl, 37 size_t kOutputSize, 38 size_t kMaxPackets, 39 size_t kPayloadsBufferSizeBytes> 40 class ForwardingChannelOutput : public ChannelOutput { 41 public: MaximumTransmissionUnit()42 size_t MaximumTransmissionUnit() override { 43 return output_.MaximumTransmissionUnit(); 44 } 45 Send(span<const std::byte> buffer)46 Status Send(span<const std::byte> buffer) override { 47 return output_.Send(buffer); 48 } 49 50 // Returns true if new packets were available to forward ForwardNextPacket(ClientServer & client_server)51 bool ForwardNextPacket(ClientServer& client_server) { 52 std::array<std::byte, kOutputSize> packet_buffer; 53 Result<ConstByteSpan> result = EncodeNextUnsentPacket(packet_buffer); 54 if (!result.ok()) { 55 return false; 56 } 57 ++sent_packets_; 58 59 Result<PacketMeta> meta = pw::rpc::PacketMeta::FromBuffer(*result); 60 PW_ASSERT(meta.ok()); 61 62 pw::Status process_result = pw::Status::Internal(); 63 if (meta->destination_is_server() && server_packet_processor_) { 64 process_result = server_packet_processor_(client_server, *result); 65 } else if (meta->destination_is_client() && client_packet_processor_) { 66 process_result = client_packet_processor_(client_server, *result); 67 } else { 68 process_result = client_server.ProcessPacket(*result); 69 } 70 PW_ASSERT(process_result.ok()); 71 return true; 72 } 73 74 protected: 75 explicit ForwardingChannelOutput( 76 TestPacketProcessor&& server_packet_processor = nullptr, 77 TestPacketProcessor&& client_packet_processor = nullptr) 78 : ChannelOutput("testing::FakeChannelOutput"), 79 server_packet_processor_(std::move(server_packet_processor)), 80 client_packet_processor_(std::move(client_packet_processor)) {} 81 82 FakeChannelOutputImpl output_; 83 84 // Functions are virtual to allow for their override in threaded version, so 85 // threading protection can be added. PacketCount()86 virtual size_t PacketCount() const { return output_.total_packets(); } 87 EncodeNextUnsentPacket(std::array<std::byte,kPayloadsBufferSizeBytes> & packet_buffer)88 virtual Result<ConstByteSpan> EncodeNextUnsentPacket( 89 std::array<std::byte, kPayloadsBufferSizeBytes>& packet_buffer) { 90 std::lock_guard lock(output_.mutex_); 91 const auto& packets = output_.packets(); 92 if (packets.size() <= sent_packets_) { 93 return Status::NotFound(); 94 } 95 return packets[sent_packets_].Encode(packet_buffer); 96 } 97 98 uint16_t sent_packets_ = 0; 99 100 const TestPacketProcessor server_packet_processor_; 101 const TestPacketProcessor client_packet_processor_; 102 }; 103 104 // Provides a testing context with a real client and server. This class is for 105 // internal use only and should not be used directly. Instead, use 106 // `NanopbClientServerTestContext` in pw_rpc/nanopb/client_server_testing.h or 107 // `PwpbClientServerTestContext` in pw_rpc/pwpb/client_server_testing.h. 108 template <typename ForwardingChannelOutputImpl, 109 size_t kOutputSize = 128, 110 size_t kMaxPackets = 16, 111 size_t kPayloadsBufferSizeBytes = 128> 112 class ClientServerTestContext { 113 public: channel()114 const pw::rpc::Channel& channel() { return channel_; } client()115 Client& client() { return client_server_.client(); } server()116 Server& server() { return client_server_.server(); } 117 118 // Should be called after each rpc call to synchronously forward all queued 119 // messages. Otherwise this function can be ignored. ForwardNewPackets()120 void ForwardNewPackets() { 121 while (channel_output_.ForwardNextPacket(client_server_)) { 122 } 123 } 124 125 protected: 126 explicit ClientServerTestContext( 127 TestPacketProcessor&& server_packet_processor = nullptr, 128 TestPacketProcessor&& client_packet_processor = nullptr) channel_output_(std::move (server_packet_processor),std::move (client_packet_processor))129 : channel_output_(std::move(server_packet_processor), 130 std::move(client_packet_processor)), 131 channel_(Channel::Create<1>(&channel_output_)), 132 client_server_({&channel_, 1}) {} 133 134 ~ClientServerTestContext() = default; 135 136 ForwardingChannelOutputImpl channel_output_; 137 138 private: 139 pw::rpc::Channel channel_; 140 ClientServer client_server_; 141 }; 142 143 } // namespace internal 144 } // namespace pw::rpc 145