blob: e7f6dd2953fb3ae2eb64075a0237a5be938ecae5 [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstddef>
#include <iterator>
#include "pw_bytes/span.h"
#include "pw_containers/vector.h"
#include "pw_rpc/channel.h"
#include "pw_rpc/internal/method_info.h"
#include "pw_rpc/internal/packet.h"
#include "pw_rpc/method_type.h"
#include "pw_rpc/payloads_view.h"
namespace pw::rpc {
class FakeServer;
namespace internal::test {
// A ChannelOutput implementation that stores outgoing packets.
class FakeChannelOutput : public ChannelOutput {
public:
FakeChannelOutput(const FakeChannelOutput&) = delete;
FakeChannelOutput(FakeChannelOutput&&) = delete;
~FakeChannelOutput();
FakeChannelOutput& operator=(const FakeChannelOutput&) = delete;
FakeChannelOutput& operator=(FakeChannelOutput&&) = delete;
Status last_status() const {
PW_ASSERT(done());
return packets_.back().status();
}
// Returns a view of the payloads seen for this RPC.
template <auto kMethod>
PayloadsView payloads(
uint32_t channel_id = Channel::kUnassignedChannelId) const {
return PayloadsView(packets_,
MethodInfo<kMethod>::kType,
channel_id,
MethodInfo<kMethod>::kServiceId,
MethodInfo<kMethod>::kMethodId);
}
PayloadsView payloads(MethodType type,
uint32_t channel_id,
uint32_t service_id,
uint32_t method_id) const {
return PayloadsView(packets_, type, channel_id, service_id, method_id);
}
// Returns a view of the final statuses seen for this RPC. Only relevant for
// checking packets sent by a server.
template <auto kMethod>
StatusView completions(
uint32_t channel_id = Channel::kUnassignedChannelId) const {
return StatusView(packets_,
internal::PacketType::RESPONSE,
internal::PacketType::RESPONSE,
channel_id,
MethodInfo<kMethod>::kServiceId,
MethodInfo<kMethod>::kMethodId);
}
template <auto kMethod>
StatusView errors(uint32_t channel_id = Channel::kUnassignedChannelId) const {
return StatusView(packets_,
internal::PacketType::CLIENT_ERROR,
internal::PacketType::SERVER_ERROR,
channel_id,
MethodInfo<kMethod>::kServiceId,
MethodInfo<kMethod>::kMethodId);
}
template <auto kMethod>
size_t client_stream_end_packets(
uint32_t channel_id = Channel::kUnassignedChannelId) const {
return internal::test::PacketsView(
packets_,
internal::test::PacketFilter(
internal::PacketType::CLIENT_STREAM_END,
internal::PacketType::CLIENT_STREAM_END,
channel_id,
MethodInfo<kMethod>::kServiceId,
MethodInfo<kMethod>::kMethodId))
.size();
}
// The maximum number of packets this FakeChannelOutput can store. Attempting
// to store more packets than this is an error.
size_t max_packets() const { return packets_.max_size(); }
// The total number of packets that have been sent.
size_t total_packets() const { return packets_.size(); }
// Set to true if a RESPONSE packet is seen.
bool done() const { return total_response_packets_ > 0; }
// Clears and resets the FakeChannelOutput.
void clear();
// Returns `status` for all future SendAndReleaseBuffer calls. Enables packet
// processing if `status` is OK.
void set_send_status(Status status) {
send_status_ = status;
return_after_packet_count_ = status.ok() ? -1 : 0;
}
// Returns `status` once after the specified positive number of packets.
void set_send_status(Status status, int return_after_packet_count) {
PW_ASSERT(!status.ok());
PW_ASSERT(return_after_packet_count > 0);
send_status_ = status;
return_after_packet_count_ = return_after_packet_count;
}
// Logs which packets have been sent for debugging purposes.
void LogPackets() const;
protected:
constexpr FakeChannelOutput(Vector<Packet>& packets,
Vector<std::byte>& payloads,
ByteSpan encoding_buffer)
: ChannelOutput("pw::rpc::internal::test::FakeChannelOutput"),
packets_(packets),
payloads_(payloads),
encoding_buffer_(encoding_buffer) {}
const Vector<Packet>& packets() const { return packets_; }
private:
friend class rpc::FakeServer;
ByteSpan AcquireBuffer() final;
// Processes buffer according to packet type and `return_after_packet_count_`
// value as follows:
// When positive, returns `send_status_` once,
// When equals 0, returns `send_status_` in all future calls,
// When negative, ignores `send_status_` processes buffer.
Status SendAndReleaseBuffer(ConstByteSpan buffer) final {
const Status status = HandlePacket(buffer);
// Clear the encoding buffer to catch code that uses this buffer after
// releasing it.
std::fill(encoding_buffer_.begin(), encoding_buffer_.end(), std::byte{0});
return status;
}
Status HandlePacket(ConstByteSpan buffer);
void CopyPayloadToBuffer(Packet& packet);
int return_after_packet_count_ = -1;
unsigned total_response_packets_ = 0;
bool buffer_acquired_ = false;
Vector<Packet>& packets_;
Vector<std::byte>& payloads_;
Status send_status_ = OkStatus();
const ByteSpan encoding_buffer_;
};
// Adds the packet output buffer to a FakeChannelOutput.
template <size_t kOutputSizeBytes,
size_t kMaxPackets,
size_t kPayloadsBufferSizeBytes>
class FakeChannelOutputBuffer : public FakeChannelOutput {
protected:
constexpr FakeChannelOutputBuffer()
: FakeChannelOutput(
packets_array_, payloads_array_, encoding_buffer_array_),
encoding_buffer_array_{},
payloads_array_ {}
{}
std::byte encoding_buffer_array_[kOutputSizeBytes];
Vector<std::byte, kPayloadsBufferSizeBytes> payloads_array_;
Vector<Packet, kMaxPackets> packets_array_;
};
} // namespace internal::test
} // namespace pw::rpc