blob: be6084159a9229eb95bbd76ebadf7aabf3a90f8e [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.
#include "quiche/quic/qbone/qbone_packet_exchanger.h"
#include <utility>
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/qbone/mock_qbone_client.h"
namespace quic {
namespace {
using ::testing::StrEq;
using ::testing::StrictMock;
const size_t kMaxPendingPackets = 2;
class MockVisitor : public QbonePacketExchanger::Visitor {
public:
MOCK_METHOD(void, OnReadError, (const std::string&), (override));
MOCK_METHOD(void, OnWriteError, (const std::string&), (override));
};
class FakeQbonePacketExchanger : public QbonePacketExchanger {
public:
using QbonePacketExchanger::QbonePacketExchanger;
// Adds a packet to the end of list of packets to be returned by ReadPacket.
// When the list is empty, ReadPacket returns nullptr to signify error as
// defined by QbonePacketExchanger. If SetReadError is not called or called
// with empty error string, ReadPacket sets blocked to true.
void AddPacketToBeRead(std::unique_ptr<QuicData> packet) {
packets_to_be_read_.push_back(std::move(packet));
}
// Sets the error to be returned by ReadPacket when the list of packets is
// empty. If error is empty string, blocked is set by ReadPacket.
void SetReadError(const std::string& error) { read_error_ = error; }
// Force WritePacket to fail with the given status. WritePacket returns true
// when blocked == true and error is empty.
void ForceWriteFailure(bool blocked, const std::string& error) {
write_blocked_ = blocked;
write_error_ = error;
}
// Packets that have been successfully written by WritePacket.
const std::vector<std::string>& packets_written() const {
return packets_written_;
}
private:
// Implements QbonePacketExchanger::ReadPacket.
std::unique_ptr<QuicData> ReadPacket(bool* blocked,
std::string* error) override {
*blocked = false;
if (packets_to_be_read_.empty()) {
*blocked = read_error_.empty();
*error = read_error_;
return nullptr;
}
std::unique_ptr<QuicData> packet = std::move(packets_to_be_read_.front());
packets_to_be_read_.pop_front();
return packet;
}
// Implements QbonePacketExchanger::WritePacket.
bool WritePacket(const char* packet, size_t size, bool* blocked,
std::string* error) override {
*blocked = false;
if (write_blocked_ || !write_error_.empty()) {
*blocked = write_blocked_;
*error = write_error_;
return false;
}
packets_written_.push_back(std::string(packet, size));
return true;
}
std::string read_error_;
std::list<std::unique_ptr<QuicData>> packets_to_be_read_;
std::string write_error_;
bool write_blocked_ = false;
std::vector<std::string> packets_written_;
};
TEST(QbonePacketExchangerTest,
ReadAndDeliverPacketDeliversPacketToQboneClient) {
StrictMock<MockVisitor> visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
StrictMock<MockQboneClient> client;
std::string packet = "data";
exchanger.AddPacketToBeRead(
std::make_unique<QuicData>(packet.data(), packet.length()));
EXPECT_CALL(client, ProcessPacketFromNetwork(StrEq("data")));
EXPECT_TRUE(exchanger.ReadAndDeliverPacket(&client));
}
TEST(QbonePacketExchangerTest,
ReadAndDeliverPacketNotifiesVisitorOnReadFailure) {
MockVisitor visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
MockQboneClient client;
// Force read error.
std::string io_error = "I/O error";
exchanger.SetReadError(io_error);
EXPECT_CALL(visitor, OnReadError(StrEq(io_error))).Times(1);
EXPECT_FALSE(exchanger.ReadAndDeliverPacket(&client));
}
TEST(QbonePacketExchangerTest,
ReadAndDeliverPacketDoesNotNotifyVisitorOnBlockedIO) {
MockVisitor visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
MockQboneClient client;
// No more packets to read.
EXPECT_FALSE(exchanger.ReadAndDeliverPacket(&client));
}
TEST(QbonePacketExchangerTest,
WritePacketToNetworkWritesDirectlyToNetworkWhenNotBlocked) {
MockVisitor visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
MockQboneClient client;
std::string packet = "data";
exchanger.WritePacketToNetwork(packet.data(), packet.length());
ASSERT_EQ(exchanger.packets_written().size(), 1);
EXPECT_THAT(exchanger.packets_written()[0], StrEq(packet));
}
TEST(QbonePacketExchangerTest,
WritePacketToNetworkQueuesPacketsAndProcessThemLater) {
MockVisitor visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
MockQboneClient client;
// Force write to be blocked so that packets are queued.
exchanger.ForceWriteFailure(true, "");
std::vector<std::string> packets = {"packet0", "packet1"};
for (int i = 0; i < packets.size(); i++) {
exchanger.WritePacketToNetwork(packets[i].data(), packets[i].length());
}
// Nothing should have been written because of blockage.
ASSERT_TRUE(exchanger.packets_written().empty());
// Remove blockage and start proccessing queued packets.
exchanger.ForceWriteFailure(false, "");
exchanger.SetWritable();
// Queued packets are processed.
ASSERT_EQ(exchanger.packets_written().size(), 2);
for (int i = 0; i < packets.size(); i++) {
EXPECT_THAT(exchanger.packets_written()[i], StrEq(packets[i]));
}
}
TEST(QbonePacketExchangerTest,
SetWritableContinuesProcessingPacketIfPreviousCallBlocked) {
MockVisitor visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
MockQboneClient client;
// Force write to be blocked so that packets are queued.
exchanger.ForceWriteFailure(true, "");
std::vector<std::string> packets = {"packet0", "packet1"};
for (int i = 0; i < packets.size(); i++) {
exchanger.WritePacketToNetwork(packets[i].data(), packets[i].length());
}
// Nothing should have been written because of blockage.
ASSERT_TRUE(exchanger.packets_written().empty());
// Start processing packets, but since writes are still blocked, nothing
// should have been written.
exchanger.SetWritable();
ASSERT_TRUE(exchanger.packets_written().empty());
// Remove blockage and start processing packets again.
exchanger.ForceWriteFailure(false, "");
exchanger.SetWritable();
ASSERT_EQ(exchanger.packets_written().size(), 2);
for (int i = 0; i < packets.size(); i++) {
EXPECT_THAT(exchanger.packets_written()[i], StrEq(packets[i]));
}
}
TEST(QbonePacketExchangerTest, WritePacketToNetworkDropsPacketIfQueueIfFull) {
std::vector<std::string> packets = {"packet0", "packet1", "packet2"};
size_t queue_size = packets.size() - 1;
MockVisitor visitor;
// exchanger has smaller queue than number of packets.
FakeQbonePacketExchanger exchanger(&visitor, queue_size);
MockQboneClient client;
exchanger.ForceWriteFailure(true, "");
for (int i = 0; i < packets.size(); i++) {
exchanger.WritePacketToNetwork(packets[i].data(), packets[i].length());
}
// Blocked writes cause packets to be queued or dropped.
ASSERT_TRUE(exchanger.packets_written().empty());
exchanger.ForceWriteFailure(false, "");
exchanger.SetWritable();
ASSERT_EQ(exchanger.packets_written().size(), queue_size);
for (int i = 0; i < queue_size; i++) {
EXPECT_THAT(exchanger.packets_written()[i], StrEq(packets[i]));
}
}
TEST(QbonePacketExchangerTest, WriteErrorsGetNotified) {
MockVisitor visitor;
FakeQbonePacketExchanger exchanger(&visitor, kMaxPendingPackets);
MockQboneClient client;
std::string packet = "data";
// Write error is delivered to visitor during WritePacketToNetwork.
std::string io_error = "I/O error";
exchanger.ForceWriteFailure(false, io_error);
EXPECT_CALL(visitor, OnWriteError(StrEq(io_error))).Times(1);
exchanger.WritePacketToNetwork(packet.data(), packet.length());
ASSERT_TRUE(exchanger.packets_written().empty());
// Write error is delivered to visitor during SetWritable.
exchanger.ForceWriteFailure(true, "");
exchanger.WritePacketToNetwork(packet.data(), packet.length());
std::string sys_error = "sys error";
exchanger.ForceWriteFailure(false, sys_error);
EXPECT_CALL(visitor, OnWriteError(StrEq(sys_error))).Times(1);
exchanger.SetWritable();
ASSERT_TRUE(exchanger.packets_written().empty());
}
TEST(QbonePacketExchangerTest, NullVisitorDoesntCrash) {
FakeQbonePacketExchanger exchanger(nullptr, kMaxPendingPackets);
MockQboneClient client;
std::string packet = "data";
// Force read error.
std::string io_error = "I/O error";
exchanger.SetReadError(io_error);
EXPECT_FALSE(exchanger.ReadAndDeliverPacket(&client));
// Force write error
exchanger.ForceWriteFailure(false, io_error);
exchanger.WritePacketToNetwork(packet.data(), packet.length());
EXPECT_TRUE(exchanger.packets_written().empty());
}
} // namespace
} // namespace quic