/*
 *  Copyright (c) 2026 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 "modules/video_coding/sframe_packet_buffer.h"

#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <variant>
#include <vector>

#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/sframe_descriptor.h"
#include "modules/rtp_rtcp/source/sframe_rtp_packet_received.h"
#include "test/gtest.h"

namespace webrtc {
namespace {

template <class... Ts>
struct overloaded : Ts... {
  using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

constexpr size_t kBufferSize = 16;

class SFramePacketBufferTest : public ::testing::Test {
 protected:
  SFramePacketBufferTest() : buffer_(kBufferSize) {}

  enum IsStart { kNotStart, kStart };
  enum IsEnd { kNotEnd, kEnd };

  std::unique_ptr<SframeRtpPacketReceived> MakePacket(
      uint16_t seq_num,
      IsStart start,
      IsEnd end,
      uint32_t timestamp = 1000,
      uint8_t payload_type = 96,
      SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame) {
    auto rtp = std::make_unique<RtpPacketReceived>();
    rtp->SetSequenceNumber(seq_num);
    rtp->SetTimestamp(timestamp);
    rtp->SetPayloadType(payload_type);
    SFrameDescriptor desc;
    desc.start = (start == kStart);
    desc.end = (end == kEnd);
    desc.encryption_level = encryption_level;
    return std::make_unique<SframeRtpPacketReceived>(std::move(rtp), desc);
  }
  struct TestResult {
    SFramePacketBuffer::InsertResult status =
        SFramePacketBuffer::InsertResult::kNoFrame;
    SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame;
    std::vector<std::unique_ptr<RtpPacketReceived>> packets;
  };

  bool BufferCleared(SFramePacketBuffer::InsertResult r) {
    return r == SFramePacketBuffer::InsertResult::kBufferCleared;
  }

  TestResult InsertInto(SFramePacketBuffer& buf,
                        std::unique_ptr<SframeRtpPacketReceived> packet) {
    auto insert_result = buf.InsertPacket(std::move(packet));
    return std::visit(
        overloaded{
            [](SFramePacketBuffer::AssembledFrame& frame) -> TestResult {
              return {.encryption_level = frame.encryption_level,
                      .packets = std::move(frame.packets)};
            },
            [](SFramePacketBuffer::InsertResult& status) -> TestResult {
              return {.status = status};
            },
        },
        insert_result);
  }

  TestResult Insert(
      uint16_t seq_num,
      IsStart start,
      IsEnd end,
      uint32_t timestamp = 1000,
      uint8_t payload_type = 96,
      SframeEncryptionLevel encryption_level = SframeEncryptionLevel::kFrame) {
    return InsertInto(buffer_, MakePacket(seq_num, start, end, timestamp,
                                          payload_type, encryption_level));
  }

  SFramePacketBuffer buffer_;
};

TEST_F(SFramePacketBufferTest, SinglePacketFrame) {
  auto result = Insert(100, kStart, kEnd);
  ASSERT_EQ(result.packets.size(), 1u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 100);
}

TEST_F(SFramePacketBufferTest, TwoPacketFrame) {
  EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
  auto result = Insert(101, kNotStart, kEnd);
  ASSERT_EQ(result.packets.size(), 2u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 100);
  EXPECT_EQ(result.packets[1]->SequenceNumber(), 101);
}

TEST_F(SFramePacketBufferTest, ThreePacketFrame) {
  EXPECT_TRUE(Insert(200, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(201, kNotStart, kNotEnd).packets.empty());
  auto result = Insert(202, kNotStart, kEnd);
  ASSERT_EQ(result.packets.size(), 3u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 200);
  EXPECT_EQ(result.packets[1]->SequenceNumber(), 201);
  EXPECT_EQ(result.packets[2]->SequenceNumber(), 202);
}

TEST_F(SFramePacketBufferTest, TwoPacketFrameReversed) {
  EXPECT_TRUE(Insert(101, kNotStart, kEnd).packets.empty());
  auto result = Insert(100, kStart, kNotEnd);
  ASSERT_EQ(result.packets.size(), 2u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 100);
  EXPECT_EQ(result.packets[1]->SequenceNumber(), 101);
}

TEST_F(SFramePacketBufferTest, ThreePacketFrameMiddleLast) {
  EXPECT_TRUE(Insert(300, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(302, kNotStart, kEnd).packets.empty());
  auto result = Insert(301, kNotStart, kNotEnd);
  ASSERT_EQ(result.packets.size(), 3u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 300);
  EXPECT_EQ(result.packets[1]->SequenceNumber(), 301);
  EXPECT_EQ(result.packets[2]->SequenceNumber(), 302);
}

TEST_F(SFramePacketBufferTest, TwoConsecutiveSinglePacketFrames) {
  auto r1 = Insert(50, kStart, kEnd, /*timestamp=*/1000);
  ASSERT_EQ(r1.packets.size(), 1u);

  auto r2 = Insert(51, kStart, kEnd, /*timestamp=*/2000);
  ASSERT_EQ(r2.packets.size(), 1u);
  EXPECT_EQ(r2.packets[0]->SequenceNumber(), 51);
}

