/*
 *  Copyright 2024 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "p2p/dtls/dtls_stun_piggyback_controller.h"

#include <cstdint>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "api/transport/stun.h"
#include "p2p/dtls/dtls_utils.h"
#include "rtc_base/byte_buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/network/received_packet.h"
#include "rtc_base/socket_address.h"
#include "test/gmock.h"
#include "test/gtest.h"

namespace webrtc {

namespace {
// Extracted from a stock DTLS call using Wireshark.
// Each packet (apart from the last) is truncated to
// the first fragment to keep things short.

// Based on a "server hello done" but with different msg_seq.
const std::vector<uint8_t> dtls_flight1 = {
    0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
    0x00, 0x01,                                            // seq=1
    0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00,  // msg_seq=0x1234
    0x00, 0x00, 0x00, 0x00, 0x00};

const std::vector<uint8_t> dtls_flight2 = {
    0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
    0x00, 0x02,                                            // seq=2
    0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x43, 0x21, 0x00,  // msg_seq=0x4321
    0x00, 0x00, 0x00, 0x00, 0x00};

const std::vector<uint8_t> dtls_flight3 = {
    0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
    0x00, 0x03,                                            // seq=3
    0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00,  // msg_seq=0x4444
    0x00, 0x00, 0x00, 0x00, 0x00};

const std::vector<uint8_t> dtls_flight4 = {
    0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
    0x00, 0x04,                                            // seq=4
    0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x54, 0x86, 0x00,  // msg_seq=0x5486
    0x00, 0x00, 0x00, 0x00, 0x00};

const std::vector<uint8_t> empty = {};

const std::vector<uint8_t> kPayload = {0x1, 0x2, 0x3};

std::vector<uint32_t> FromAckAttribute(std::span<uint8_t> attr) {
  ByteBufferReader ack_reader(attr);
  std::vector<uint32_t> values;
  uint32_t value;
  while (ack_reader.ReadUInt32(&value)) {
    values.push_back(value);
  }
  RTC_DCHECK_EQ(ack_reader.Length(), 0);
  return values;
}

std::vector<uint8_t> FakeDtlsPacket(uint16_t packet_number) {
  auto packet = dtls_flight1;
  packet[17] = static_cast<uint8_t>(packet_number >> 8);
  packet[18] = static_cast<uint8_t>(packet_number & 255);
  return packet;
}

std::unique_ptr<StunByteStringAttribute> WrapInStun(IceAttributeType type,
                                                    absl::string_view data) {
  return std::make_unique<StunByteStringAttribute>(type, data);
}

std::unique_ptr<StunByteStringAttribute> WrapInStun(
    IceAttributeType type,
    const std::vector<uint8_t>& data) {
  return std::make_unique<StunByteStringAttribute>(type, data);
}

std::unique_ptr<StunByteStringAttribute> WrapInStun(
    IceAttributeType type,
    const std::vector<uint32_t>& data) {
  return std::make_unique<StunByteStringAttribute>(type, data);
}

}  // namespace

using ::testing::ElementsAreArray;
using ::testing::MockFunction;
using ::testing::NotNull;
using State = DtlsStunPiggybackController::State;

class DtlsStunPiggybackControllerTest : public ::testing::Test {
 protected:
  DtlsStunPiggybackControllerTest()
      : client_(
            [this](std::span<const uint8_t> data) { ClientPacketSink(data); },
            [this](bool success) { ClientCompleteCallback(success); }),
        server_(
            [this](std::span<const uint8_t> data) { ServerPacketSink(data); },
            [this](bool success) { ServerCompleteCallback(success); }),
        packet_(kPayload, SocketAddress(), std::nullopt) {}

  // Send from client to server embedded in STUN.
  void SendClientToServerEmbedded(const std::vector<uint8_t>& packet,
                                  StunMessageType type) {
    if (!packet.empty()) {
      client_.CapturePacket(packet);
      client_.Flush();
    } else {
      client_.ClearCachedPacketForTesting();
    }
    std::unique_ptr<StunByteStringAttribute> attr_data;
    std::optional<std::span<uint8_t>> view_data;
    if (auto data = client_.GetDataToPiggyback(type)) {
      attr_data = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, *data);
      view_data = attr_data->array_view();
    }
    std::unique_ptr<StunByteStringAttribute> attr_ack;
    std::optional<std::vector<uint32_t>> view_acks;
    if (auto ack = client_.GetAckToPiggyback(type)) {
      attr_ack = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN_ACK, *ack);
      view_acks = FromAckAttribute(attr_ack->array_view());
    }
    server_.ReportDataPiggybacked(view_data, view_acks);
  }
  // Send from client to server as plain DTLS.
  void SendClientToServerDtls(const std::vector<uint8_t> packet) {
    if (!packet.empty()) {
      client_.CapturePacket(packet);
      client_.Flush();
    } else {
      client_.ClearCachedPacketForTesting();
    }
    server_.ReportDtlsPacket(packet);
  }
  // Send from server to client embedded in STUN
  void SendServerToClientEmbedded(const std::vector<uint8_t>& packet,
                                  StunMessageType type) {
    if (!packet.empty()) {
      server_.CapturePacket(packet);
      server_.Flush();
    } else {
      server_.ClearCachedPacketForTesting();
    }
    std::unique_ptr<StunByteStringAttribute> attr_data;
    std::optional<std::span<uint8_t>> view_data;
    if (auto data = server_.GetDataToPiggyback(type)) {
      attr_data = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, *data);
      view_data = attr_data->array_view();
    }
    std::unique_ptr<StunByteStringAttribute> attr_ack;
    std::optional<std::vector<uint32_t>> view_acks;
    if (auto ack = server_.GetAckToPiggyback(type)) {
      attr_ack = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN_ACK, *ack);
      view_acks = FromAckAttribute(attr_ack->array_view());
    }
    client_.ReportDataPiggybacked(view_data, view_acks);
    MaybeSetHandshakeComplete(packet);
  }
  // Send from server to client as plain DTLS.
  void SendServerToClientDtls(const std::vector<uint8_t> packet) {
    if (!packet.empty()) {
      server_.CapturePacket(packet);
      server_.Flush();
    } else {
      server_.ClearCachedPacketForTesting();
    }
    client_.ReportDtlsPacket(packet);
    MaybeSetHandshakeComplete(packet);
  }

  void DisableSupport(DtlsStunPiggybackController& client_or_server) {
    ASSERT_EQ(client_or_server.state(), State::TENTATIVE);
    client_or_server.ReportDataPiggybacked(std::nullopt, std::nullopt);
    ASSERT_EQ(client_or_server.state(), State::OFF);
  }

  DtlsStunPiggybackController client_;
  DtlsStunPiggybackController server_;

  MOCK_METHOD(void, ClientPacketSink, (std::span<const uint8_t>));
  MOCK_METHOD(void, ServerPacketSink, (std::span<const uint8_t>));

  MOCK_METHOD(void, ClientCompleteCallback, (bool));
  MOCK_METHOD(void, ServerCompleteCallback, (bool));

  ReceivedIpPacket packet_;

 private:
  void MaybeSetHandshakeComplete(std::vector<uint8_t> packet) {
    // Note: this assumes DTLS 1.2
    if (packet == dtls_flight4) {
      // After sending flight 4, the server handshake is complete.
      server_.SetDtlsHandshakeComplete(/*is_client=*/false,
                                       /*is_dtls13=*/false);
      // When receiving flight 4, client handshake is complete.
      client_.SetDtlsHandshakeComplete(/*is_client=*/true, /*is_dtls13=*/false);
    }
  }
};

