1 // Copyright 2020 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 15 // Internal-only testing utilities. public/pw_rpc/test_method_context.h provides 16 // improved public-facing utilities for testing RPC services. 17 #pragma once 18 19 #include <array> 20 #include <cstddef> 21 #include <cstdint> 22 #include <span> 23 24 #include "pw_assert/light.h" 25 #include "pw_rpc/client.h" 26 #include "pw_rpc/internal/channel.h" 27 #include "pw_rpc/internal/method.h" 28 #include "pw_rpc/internal/packet.h" 29 #include "pw_rpc/internal/server.h" 30 31 namespace pw::rpc { 32 33 template <size_t kOutputBufferSize> 34 class TestOutput : public ChannelOutput { 35 public: buffer_size()36 static constexpr size_t buffer_size() { return kOutputBufferSize; } 37 38 constexpr TestOutput(const char* name = "TestOutput") ChannelOutput(name)39 : ChannelOutput(name), sent_data_{} {} 40 AcquireBuffer()41 std::span<std::byte> AcquireBuffer() override { return buffer_; } 42 SendAndReleaseBuffer(std::span<const std::byte> buffer)43 Status SendAndReleaseBuffer(std::span<const std::byte> buffer) override { 44 if (buffer.empty()) { 45 return OkStatus(); 46 } 47 48 PW_ASSERT(buffer.data() == buffer_.data()); 49 50 packet_count_ += 1; 51 sent_data_ = buffer; 52 Result<internal::Packet> result = internal::Packet::FromBuffer(sent_data_); 53 EXPECT_EQ(OkStatus(), result.status()); 54 sent_packet_ = result.value_or(internal::Packet()); 55 return send_status_; 56 } 57 buffer()58 std::span<const std::byte> buffer() const { return buffer_; } 59 packet_count()60 size_t packet_count() const { return packet_count_; } 61 set_send_status(Status status)62 void set_send_status(Status status) { send_status_ = status; } 63 sent_data()64 const std::span<const std::byte>& sent_data() const { return sent_data_; } sent_packet()65 const internal::Packet& sent_packet() const { 66 EXPECT_GT(packet_count_, 0u); 67 return sent_packet_; 68 } 69 70 private: 71 std::array<std::byte, buffer_size()> buffer_; 72 std::span<const std::byte> sent_data_; 73 internal::Packet sent_packet_; 74 size_t packet_count_ = 0; 75 Status send_status_; 76 }; 77 78 // Version of the internal::Server with extra methods exposed for testing. 79 class TestServer : public internal::Server { 80 public: 81 using internal::Server::writers; 82 }; 83 84 template <typename Service, 85 size_t kOutputBufferSize = 128, 86 uint32_t kChannelId = 99, 87 uint32_t kServiceId = 16> 88 class ServerContextForTest { 89 public: channel_id()90 static constexpr uint32_t channel_id() { return kChannelId; } service_id()91 static constexpr uint32_t service_id() { return kServiceId; } 92 ServerContextForTest(const internal::Method & method)93 ServerContextForTest(const internal::Method& method) 94 : channel_(Channel::Create<kChannelId>(&output_)), 95 server_(std::span(&channel_, 1)), 96 service_(kServiceId), 97 context_(static_cast<internal::Server&>(server_), 98 static_cast<internal::Channel&>(channel_), 99 service_, 100 method) { 101 server_.RegisterService(service_); 102 } 103 104 // Creates a response packet for this context's channel, service, and method. packet(std::span<const std::byte> payload)105 internal::Packet packet(std::span<const std::byte> payload) const { 106 return internal::Packet(internal::PacketType::RESPONSE, 107 kChannelId, 108 kServiceId, 109 context_.method().id(), 110 payload, 111 OkStatus()); 112 } 113 get()114 internal::ServerCall& get() { return context_; } output()115 auto& output() { return output_; } server()116 TestServer& server() { return static_cast<TestServer&>(server_); } 117 118 private: 119 TestOutput<kOutputBufferSize> output_; 120 Channel channel_; 121 Server server_; 122 Service service_; 123 124 internal::ServerCall context_; 125 }; 126 127 template <size_t kOutputBufferSize = 128, 128 size_t input_buffer_size = 128, 129 uint32_t kChannelId = 99, 130 uint32_t kServiceId = 16, 131 uint32_t kMethodId = 111> 132 class ClientContextForTest { 133 public: channel_id()134 static constexpr uint32_t channel_id() { return kChannelId; } service_id()135 static constexpr uint32_t service_id() { return kServiceId; } method_id()136 static constexpr uint32_t method_id() { return kMethodId; } 137 ClientContextForTest()138 ClientContextForTest() 139 : channel_(Channel::Create<kChannelId>(&output_)), 140 client_(std::span(&channel_, 1)) {} 141 output()142 const auto& output() const { return output_; } channel()143 Channel& channel() { return channel_; } client()144 Client& client() { return client_; } 145 146 // Sends a packet to be processed by the client. Returns the client's 147 // ProcessPacket status. 148 Status SendPacket(internal::PacketType type, 149 Status status = OkStatus(), 150 std::span<const std::byte> payload = {}) { 151 internal::Packet packet( 152 type, kChannelId, kServiceId, kMethodId, payload, status); 153 std::byte buffer[input_buffer_size]; 154 Result result = packet.Encode(buffer); 155 EXPECT_EQ(result.status(), OkStatus()); 156 return client_.ProcessPacket(result.value_or(ConstByteSpan())); 157 } 158 SendResponse(Status status,std::span<const std::byte> payload)159 Status SendResponse(Status status, std::span<const std::byte> payload) { 160 return SendPacket(internal::PacketType::RESPONSE, status, payload); 161 } 162 163 private: 164 TestOutput<kOutputBufferSize> output_; 165 Channel channel_; 166 Client client_; 167 }; 168 169 } // namespace pw::rpc 170