TEST_F(SFramePacketBufferTest, TwoFramesBackToBack) {
  // Frame 1: seq 10-11
  EXPECT_TRUE(Insert(10, kStart, kNotEnd, /*timestamp=*/100).packets.empty());
  auto r1 = Insert(11, kNotStart, kEnd, /*timestamp=*/100);
  ASSERT_EQ(r1.packets.size(), 2u);

  // Frame 2: seq 12-13
  EXPECT_TRUE(Insert(12, kStart, kNotEnd, /*timestamp=*/200).packets.empty());
  auto r2 = Insert(13, kNotStart, kEnd, /*timestamp=*/200);
  ASSERT_EQ(r2.packets.size(), 2u);
}

TEST_F(SFramePacketBufferTest, IncompleteFrameMissingEnd) {
  EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(101, kNotStart, kNotEnd).packets.empty());
  // Neither packet completes a frame, but completing the frame later works.
  auto result = Insert(102, kNotStart, kEnd);
  EXPECT_EQ(result.packets.size(), 3u);
}

TEST_F(SFramePacketBufferTest, IncompleteFrameMissingStart) {
  EXPECT_TRUE(Insert(101, kNotStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(102, kNotStart, kEnd).packets.empty());
  // Packets are buffered but no S-bit found, so adding it completes the frame.
  auto result = Insert(100, kStart, kNotEnd);
  EXPECT_EQ(result.packets.size(), 3u);
}

TEST_F(SFramePacketBufferTest, IncompleteFrameGap) {
  // S-bit at 100, gap at 101, E-bit at 102.
  EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(102, kNotStart, kEnd).packets.empty());
  // Filling the gap completes the frame.
  auto result = Insert(101, kNotStart, kNotEnd);
  EXPECT_EQ(result.packets.size(), 3u);
}

TEST_F(SFramePacketBufferTest, DuplicatePacketDropped) {
  EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(100, kStart, kNotEnd).packets.empty());
  // Original packet still in buffer -- completing the frame proves it.
  auto result = Insert(101, kNotStart, kEnd);
  EXPECT_EQ(result.packets.size(), 2u);
}

TEST_F(SFramePacketBufferTest, TBitMismatchDropsFrame) {
  EXPECT_TRUE(
      Insert(100, kStart, kNotEnd, 1000, 96, SframeEncryptionLevel::kFrame)
          .packets.empty());
  auto result =
      Insert(101, kNotStart, kEnd, 1000, 96, SframeEncryptionLevel::kPacket);
  EXPECT_TRUE(result.packets.empty());
  // Dropped packets don't block subsequent frames.
  auto r2 = Insert(102, kStart, kEnd, /*timestamp=*/2000);
  EXPECT_FALSE(r2.packets.empty());
}