TEST_F(DtlsStunPiggybackControllerTest, BasicHandshake) {
  // Flight 1+2
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 3+4
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::PENDING);
  EXPECT_EQ(client_.state(), State::PENDING);

  // Post-handshake ACK
  EXPECT_CALL(*this, ClientCompleteCallback(true));
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_CALL(*this, ServerCompleteCallback(true));
  SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::COMPLETE);
  EXPECT_EQ(client_.state(), State::COMPLETE);
}

TEST_F(DtlsStunPiggybackControllerTest,
       BasicHandshakeCompleteWithDecryptedPacket) {
  // Flight 1+2
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 3+4
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::PENDING);
  EXPECT_EQ(client_.state(), State::PENDING);

  // Post-handshake ACK
  EXPECT_CALL(*this, ClientCompleteCallback);
  client_.ApplicationPacketReceived(
      packet_.CopyAndSet(ReceivedIpPacket::kDtlsDecrypted));
  EXPECT_EQ(client_.state(), State::COMPLETE);

  EXPECT_CALL(*this, ServerCompleteCallback);
  server_.ApplicationPacketReceived(
      packet_.CopyAndSet(ReceivedIpPacket::kSrtpEncrypted));
  EXPECT_EQ(server_.state(), State::COMPLETE);
}

TEST_F(DtlsStunPiggybackControllerTest,
       BasicHandshakeEarlySrtpDoesNotComplete) {
  // Flight 1+2
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 3
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  EXPECT_EQ(server_.state(), State::CONFIRMED);

  // An srtp packet arriving before reaching PENDING state.
  server_.ApplicationPacketReceived(
      packet_.CopyAndSet(ReceivedIpPacket::kSrtpEncrypted));
  EXPECT_EQ(server_.state(), State::CONFIRMED);

  // Flight 4
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::PENDING);
  EXPECT_EQ(client_.state(), State::PENDING);

  // Post-handshake ACK
  EXPECT_CALL(*this, ClientCompleteCallback);
  client_.ApplicationPacketReceived(
      packet_.CopyAndSet(ReceivedIpPacket::kDtlsDecrypted));
  EXPECT_EQ(client_.state(), State::COMPLETE);

  EXPECT_CALL(*this, ServerCompleteCallback);
  server_.ApplicationPacketReceived(
      packet_.CopyAndSet(ReceivedIpPacket::kSrtpEncrypted));
  EXPECT_EQ(server_.state(), State::COMPLETE);
}

