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