TEST_F(SFramePacketBufferTest, PayloadTypeMismatchDropsFrame) {
  EXPECT_TRUE(
      Insert(100, kStart, kNotEnd, 1000, /*payload_type=*/96).packets.empty());
  auto result = Insert(101, kNotStart, kEnd, 1000, /*payload_type=*/97);
  EXPECT_TRUE(result.packets.empty());
  auto r2 = Insert(102, kStart, kEnd, /*timestamp=*/2000);
  EXPECT_FALSE(r2.packets.empty());
}

TEST_F(SFramePacketBufferTest, TimestampMismatchDropsFrame) {
  EXPECT_TRUE(Insert(100, kStart, kNotEnd, /*timestamp=*/1000).packets.empty());
  auto result = Insert(101, kNotStart, kEnd, /*timestamp=*/2000);
  EXPECT_TRUE(result.packets.empty());
  auto r2 = Insert(102, kStart, kEnd, /*timestamp=*/3000);
  EXPECT_FALSE(r2.packets.empty());
}

TEST_F(SFramePacketBufferTest, PacketizedBitPropagated) {
  auto result =
      Insert(100, kStart, kEnd, 1000, 96, SframeEncryptionLevel::kPacket);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.encryption_level, SframeEncryptionLevel::kPacket);
}

TEST_F(SFramePacketBufferTest, NonPacketizedBitPropagated) {
  auto result =
      Insert(100, kStart, kEnd, 1000, 96, SframeEncryptionLevel::kFrame);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.encryption_level, SframeEncryptionLevel::kFrame);
}

TEST_F(SFramePacketBufferTest, AssembledPacketsRemovedFromBuffer) {
  auto r1 = Insert(100, kStart, kEnd);
  ASSERT_FALSE(r1.packets.empty());
  // Slot is freed after assembly. Re-inserting assembles a new frame.
  auto r2 = Insert(100, kStart, kEnd);
  ASSERT_FALSE(r2.packets.empty());
  EXPECT_EQ(r2.packets.size(), 1u);
}

TEST_F(SFramePacketBufferTest, MultiPacketFrameRemovedFromBuffer) {
  Insert(100, kStart, kNotEnd);
  Insert(101, kNotStart, kNotEnd);
  auto result = Insert(102, kNotStart, kEnd);
  ASSERT_EQ(result.packets.size(), 3u);
  // Slots freed -- a new frame at same seq range can be inserted.
  auto r2 = Insert(100, kStart, kEnd, /*timestamp=*/9000);
  EXPECT_FALSE(r2.packets.empty());
}

TEST_F(SFramePacketBufferTest, ClearRemovesAll) {
  Insert(100, kStart, kNotEnd);
  Insert(101, kNotStart, kNotEnd);

  buffer_.Clear();
  // After clear, inserting a new single-packet frame works (no stale data).
  auto result = Insert(200, kStart, kEnd, /*timestamp=*/5000);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 200);
}

TEST_F(SFramePacketBufferTest, ClearToRemovesOldPackets) {
  Insert(100, kStart, kNotEnd, /*timestamp=*/1000);
  Insert(101, kNotStart, kNotEnd, /*timestamp=*/1000);
  Insert(102, kNotStart, kNotEnd, /*timestamp=*/1000);

  buffer_.ClearTo(101);
  // Packets 100-101 cleared, packet 102 remains.
  // Completing a frame starting from 102 should work.
  auto result = Insert(103, kNotStart, kEnd, /*timestamp=*/1000);
  // 102 has no S-bit and 100-101 are gone, so this can't assemble.
  EXPECT_TRUE(result.packets.empty());
}