TEST_F(DtlsStunPiggybackControllerTest, FirstClientPacketLost) {
  // Client to server got lost (or arrives late)
  // Flight 1
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 2+3
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_REQUEST);
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 4
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_REQUEST);
  EXPECT_CALL(*this, ServerCompleteCallback(true));
  SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::COMPLETE);
  EXPECT_EQ(client_.state(), State::PENDING);

  // Post-handshake ACK
  EXPECT_CALL(*this, ClientCompleteCallback(true));
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_EQ(client_.state(), State::COMPLETE);
}

TEST_F(DtlsStunPiggybackControllerTest, NotSupportedByServer) {
  DisableSupport(server_);

  // Flight 1
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  // TODO: bugs.webrtc.org/367395350 - assert when calling the complete
  // callback in this case which currently causes a sleuth of test failures.
  // EXPECT_CALL(*this, ClientCompleteCallback());
  SendServerToClientEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(client_.state(), State::OFF);
}

TEST_F(DtlsStunPiggybackControllerTest, NotSupportedByServerClientReceives) {
  DisableSupport(server_);

  // Client to server got lost (or arrives late)
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_EQ(client_.state(), State::OFF);
}

TEST_F(DtlsStunPiggybackControllerTest, NotSupportedByClient) {
  DisableSupport(client_);

  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::OFF);
}

TEST_F(DtlsStunPiggybackControllerTest, SomeRequestsDoNotGoThrough) {
  // Client to server got lost (or arrives late)
  // Flight 1
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 1+2, server sent request got lost.
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 3+4
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::PENDING);
  EXPECT_EQ(client_.state(), State::PENDING);

  // Post-handshake ACK
  EXPECT_CALL(*this, ServerCompleteCallback(true));
  SendClientToServerEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_CALL(*this, ClientCompleteCallback(true));
  SendServerToClientEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::COMPLETE);
  EXPECT_EQ(client_.state(), State::COMPLETE);
}

TEST_F(DtlsStunPiggybackControllerTest, LossOnPostHandshakeAck) {
  // Flight 1+2
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  EXPECT_EQ(server_.state(), State::CONFIRMED);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_EQ(client_.state(), State::CONFIRMED);

  // Flight 3+4
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::PENDING);
  EXPECT_EQ(client_.state(), State::PENDING);

  // Post-handshake ACK. Client to server gets lost
  EXPECT_CALL(*this, ClientCompleteCallback(true));
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_CALL(*this, ServerCompleteCallback(true));
  SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::COMPLETE);
  EXPECT_EQ(client_.state(), State::COMPLETE);
}

