xref: /aosp_15_r20/external/pigweed/pw_rpc/public/pw_rpc/internal/client_server_testing.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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