TEST_F(SFramePacketBufferTest, ClearToIsInclusive) {
  Insert(100, kStart, kEnd, /*timestamp=*/1000);
  // Frame was assembled, buffer is empty. Insert new incomplete frame.
  Insert(101, kStart, kNotEnd, /*timestamp=*/2000);
  Insert(102, kNotStart, kNotEnd, /*timestamp=*/2000);

  buffer_.ClearTo(102);
  // Both 101 and 102 should be cleared. A new frame after works.
  auto result = Insert(103, kStart, kEnd, /*timestamp=*/3000);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 103);
}

TEST_F(SFramePacketBufferTest, ClearToBeforeFirstPacketIsNoop) {
  Insert(100, kStart, kNotEnd);

  // ClearTo a seq_num before the first received packet should be a no-op.
  buffer_.ClearTo(50);
  // Packet 100 is still there -- completing the frame proves it.
  auto result = Insert(101, kNotStart, kEnd);
  ASSERT_EQ(result.packets.size(), 2u);
}

TEST_F(SFramePacketBufferTest, SeqNumWraparound) {
  uint16_t seq = 0xFFFE;
  EXPECT_TRUE(Insert(seq, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(seq + 1, kNotStart, kNotEnd).packets.empty());
  // seq + 2 wraps to 0.
  auto result = Insert(static_cast<uint16_t>(seq + 2), kNotStart, kEnd);
  ASSERT_EQ(result.packets.size(), 3u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 0xFFFE);
  EXPECT_EQ(result.packets[1]->SequenceNumber(), 0xFFFF);
  EXPECT_EQ(result.packets[2]->SequenceNumber(), 0x0000);
}

TEST_F(SFramePacketBufferTest, SinglePacketAtWraparound) {
  auto result = Insert(0xFFFF, kStart, kEnd);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 0xFFFF);
}

TEST_F(SFramePacketBufferTest, ReorderedPacketUnwrapsToNegative) {
  // First packet at seq 2 unwraps to 2.  Then a reordered packet at raw seq
  // 65535 unwraps to -1 (backward by 3 is shorter than forward by 65533).
  // ToIdx must handle the negative unwrapped value correctly.
  EXPECT_TRUE(Insert(2, kNotStart, kEnd).packets.empty());
  auto result = Insert(0xFFFF, kStart, kNotEnd);
  EXPECT_TRUE(result.packets.empty());
  // Both packets are buffered — insert the middle to complete the frame.
  auto r2 = Insert(0, kNotStart, kNotEnd);
  EXPECT_TRUE(r2.packets.empty());
  auto r3 = Insert(1, kNotStart, kNotEnd);
  ASSERT_EQ(r3.packets.size(), 4u);
  EXPECT_EQ(r3.packets[0]->SequenceNumber(), 0xFFFF);
  EXPECT_EQ(r3.packets[3]->SequenceNumber(), 2);
}

TEST_F(SFramePacketBufferTest, CollisionClearsBuffer) {
  // seq 0 and seq 16 both map to slot 0 in a size-16 buffer.
  EXPECT_TRUE(Insert(0, kStart, kNotEnd, /*timestamp=*/100).packets.empty());
  // Insert 16 which collides with slot 0. Should clear the buffer.
  auto result = Insert(16, kStart, kNotEnd, /*timestamp=*/200);
  EXPECT_TRUE(BufferCleared(result.status));
}

