| // 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 "quic/qbone/qbone_packet_exchanger.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "quic/platform/api/quic_test.h" | 
 | #include "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()); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace quic |