blob: e14040fb39cf772bf0291e25b65f52b0bfdea9f5 [file] [log] [blame]
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_
#define QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_
#include <sys/socket.h>
#include <sys/types.h>
#include <cstddef>
#include <iostream>
#include <utility>
#include "absl/base/optimization.h"
#include "quic/core/batch_writer/quic_batch_writer_base.h"
#include "quic/core/quic_udp_socket.h"
#include "quic/platform/api/quic_test.h"
namespace quic {
namespace test {
static bool IsAddressFamilySupported(int address_family) {
static auto check_function = [](int address_family) {
int fd = socket(address_family, SOCK_STREAM, 0);
if (fd < 0) {
QUIC_LOG(ERROR) << "address_family not supported: " << address_family
<< ", error: " << strerror(errno);
EXPECT_EQ(EAFNOSUPPORT, errno);
return false;
}
close(fd);
return true;
};
if (address_family == AF_INET) {
static const bool ipv4_supported = check_function(AF_INET);
return ipv4_supported;
}
static const bool ipv6_supported = check_function(AF_INET6);
return ipv6_supported;
}
static bool CreateSocket(int family, QuicSocketAddress* address, int* fd) {
if (family == AF_INET) {
*address = QuicSocketAddress(QuicIpAddress::Loopback4(), 0);
} else {
QUICHE_DCHECK_EQ(family, AF_INET6);
*address = QuicSocketAddress(QuicIpAddress::Loopback6(), 0);
}
QuicUdpSocketApi socket_api;
*fd = socket_api.Create(family,
/*receive_buffer_size=*/kDefaultSocketReceiveBuffer,
/*send_buffer_size=*/kDefaultSocketReceiveBuffer);
if (*fd < 0) {
QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
return false;
}
socket_api.EnableDroppedPacketCount(*fd);
if (!socket_api.Bind(*fd, *address)) {
QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
return false;
}
if (address->FromSocket(*fd) != 0) {
QUIC_LOG(ERROR) << "Unable to get self address. Error: "
<< strerror(errno);
return false;
}
return true;
}
struct QuicUdpBatchWriterIOTestParams;
class QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTestDelegate {
public:
virtual ~QuicUdpBatchWriterIOTestDelegate() {}
virtual bool ShouldSkip(const QuicUdpBatchWriterIOTestParams& /*params*/) {
return false;
}
virtual void ResetWriter(int fd) = 0;
virtual QuicUdpBatchWriter* GetWriter() = 0;
};
struct QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTestParams {
// Use shared_ptr because gtest makes copies of test params.
std::shared_ptr<QuicUdpBatchWriterIOTestDelegate> delegate;
int address_family;
int data_size;
int packet_size;
QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os,
const QuicUdpBatchWriterIOTestParams& p) {
os << "{ address_family: " << p.address_family
<< " data_size: " << p.data_size << " packet_size: " << p.packet_size
<< " }";
return os;
}
};
template <class QuicUdpBatchWriterIOTestDelegateT>
static std::vector<QuicUdpBatchWriterIOTestParams>
MakeQuicBatchWriterTestParams() {
static_assert(std::is_base_of<QuicUdpBatchWriterIOTestDelegate,
QuicUdpBatchWriterIOTestDelegateT>::value,
"<QuicUdpBatchWriterIOTestDelegateT> needs to derive from "
"QuicUdpBatchWriterIOTestDelegate");
std::vector<QuicUdpBatchWriterIOTestParams> params;
for (int address_family : {AF_INET, AF_INET6}) {
for (int data_size : {1, 150, 1500, 15000, 64000, 512 * 1024}) {
for (int packet_size : {1, 50, 1350, 1452}) {
if (packet_size <= data_size && (data_size / packet_size < 2000)) {
params.push_back(
{std::make_unique<QuicUdpBatchWriterIOTestDelegateT>(),
address_family, data_size, packet_size});
}
}
}
}
return params;
}
// QuicUdpBatchWriterIOTest is a value parameterized test fixture that can be
// used by tests of derived classes of QuicUdpBatchWriter, to verify basic
// packet IO capabilities.
class QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTest
: public QuicTestWithParam<QuicUdpBatchWriterIOTestParams> {
protected:
QuicUdpBatchWriterIOTest()
: address_family_(GetParam().address_family),
data_size_(GetParam().data_size),
packet_size_(GetParam().packet_size),
self_socket_(-1),
peer_socket_(-1) {
QUIC_LOG(INFO) << "QuicUdpBatchWriterIOTestParams: " << GetParam();
EXPECT_TRUE(address_family_ == AF_INET || address_family_ == AF_INET6);
EXPECT_LE(packet_size_, data_size_);
EXPECT_LE(packet_size_, sizeof(packet_buffer_));
}
~QuicUdpBatchWriterIOTest() override {
if (self_socket_ > 0) {
close(self_socket_);
}
if (peer_socket_ > 0) {
close(peer_socket_);
}
}
// Whether this test should be skipped. A test is passed if skipped.
// A test can be skipped when e.g. it exercises a kernel feature that is not
// available on the system.
bool ShouldSkip() {
if (!IsAddressFamilySupported(address_family_)) {
QUIC_LOG(WARNING)
<< "Test skipped since address_family is not supported.";
return true;
}
return GetParam().delegate->ShouldSkip(GetParam());
}
// Initialize a test.
// To fail the test in Initialize, use ASSERT_xx macros.
void Initialize() {
ASSERT_TRUE(CreateSocket(address_family_, &self_address_, &self_socket_));
ASSERT_TRUE(CreateSocket(address_family_, &peer_address_, &peer_socket_));
QUIC_DLOG(INFO) << "Self address: " << self_address_.ToString() << ", fd "
<< self_socket_;
QUIC_DLOG(INFO) << "Peer address: " << peer_address_.ToString() << ", fd "
<< peer_socket_;
GetParam().delegate->ResetWriter(self_socket_);
}
QuicUdpBatchWriter* GetWriter() { return GetParam().delegate->GetWriter(); }
void ValidateWrite() {
char this_packet_content = '\0';
int this_packet_size;
int num_writes = 0;
size_t bytes_flushed = 0;
WriteResult result;
for (size_t bytes_sent = 0; bytes_sent < data_size_;
bytes_sent += this_packet_size, ++this_packet_content) {
this_packet_size = std::min(packet_size_, data_size_ - bytes_sent);
memset(&packet_buffer_[0], this_packet_content, this_packet_size);
result = GetWriter()->WritePacket(&packet_buffer_[0], this_packet_size,
self_address_.host(), peer_address_,
nullptr);
ASSERT_EQ(WRITE_STATUS_OK, result.status) << strerror(result.error_code);
bytes_flushed += result.bytes_written;
++num_writes;
QUIC_DVLOG(1) << "[write #" << num_writes
<< "] this_packet_size: " << this_packet_size
<< ", total_bytes_sent: " << bytes_sent + this_packet_size
<< ", bytes_flushed: " << bytes_flushed
<< ", pkt content:" << std::hex << int(this_packet_content);
}
result = GetWriter()->Flush();
ASSERT_EQ(WRITE_STATUS_OK, result.status) << strerror(result.error_code);
bytes_flushed += result.bytes_written;
ASSERT_EQ(data_size_, bytes_flushed);
QUIC_LOG(INFO) << "Sent " << data_size_ << " bytes in " << num_writes
<< " writes.";
}
void ValidateRead() {
char this_packet_content = '\0';
int this_packet_size;
int packets_received = 0;
for (size_t bytes_received = 0; bytes_received < data_size_;
bytes_received += this_packet_size, ++this_packet_content) {
this_packet_size = std::min(packet_size_, data_size_ - bytes_received);
SCOPED_TRACE(testing::Message()
<< "Before ReadPacket: bytes_received=" << bytes_received
<< ", this_packet_size=" << this_packet_size);
QuicUdpSocketApi::ReadPacketResult result;
result.packet_buffer = {&packet_buffer_[0], sizeof(packet_buffer_)};
result.control_buffer = {&control_buffer_[0], sizeof(control_buffer_)};
QuicUdpSocketApi().ReadPacket(
peer_socket_,
quic::BitMask64(QuicUdpPacketInfoBit::V4_SELF_IP,
QuicUdpPacketInfoBit::V6_SELF_IP,
QuicUdpPacketInfoBit::PEER_ADDRESS),
&result);
ASSERT_TRUE(result.ok);
ASSERT_TRUE(
result.packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS));
QuicSocketAddress read_peer_address = result.packet_info.peer_address();
QuicIpAddress read_self_address = read_peer_address.host().IsIPv6()
? result.packet_info.self_v6_ip()
: result.packet_info.self_v4_ip();
EXPECT_EQ(read_self_address, peer_address_.host());
EXPECT_EQ(read_peer_address, self_address_);
for (int i = 0; i < this_packet_size; ++i) {
EXPECT_EQ(this_packet_content, packet_buffer_[i]);
}
packets_received += this_packet_size;
}
QUIC_LOG(INFO) << "Received " << data_size_ << " bytes in "
<< packets_received << " packets.";
}
QuicSocketAddress self_address_;
QuicSocketAddress peer_address_;
ABSL_CACHELINE_ALIGNED char packet_buffer_[1500];
ABSL_CACHELINE_ALIGNED char
control_buffer_[kDefaultUdpPacketControlBufferSize];
int address_family_;
const size_t data_size_;
const size_t packet_size_;
int self_socket_;
int peer_socket_;
};
TEST_P(QuicUdpBatchWriterIOTest, WriteAndRead) {
if (ShouldSkip()) {
return;
}
Initialize();
ValidateWrite();
ValidateRead();
}
} // namespace test
} // namespace quic
#endif // QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_