TEST_F(SFramePacketBufferTest, BufferClearedOnCollision) {
  // Create a small buffer so collisions happen quickly.
  SFramePacketBuffer small_buffer(4);

  // Fill all 4 slots with non-colliding packets.
  small_buffer.InsertPacket(MakePacket(0, kStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(1, kNotStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(2, kNotStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(3, kNotStart, kNotEnd, 100));

  // Insert packet that collides (seq 4 maps to slot 0 in size-4 buffer).
  // Should clear and re-insert.
  auto result = small_buffer.InsertPacket(MakePacket(4, kStart, kEnd, 200));
  // Buffer cleared — caller should request a keyframe.
  ASSERT_TRUE(std::holds_alternative<SFramePacketBuffer::InsertResult>(result));
  EXPECT_EQ(std::get<SFramePacketBuffer::InsertResult>(result),
            SFramePacketBuffer::InsertResult::kBufferCleared);
}

TEST_F(SFramePacketBufferTest, VeryOldPacketDropped) {
  // Insert a packet to establish the window.
  Insert(1000, kStart, kEnd);

  // Try to insert a packet far before the window. With buffer_size=16,
  // any packet >= 16 positions back is dropped.
  auto result = Insert(900, kStart, kEnd);
  // Packet is dropped (too old), no assembly.
  EXPECT_TRUE(result.packets.empty());
}

TEST_F(SFramePacketBufferTest, TimestampPreservedInResult) {
  auto result = Insert(100, kStart, kEnd, /*timestamp=*/42000);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->Timestamp(), 42000u);
}

TEST_F(SFramePacketBufferTest, PayloadTypePreservedInResult) {
  auto result = Insert(100, kStart, kEnd, 1000, /*payload_type=*/111);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->PayloadType(), 111);
}

TEST_F(SFramePacketBufferTest, InterleavedFramesAssembleIndependently) {
  // Frame A: seq 10-11 (ts 1000), Frame B: seq 12-13 (ts 2000).
  // Insert them interleaved: A-start, B-start, A-end, B-end.
  EXPECT_TRUE(Insert(10, kStart, kNotEnd, /*timestamp=*/1000).packets.empty());
  EXPECT_TRUE(Insert(12, kStart, kNotEnd, /*timestamp=*/2000).packets.empty());

  // Completing frame A.
  auto r1 = Insert(11, kNotStart, kEnd, /*timestamp=*/1000);
  ASSERT_EQ(r1.packets.size(), 2u);
  EXPECT_EQ(r1.packets[0]->SequenceNumber(), 10);

  // Completing frame B.
  auto r2 = Insert(13, kNotStart, kEnd, /*timestamp=*/2000);
  ASSERT_EQ(r2.packets.size(), 2u);
  EXPECT_EQ(r2.packets[0]->SequenceNumber(), 12);
}

TEST_F(SFramePacketBufferTest, FrameAfterDroppedFrameStillAssembles) {
  // Frame 1 with timestamp mismatch -> dropped.
  Insert(100, kStart, kNotEnd, /*timestamp=*/1000);
  Insert(101, kNotStart, kEnd, /*timestamp=*/9999);  // mismatch

  // Frame 2 is valid.
  auto result = Insert(102, kStart, kEnd, /*timestamp=*/2000);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 102);
}

TEST_F(SFramePacketBufferTest, ClearToPreventsLatePacketFromMovingWindow) {
  // Insert and assemble frame at seq 100.
  auto r1 = Insert(100, kStart, kEnd, /*timestamp=*/1000);
  ASSERT_FALSE(r1.packets.empty());

  // Advance the window past seq 105.
  Insert(101, kStart, kNotEnd, /*timestamp=*/2000);
  Insert(102, kNotStart, kNotEnd, /*timestamp=*/2000);
  Insert(103, kNotStart, kNotEnd, /*timestamp=*/2000);
  Insert(104, kNotStart, kNotEnd, /*timestamp=*/2000);
  Insert(105, kNotStart, kNotEnd, /*timestamp=*/2000);
  buffer_.ClearTo(105);

  // A late packet from before the cleared point should be dropped.
  auto late = Insert(103, kStart, kEnd, /*timestamp=*/2000);
  EXPECT_TRUE(late.packets.empty());
}

TEST_F(SFramePacketBufferTest, ClearToAllowsNewPacketsAfterClearedPoint) {
  Insert(100, kStart, kEnd, /*timestamp=*/1000);
  buffer_.ClearTo(100);

  // Packet after the cleared point should work fine.
  auto result = Insert(110, kStart, kEnd, /*timestamp=*/2000);
  ASSERT_FALSE(result.packets.empty());
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 110);
}