TEST_F(DtlsStunPiggybackControllerTest,
       UnsupportedStateAfterFallbackHandshakeRemainsOff) {
  DisableSupport(client_);
  DisableSupport(server_);

  // Set DTLS complete after normal handshake.
  client_.SetDtlsHandshakeComplete(/*is_client=*/true, /*is_dtls13=*/false);
  EXPECT_EQ(client_.state(), State::OFF);
  server_.SetDtlsHandshakeComplete(/*is_client=*/false, /*is_dtls13=*/false);
  EXPECT_EQ(server_.state(), State::OFF);
}

TEST_F(DtlsStunPiggybackControllerTest, BasicHandshakeAckData) {
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
            std::vector<uint32_t>({}));
  EXPECT_EQ(client_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
            std::vector<uint32_t>({}));

  // Flight 1+2
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({ComputeDtlsPacketHash(dtls_flight1)}));
  EXPECT_THAT(*client_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
              ElementsAreArray({ComputeDtlsPacketHash(dtls_flight2)}));

  // Flight 3+4
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight1),
                  ComputeDtlsPacketHash(dtls_flight3),
              }));
  EXPECT_THAT(*client_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight2),
                  ComputeDtlsPacketHash(dtls_flight4),
              }));

  // Post-handshake ACK
  EXPECT_CALL(*this, ClientCompleteCallback);
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_CALL(*this, ServerCompleteCallback);
  SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::COMPLETE);
  EXPECT_EQ(client_.state(), State::COMPLETE);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_RESPONSE), std::nullopt);
  EXPECT_EQ(client_.GetAckToPiggyback(STUN_BINDING_REQUEST), std::nullopt);
}

TEST_F(DtlsStunPiggybackControllerTest, UnwrappedHandshakeAckData) {
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
            std::vector<uint32_t>({}));
  EXPECT_EQ(client_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
            std::vector<uint32_t>({}));

  // Flight 1+2 (embedded)
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({ComputeDtlsPacketHash(dtls_flight1)}));
  EXPECT_THAT(*client_.GetAckToPiggyback(STUN_BINDING_RESPONSE),
              ElementsAreArray({ComputeDtlsPacketHash(dtls_flight2)}));

  // Flight 3+4 (not embedded)
  SendClientToServerDtls(dtls_flight3);
  SendServerToClientDtls(dtls_flight4);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight1),
                  ComputeDtlsPacketHash(dtls_flight3),
              }));
  EXPECT_THAT(*client_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight2),
                  ComputeDtlsPacketHash(dtls_flight4),
              }));

  // Post-handshake ACK
  EXPECT_CALL(*this, ClientCompleteCallback);
  SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST);
  EXPECT_CALL(*this, ServerCompleteCallback);
  SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE);
  EXPECT_EQ(server_.state(), State::COMPLETE);
  EXPECT_EQ(client_.state(), State::COMPLETE);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_RESPONSE), std::nullopt);
  EXPECT_EQ(client_.GetAckToPiggyback(STUN_BINDING_REQUEST), std::nullopt);
}

TEST_F(DtlsStunPiggybackControllerTest, AckDataNoDuplicates) {
  // Flight 1+2
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({ComputeDtlsPacketHash(dtls_flight1)}));
  SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight1),
                  ComputeDtlsPacketHash(dtls_flight3),
              }));

  // Receive Flight 1 again, no change expected.
  SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight1),
                  ComputeDtlsPacketHash(dtls_flight3),
              }));
}

TEST_F(DtlsStunPiggybackControllerTest, AckDataNoDuplicatesFromDualReporting) {
  std::unique_ptr<StunByteStringAttribute> attr_data =
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight1);
  std::unique_ptr<StunByteStringAttribute> attr_ack;
  if (auto ack = client_.GetAckToPiggyback(STUN_BINDING_REQUEST)) {
    attr_ack = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN_ACK, *ack);
  }
  ASSERT_THAT(attr_ack, NotNull());
  server_.ReportDataPiggybacked(attr_data->array_view(),
                                FromAckAttribute(attr_ack->array_view()));
  server_.ReportDtlsPacket(dtls_flight1);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({ComputeDtlsPacketHash(dtls_flight1)}));
}

