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 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 23 #include "gtest/gtest.h" 24 #include "pw_assert/assert.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/raw/fake_channel_output.h" 30 #include "pw_rpc/server.h" 31 #include "pw_span/span.h" 32 33 namespace pw::rpc::internal { 34 35 // Version of the Server with extra methods exposed for testing. 36 class TestServer : public Server { 37 public: 38 using Server::calls_end; 39 using Server::CloseCallAndMarkForCleanup; 40 using Server::FindCall; 41 }; 42 43 template <typename Service, uint32_t kChannelId = 99, uint32_t kServiceId = 16> 44 class ServerContextForTest { 45 public: channel_id()46 static constexpr uint32_t channel_id() { return kChannelId; } service_id()47 static constexpr uint32_t service_id() { return kServiceId; } 48 ServerContextForTest(const internal::Method & method)49 ServerContextForTest(const internal::Method& method) 50 : channel_(Channel::Create<kChannelId>(&output_)), 51 server_(span(&channel_, 1)), 52 service_(kServiceId), 53 context_(server_, channel_.id(), service_, method, 0) { 54 server_.RegisterService(service_); 55 } 56 57 // Create packets for this context's channel, service, and method. request(span<const std::byte> payload)58 internal::Packet request(span<const std::byte> payload) const { 59 return internal::Packet(internal::pwpb::PacketType::REQUEST, 60 kChannelId, 61 kServiceId, 62 context_.method().id(), 63 0, 64 payload); 65 } 66 67 internal::Packet response(Status status, 68 span<const std::byte> payload = {}) const { 69 return internal::Packet(internal::pwpb::PacketType::RESPONSE, 70 kChannelId, 71 kServiceId, 72 context_.method().id(), 73 0, 74 payload, 75 status); 76 } 77 server_stream(span<const std::byte> payload)78 internal::Packet server_stream(span<const std::byte> payload) const { 79 return internal::Packet(internal::pwpb::PacketType::SERVER_STREAM, 80 kChannelId, 81 kServiceId, 82 context_.method().id(), 83 0, 84 payload); 85 } 86 client_stream(span<const std::byte> payload)87 internal::Packet client_stream(span<const std::byte> payload) const { 88 return internal::Packet(internal::pwpb::PacketType::CLIENT_STREAM, 89 kChannelId, 90 kServiceId, 91 context_.method().id(), 92 0, 93 payload); 94 } 95 96 CallContext get(uint32_t id = 0) const { 97 return CallContext(context_.server(), 98 context_.channel_id(), 99 context_.service(), 100 context_.method(), 101 id); 102 } 103 output()104 internal::test::FakeChannelOutput& output() { return output_; } server()105 TestServer& server() { return static_cast<TestServer&>(server_); } service()106 Service& service() { return service_; } 107 108 private: 109 RawFakeChannelOutput<5> output_; 110 rpc::Channel channel_; 111 rpc::Server server_; 112 Service service_; 113 114 const internal::CallContext context_; 115 }; 116 117 template <size_t kInputBufferSize = 128, 118 uint32_t kChannelId = 99, 119 uint32_t kServiceId = 16, 120 uint32_t kMethodId = 111> 121 class ClientContextForTest { 122 public: channel_id()123 static constexpr uint32_t channel_id() { return kChannelId; } service_id()124 static constexpr uint32_t service_id() { return kServiceId; } method_id()125 static constexpr uint32_t method_id() { return kMethodId; } 126 ClientContextForTest()127 ClientContextForTest() 128 : channel_(Channel::Create<kChannelId>(&output_)), 129 client_(span(&channel_, 1)) {} 130 output()131 const internal::test::FakeChannelOutput& output() const { return output_; } channel()132 Channel& channel() { return static_cast<Channel&>(channel_); } client()133 Client& client() { return client_; } 134 135 // Sends a packet to be processed by the client. Returns the client's 136 // ProcessPacket status. 137 Status SendPacket(internal::pwpb::PacketType type, 138 Status status = OkStatus(), 139 span<const std::byte> payload = {}) { 140 uint32_t call_id = 141 output().total_packets() > 0 ? output().last_packet().call_id() : 0; 142 143 internal::Packet packet( 144 type, kChannelId, kServiceId, kMethodId, call_id, payload, status); 145 std::byte buffer[kInputBufferSize]; 146 Result result = packet.Encode(buffer); 147 EXPECT_EQ(result.status(), OkStatus()); 148 return client_.ProcessPacket(result.value_or(ConstByteSpan())); 149 } 150 151 Status SendResponse(Status status, span<const std::byte> payload = {}) { 152 return SendPacket(internal::pwpb::PacketType::RESPONSE, status, payload); 153 } 154 SendServerStream(span<const std::byte> payload)155 Status SendServerStream(span<const std::byte> payload) { 156 return SendPacket( 157 internal::pwpb::PacketType::SERVER_STREAM, OkStatus(), payload); 158 } 159 160 private: 161 RawFakeChannelOutput<5> output_; 162 rpc::Channel channel_; 163 Client client_; 164 }; 165 166 } // namespace pw::rpc::internal 167