TEST_F(SFramePacketBufferTest, ClearToDoesNotWipeNewerPacket) {
  // Insert two frames in non-adjacent slots.
  auto r1 = Insert(0, kStart, kEnd, /*timestamp=*/1000);
  ASSERT_FALSE(r1.packets.empty());

  // Packet in a higher slot (2 * kBufferSize away to avoid aliasing).
  Insert(2 * kBufferSize, kStart, kNotEnd, /*timestamp=*/3000);

  // ClearTo the first frame — must not affect the second.
  buffer_.ClearTo(0);

  // Completing the second frame still works.
  auto r2 = Insert(2 * kBufferSize + 1, kNotStart, kEnd, /*timestamp=*/3000);
  ASSERT_EQ(r2.packets.size(), 2u);
  EXPECT_EQ(r2.packets[0]->SequenceNumber(), 2 * kBufferSize);
}

TEST_F(SFramePacketBufferTest, FreedSlotsReuseFullBuffer) {
  // Use a small buffer where we can fill it entirely.
  SFramePacketBuffer small_buffer(4);

  // Frame 1: 3 packets, assembles and frees slots.
  small_buffer.InsertPacket(MakePacket(10, kStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(11, kNotStart, kNotEnd, 100));
  auto r1 = InsertInto(small_buffer, MakePacket(12, kNotStart, kEnd, 100));
  ASSERT_EQ(r1.packets.size(), 3u);

  // Frame 2: fills all 4 slots using the freed space.
  small_buffer.InsertPacket(MakePacket(13, kStart, kNotEnd, 200));
  small_buffer.InsertPacket(MakePacket(14, kNotStart, kNotEnd, 200));
  small_buffer.InsertPacket(MakePacket(15, kNotStart, kNotEnd, 200));
  auto r2 = InsertInto(small_buffer, MakePacket(16, kNotStart, kEnd, 200));
  ASSERT_EQ(r2.packets.size(), 4u);
  EXPECT_FALSE(BufferCleared(r2.status));
}

TEST_F(SFramePacketBufferTest, FramesAfterClearToWithReordering) {
  // Assemble and clear past a frame.
  Insert(100, kStart, kEnd, /*timestamp=*/1000);
  Insert(101, kStart, kEnd, /*timestamp=*/2000);
  buffer_.ClearTo(101);

  // Insert a later frame first (out of order).
  auto r1 = Insert(110, kStart, kEnd, /*timestamp=*/4000);
  ASSERT_FALSE(r1.packets.empty());
  EXPECT_EQ(r1.packets[0]->SequenceNumber(), 110);

  // Insert a frame between the cleared point and the later frame.
  auto r2 = Insert(105, kStart, kEnd, /*timestamp=*/3000);
  ASSERT_FALSE(r2.packets.empty());
  EXPECT_EQ(r2.packets[0]->SequenceNumber(), 105);
}

TEST_F(SFramePacketBufferTest, TwoStartsWithoutEnd) {
  // S(101) followed by S(102) — two starts, no complete frame.
  EXPECT_TRUE(Insert(101, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(102, kStart, kNotEnd, /*timestamp=*/2000).packets.empty());
  // Adding end for second "frame" (102-103) should assemble.
  auto result = Insert(103, kNotStart, kEnd, /*timestamp=*/2000);
  ASSERT_EQ(result.packets.size(), 2u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 102);
  EXPECT_EQ(result.packets[1]->SequenceNumber(), 103);
}

TEST_F(SFramePacketBufferTest, TwoEndsWithoutStart) {
  // Two E-bit packets with no preceding S-bit — no frame assembles.
  EXPECT_TRUE(Insert(100, kNotStart, kEnd).packets.empty());
  EXPECT_TRUE(Insert(101, kNotStart, kEnd, /*timestamp=*/2000).packets.empty());
}

TEST_F(SFramePacketBufferTest, EFollowedByE) {
  // S at 100, E at 101, then another E at 102 with different timestamp.
  auto r1 = Insert(100, kStart, kNotEnd, /*timestamp=*/1000);
  EXPECT_TRUE(r1.packets.empty());
  auto r2 = Insert(101, kNotStart, kEnd, /*timestamp=*/1000);
  ASSERT_EQ(r2.packets.size(), 2u);
  // Orphan E at 102 — no start found.
  auto r3 = Insert(102, kNotStart, kEnd, /*timestamp=*/2000);
  EXPECT_TRUE(r3.packets.empty());
}

TEST_F(SFramePacketBufferTest, TBitMismatchInMiddlePacketDropsFrame) {
  // 3-packet frame where the middle packet has a mismatched T-bit.
  EXPECT_TRUE(
      Insert(100, kStart, kNotEnd, 1000, 96, SframeEncryptionLevel::kFrame)
          .packets.empty());
  EXPECT_TRUE(
      Insert(101, kNotStart, kNotEnd, 1000, 96, SframeEncryptionLevel::kPacket)
          .packets.empty());
  auto result =
      Insert(102, kNotStart, kEnd, 1000, 96, SframeEncryptionLevel::kFrame);
  EXPECT_TRUE(result.packets.empty());
  // All slots must be freed — re-inserting a fresh frame at same seqs works.
  auto r2 = Insert(100, kStart, kEnd, /*timestamp=*/5000);
  ASSERT_EQ(r2.packets.size(), 1u);
}

TEST_F(SFramePacketBufferTest, FrameWrapsAroundRingBuffer) {
  // With buffer_size=16, place S near the end and E past the wrap.
  // Seq 14 → slot 14, seq 15 → slot 15, seq 16 → slot 0.
  EXPECT_TRUE(Insert(14, kStart, kNotEnd).packets.empty());
  EXPECT_TRUE(Insert(15, kNotStart, kNotEnd).packets.empty());
  auto result = Insert(16, kNotStart, kEnd);
  ASSERT_EQ(result.packets.size(), 3u);
  EXPECT_EQ(result.packets[0]->SequenceNumber(), 14);
  EXPECT_EQ(result.packets[2]->SequenceNumber(), 16);
}

TEST_F(SFramePacketBufferTest, CollisionDuringMultiPacketFrameClearsBuffer) {
  // Start a multi-packet frame.
  EXPECT_TRUE(Insert(0, kStart, kNotEnd, /*timestamp=*/100).packets.empty());
  EXPECT_TRUE(Insert(1, kNotStart, kNotEnd, /*timestamp=*/100).packets.empty());
  // Insert a packet that collides (seq 16 → slot 0). Buffer clears.
  auto result = Insert(16, kStart, kNotEnd, /*timestamp=*/200);
  EXPECT_TRUE(BufferCleared(result.status));
}

TEST_F(SFramePacketBufferTest, BufferFullBeforeEndPacket) {
  // Small buffer: S and middle packets fill it, E arrives too late.
  SFramePacketBuffer small_buffer(4);
  small_buffer.InsertPacket(MakePacket(0, kStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(1, kNotStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(2, kNotStart, kNotEnd, 100));
  small_buffer.InsertPacket(MakePacket(3, kNotStart, kNotEnd, 100));
  // Packet 4 (the end) collides with slot 0; buffer clears.
  auto result = InsertInto(small_buffer, MakePacket(4, kNotStart, kEnd, 100));
  // Buffer cleared — the frame is lost.
  EXPECT_TRUE(BufferCleared(result.status));
  // Only packet 4 remains; it has no S-bit so no frame assembles.
  EXPECT_TRUE(result.packets.empty());
}

}  // namespace
}  // namespace webrtc