TEST_F(DtlsStunPiggybackControllerTest, IgnoresNonDtlsData) {
  std::vector<uint8_t> ascii = {0x64, 0x72, 0x6f, 0x70, 0x6d, 0x65};

  EXPECT_CALL(*this, ServerPacketSink).Times(0);
  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, ascii)->array_view(),
      std::nullopt);
  EXPECT_EQ(0, server_.GetCountOfReceivedData());
}

TEST_F(DtlsStunPiggybackControllerTest, DontSendAckedPackets) {
  server_.CapturePacket(dtls_flight1);
  server_.Flush();
  EXPECT_TRUE(server_.GetDataToPiggyback(STUN_BINDING_REQUEST).has_value());
  server_.ReportDataPiggybacked(
      std::nullopt,
      std::vector<uint32_t>({ComputeDtlsPacketHash(dtls_flight1)}));
  // No unacked packet exists.
  EXPECT_FALSE(server_.GetDataToPiggyback(STUN_BINDING_REQUEST).has_value());
}

TEST_F(DtlsStunPiggybackControllerTest, LimitAckSize) {
  std::vector<uint8_t> dtls_flight5 = FakeDtlsPacket(0x5487);

  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight1)->array_view(),
      std::nullopt);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 1u);
  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight2)->array_view(),
      std::nullopt);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 2u);
  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight3)->array_view(),
      std::nullopt);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 3u);
  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight4)->array_view(),
      std::nullopt);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 4u);

  // Limit size of ack so that it does not grow unbounded.
  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight5)->array_view(),
      std::nullopt);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(),
            DtlsStunPiggybackController::kMaxAckSize);
  EXPECT_THAT(*server_.GetAckToPiggyback(STUN_BINDING_REQUEST),
              ElementsAreArray({
                  ComputeDtlsPacketHash(dtls_flight2),
                  ComputeDtlsPacketHash(dtls_flight3),
                  ComputeDtlsPacketHash(dtls_flight4),
                  ComputeDtlsPacketHash(dtls_flight5),
              }));
}

TEST_F(DtlsStunPiggybackControllerTest, EmptyDataDoesNotClearAck) {
  std::vector<uint8_t> dtls_flight5 = FakeDtlsPacket(0x5487);

  server_.ReportDataPiggybacked(
      WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight1)->array_view(),
      std::nullopt);
  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 1u);

  // The fact that we don't get any data does not mean that
  // we can clear the ack list.
  // a) packets can be arbitrary reordered.
  // b) the peer might be needing 2 packets (ie. pqc) to produce
  // a return packet and only one of them has arrived.
  server_.ReportDataPiggybacked(
      std::nullopt,
      std::vector<uint32_t>({ComputeDtlsPacketHash(dtls_flight1)}));

  EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 1u);
}

TEST_F(DtlsStunPiggybackControllerTest, MultiPacketRoundRobin) {
  // Let's pretend that a flight is 3 packets...
  server_.CapturePacket(dtls_flight1);
  server_.CapturePacket(dtls_flight2);
  server_.CapturePacket(dtls_flight3);
  server_.Flush();
  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight1.begin(), dtls_flight1.end()));
  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight2.begin(), dtls_flight2.end()));
  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight3.begin(), dtls_flight3.end()));

  server_.ReportDataPiggybacked(
      std::nullopt,
      std::vector<uint32_t>({ComputeDtlsPacketHash(dtls_flight1)}));

  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight2.begin(), dtls_flight2.end()));
  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight3.begin(), dtls_flight3.end()));

  server_.ReportDataPiggybacked(
      std::nullopt,
      std::vector<uint32_t>({ComputeDtlsPacketHash(dtls_flight3)}));

  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight2.begin(), dtls_flight2.end()));
  EXPECT_EQ(server_.GetDataToPiggyback(STUN_BINDING_REQUEST),
            std::string(dtls_flight2.begin(), dtls_flight2.end()));
}

TEST_F(DtlsStunPiggybackControllerTest, DuplicateAck) {
  server_.CapturePacket(dtls_flight1);
  server_.Flush();
  server_.ReportDataPiggybacked(
      std::nullopt,
      std::vector<uint32_t>({ComputeDtlsPacketHash(dtls_flight1),
                             ComputeDtlsPacketHash(dtls_flight1)}));
}

}  // namespace webrtc
