// 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/core/quic_coalesced_packet.h"

#include "quiche/quic/platform/api/quic_expect_bug.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/common/test_tools/quiche_test_utils.h"

namespace quic {
namespace test {
namespace {

TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) {
  QuicCoalescedPacket coalesced;
  EXPECT_EQ("total_length: 0 padding_size: 0 packets: {}",
            coalesced.ToString(0));
  quiche::SimpleBufferAllocator allocator;
  EXPECT_EQ(0u, coalesced.length());
  EXPECT_EQ(0u, coalesced.NumberOfPackets());
  char buffer[1000];
  QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1);
  QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2);
  SerializedPacket packet1(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet1.transmission_type = PTO_RETRANSMISSION;
  QuicAckFrame ack_frame(InitAckFrame(1));
  packet1.nonretransmittable_frames.push_back(QuicFrame(&ack_frame));
  packet1.retransmittable_frames.push_back(
      QuicFrame(QuicStreamFrame(1, true, 0, 100)));
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(PTO_RETRANSMISSION,
            coalesced.TransmissionTypeOfPacket(ENCRYPTION_INITIAL));
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(500u, coalesced.length());
  EXPECT_EQ(1u, coalesced.NumberOfPackets());
  EXPECT_EQ(
      "total_length: 1500 padding_size: 1000 packets: {ENCRYPTION_INITIAL}",
      coalesced.ToString(1500));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  // Cannot coalesce packet of the same encryption level.
  SerializedPacket packet2(QuicPacketNumber(2), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  EXPECT_FALSE(coalesced.MaybeCoalescePacket(
      packet2, self_address, peer_address, &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  SerializedPacket packet3(QuicPacketNumber(3), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet3.nonretransmittable_frames.push_back(QuicFrame(QuicPaddingFrame(100)));
  packet3.encryption_level = ENCRYPTION_ZERO_RTT;
  packet3.transmission_type = LOSS_RETRANSMISSION;
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet3, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(1000u, coalesced.length());
  EXPECT_EQ(2u, coalesced.NumberOfPackets());
  EXPECT_EQ(LOSS_RETRANSMISSION,
            coalesced.TransmissionTypeOfPacket(ENCRYPTION_ZERO_RTT));
  EXPECT_EQ(
      "total_length: 1500 padding_size: 500 packets: {ENCRYPTION_INITIAL, "
      "ENCRYPTION_ZERO_RTT}",
      coalesced.ToString(1500));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  SerializedPacket packet4(QuicPacketNumber(4), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet4.encryption_level = ENCRYPTION_FORWARD_SECURE;
  // Cannot coalesce packet of changed self/peer address.
  EXPECT_FALSE(coalesced.MaybeCoalescePacket(
      packet4, QuicSocketAddress(QuicIpAddress::Loopback4(), 3), peer_address,
      &allocator, 1500, ECN_NOT_ECT));

  // Packet does not fit.
  SerializedPacket packet5(QuicPacketNumber(5), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 501, false, false);
  packet5.encryption_level = ENCRYPTION_FORWARD_SECURE;
  EXPECT_FALSE(coalesced.MaybeCoalescePacket(
      packet5, self_address, peer_address, &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(1000u, coalesced.length());
  EXPECT_EQ(2u, coalesced.NumberOfPackets());
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  // Max packet number length changed.
  SerializedPacket packet6(QuicPacketNumber(6), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 100, false, false);
  packet6.encryption_level = ENCRYPTION_FORWARD_SECURE;
  EXPECT_QUIC_BUG(
      coalesced.MaybeCoalescePacket(packet6, self_address, peer_address,
                                    &allocator, 1000, ECN_NOT_ECT),
      "Max packet length changes in the middle of the write path");
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(1000u, coalesced.length());
  EXPECT_EQ(2u, coalesced.NumberOfPackets());
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);
}

TEST(QuicCoalescedPacketTest, CopyEncryptedBuffers) {
  QuicCoalescedPacket coalesced;
  quiche::SimpleBufferAllocator allocator;
  QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1);
  QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2);
  std::string buffer(500, 'a');
  std::string buffer2(500, 'b');
  SerializedPacket packet1(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
                           buffer.data(), 500,
                           /*has_ack=*/false, /*has_stop_waiting=*/false);
  packet1.encryption_level = ENCRYPTION_ZERO_RTT;
  SerializedPacket packet2(QuicPacketNumber(2), PACKET_4BYTE_PACKET_NUMBER,
                           buffer2.data(), 500,
                           /*has_ack=*/false, /*has_stop_waiting=*/false);
  packet2.encryption_level = ENCRYPTION_FORWARD_SECURE;

  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet2, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(1000u, coalesced.length());
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  char copy_buffer[1000];
  size_t length_copied = 0;
  EXPECT_FALSE(
      coalesced.CopyEncryptedBuffers(copy_buffer, 900, &length_copied));
  ASSERT_TRUE(
      coalesced.CopyEncryptedBuffers(copy_buffer, 1000, &length_copied));
  EXPECT_EQ(1000u, length_copied);
  char expected[1000];
  memset(expected, 'a', 500);
  memset(expected + 500, 'b', 500);
  quiche::test::CompareCharArraysWithHexError("copied buffers", copy_buffer,
                                              length_copied, expected, 1000);
}

TEST(QuicCoalescedPacketTest, NeuterInitialPacket) {
  QuicCoalescedPacket coalesced;
  EXPECT_EQ("total_length: 0 padding_size: 0 packets: {}",
            coalesced.ToString(0));
  // Noop when neutering initial packet on a empty coalescer.
  coalesced.NeuterInitialPacket();
  EXPECT_EQ("total_length: 0 padding_size: 0 packets: {}",
            coalesced.ToString(0));

  quiche::SimpleBufferAllocator allocator;
  EXPECT_EQ(0u, coalesced.length());
  char buffer[1000];
  QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1);
  QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2);
  SerializedPacket packet1(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet1.transmission_type = PTO_RETRANSMISSION;
  QuicAckFrame ack_frame(InitAckFrame(1));
  packet1.nonretransmittable_frames.push_back(QuicFrame(&ack_frame));
  packet1.retransmittable_frames.push_back(
      QuicFrame(QuicStreamFrame(1, true, 0, 100)));
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(PTO_RETRANSMISSION,
            coalesced.TransmissionTypeOfPacket(ENCRYPTION_INITIAL));
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(500u, coalesced.length());
  EXPECT_EQ(
      "total_length: 1500 padding_size: 1000 packets: {ENCRYPTION_INITIAL}",
      coalesced.ToString(1500));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);
  coalesced.NeuterInitialPacket();
  EXPECT_EQ(0u, coalesced.max_packet_length());
  EXPECT_EQ(0u, coalesced.length());
  EXPECT_EQ("total_length: 0 padding_size: 0 packets: {}",
            coalesced.ToString(0));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  // Coalesce initial packet again.
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));

  SerializedPacket packet2(QuicPacketNumber(3), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet2.nonretransmittable_frames.push_back(QuicFrame(QuicPaddingFrame(100)));
  packet2.encryption_level = ENCRYPTION_ZERO_RTT;
  packet2.transmission_type = LOSS_RETRANSMISSION;
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet2, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(1000u, coalesced.length());
  EXPECT_EQ(LOSS_RETRANSMISSION,
            coalesced.TransmissionTypeOfPacket(ENCRYPTION_ZERO_RTT));
  EXPECT_EQ(
      "total_length: 1500 padding_size: 500 packets: {ENCRYPTION_INITIAL, "
      "ENCRYPTION_ZERO_RTT}",
      coalesced.ToString(1500));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  // Neuter initial packet.
  coalesced.NeuterInitialPacket();
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(500u, coalesced.length());
  EXPECT_EQ(
      "total_length: 1500 padding_size: 1000 packets: {ENCRYPTION_ZERO_RTT}",
      coalesced.ToString(1500));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);

  SerializedPacket packet3(QuicPacketNumber(5), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 501, false, false);
  packet3.encryption_level = ENCRYPTION_FORWARD_SECURE;
  EXPECT_TRUE(coalesced.MaybeCoalescePacket(packet3, self_address, peer_address,
                                            &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(1001u, coalesced.length());
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);
  // Neuter initial packet.
  coalesced.NeuterInitialPacket();
  EXPECT_EQ(1500u, coalesced.max_packet_length());
  EXPECT_EQ(1001u, coalesced.length());
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_NOT_ECT);
}

TEST(QuicCoalescedPacketTest, DoNotCoalesceDifferentEcn) {
  QuicCoalescedPacket coalesced;
  EXPECT_EQ("total_length: 0 padding_size: 0 packets: {}",
            coalesced.ToString(0));
  quiche::SimpleBufferAllocator allocator;
  EXPECT_EQ(0u, coalesced.length());
  EXPECT_EQ(0u, coalesced.NumberOfPackets());
  char buffer[1000];
  QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1);
  QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2);
  SerializedPacket packet1(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet1.transmission_type = PTO_RETRANSMISSION;
  QuicAckFrame ack_frame(InitAckFrame(1));
  packet1.nonretransmittable_frames.push_back(QuicFrame(&ack_frame));
  packet1.retransmittable_frames.push_back(
      QuicFrame(QuicStreamFrame(1, true, 0, 100)));
  ASSERT_TRUE(coalesced.MaybeCoalescePacket(packet1, self_address, peer_address,
                                            &allocator, 1500, ECN_ECT1));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_ECT1);

  SerializedPacket packet2(QuicPacketNumber(2), PACKET_4BYTE_PACKET_NUMBER,
                           buffer, 500, false, false);
  packet2.nonretransmittable_frames.push_back(QuicFrame(QuicPaddingFrame(100)));
  packet2.encryption_level = ENCRYPTION_ZERO_RTT;
  packet2.transmission_type = LOSS_RETRANSMISSION;
  EXPECT_FALSE(coalesced.MaybeCoalescePacket(
      packet2, self_address, peer_address, &allocator, 1500, ECN_NOT_ECT));
  EXPECT_EQ(coalesced.ecn_codepoint(), ECN_ECT1);
}

}  // namespace
}  // namespace test
}  // namespace quic
