1 // Copyright 2021 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 <cstdint> 18 #include <type_traits> 19 20 #include "pw_bytes/span.h" 21 #include "pw_rpc/client.h" 22 #include "pw_rpc/internal/method_info.h" 23 #include "pw_rpc/internal/packet.h" 24 #include "pw_rpc/method_type.h" 25 #include "pw_rpc/raw/fake_channel_output.h" 26 27 namespace pw::rpc { 28 29 // TODO(b/234878467): Document the client testing APIs. 30 31 // Sends packets to an RPC client as if it were a pw_rpc server. 32 class FakeServer { 33 public: FakeServer(internal::test::FakeChannelOutput & output,Client & client,uint32_t channel_id,ByteSpan packet_buffer)34 constexpr FakeServer(internal::test::FakeChannelOutput& output, 35 Client& client, 36 uint32_t channel_id, 37 ByteSpan packet_buffer) 38 : output_(output), 39 client_(client), 40 channel_id_(channel_id), 41 packet_buffer_(packet_buffer) {} 42 43 // Sends a response packet for a server or bidirectional streaming RPC to the 44 // client. 45 template <auto kMethod, 46 typename = std::enable_if_t< 47 HasServerStream(internal::MethodInfo<kMethod>::kType)>> 48 void SendResponse(Status status, 49 std::optional<uint32_t> call_id = std::nullopt) const { 50 SendPacket<kMethod>( 51 internal::pwpb::PacketType::RESPONSE, {}, status, call_id); 52 } 53 54 // Sends a response packet for a unary or client streaming streaming RPC to 55 // the client. 56 template <auto kMethod, 57 typename = std::enable_if_t< 58 !HasServerStream(internal::MethodInfo<kMethod>::kType)>> 59 void SendResponse(ConstByteSpan payload, 60 Status status, 61 std::optional<uint32_t> call_id = std::nullopt) const { 62 SendPacket<kMethod>( 63 internal::pwpb::PacketType::RESPONSE, payload, status, call_id); 64 } 65 66 // Sends a stream packet for a server or bidirectional streaming RPC to the 67 // client. 68 template <auto kMethod> 69 void SendServerStream(ConstByteSpan payload, 70 std::optional<uint32_t> call_id = std::nullopt) const { 71 static_assert(HasServerStream(internal::MethodInfo<kMethod>::kType), 72 "Only server and bidirectional streaming methods can receive " 73 "server stream packets"); 74 SendPacket<kMethod>(internal::pwpb::PacketType::SERVER_STREAM, 75 payload, 76 OkStatus(), 77 call_id); 78 } 79 80 // Sends a server error packet to the client. 81 template <auto kMethod> 82 void SendServerError(Status error, 83 std::optional<uint32_t> call_id = std::nullopt) const { 84 SendPacket<kMethod>( 85 internal::pwpb::PacketType::SERVER_ERROR, {}, error, call_id); 86 } 87 88 private: 89 template <auto kMethod> SendPacket(internal::pwpb::PacketType type,ConstByteSpan payload,Status status,std::optional<uint32_t> call_id)90 void SendPacket(internal::pwpb::PacketType type, 91 ConstByteSpan payload, 92 Status status, 93 std::optional<uint32_t> call_id) const { 94 using Info = internal::MethodInfo<kMethod>; 95 CheckProcessPacket( 96 type, Info::kServiceId, Info::kMethodId, call_id, payload, status); 97 } 98 99 void CheckProcessPacket(internal::pwpb::PacketType type, 100 uint32_t service_id, 101 uint32_t method_id, 102 std::optional<uint32_t> call_id, 103 ConstByteSpan payload, 104 Status status) const; 105 106 Status ProcessPacket(internal::pwpb::PacketType type, 107 uint32_t service_id, 108 uint32_t method_id, 109 std::optional<uint32_t> call_id, 110 ConstByteSpan payload, 111 Status status) const; 112 113 internal::test::FakeChannelOutput& output_; 114 Client& client_; 115 const uint32_t channel_id_; 116 ByteSpan packet_buffer_; // For encoding packets sent by the server 117 }; 118 119 // Instantiates a FakeServer, Client, Channel, and RawFakeChannelOutput for 120 // testing RPC client calls. These components may be used individually, but are 121 // instantiated together for convenience. 122 template <size_t kMaxPackets = 10, 123 size_t kPacketEncodeBufferSizeBytes = 128, 124 size_t kPayloadsBufferSizeBytes = 256> 125 class RawClientTestContext { 126 public: 127 static constexpr uint32_t kDefaultChannelId = 1; 128 RawClientTestContext()129 constexpr RawClientTestContext() 130 : channel_(Channel::Create<kDefaultChannelId>(&channel_output_)), 131 client_(span(&channel_, 1)), 132 packet_buffer_{}, 133 fake_server_( 134 channel_output_, client_, kDefaultChannelId, packet_buffer_) {} 135 channel()136 const Channel& channel() const { return channel_; } channel()137 Channel& channel() { return channel_; } 138 server()139 const FakeServer& server() const { return fake_server_; } server()140 FakeServer& server() { return fake_server_; } 141 client()142 const Client& client() const { return client_; } client()143 Client& client() { return client_; } 144 output()145 const auto& output() const { return channel_output_; } output()146 auto& output() { return channel_output_; } 147 148 private: 149 RawFakeChannelOutput<kMaxPackets, kPayloadsBufferSizeBytes> channel_output_; 150 Channel channel_; 151 Client client_; 152 std::byte packet_buffer_[kPacketEncodeBufferSizeBytes]; 153 FakeServer fake_server_; 154 }; 155 156 } // namespace pw::rpc 157