// Copyright 2018 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 "components/mirroring/service/remoting_sender.h"

#include <utility>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/default_tick_clock.h"
#include "media/cast/constants.h"
#include "media/cast/net/cast_transport.h"
#include "media/cast/test/utility/default_config.h"
#include "media/mojo/interfaces/remoting.mojom.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace mirroring {

namespace {

// Data pipe capacity is 1KB.
constexpr int kDataPipeCapacity = 1024;

// Implements the CastTransport interface to capture output from the
// RemotingSender.
class FakeTransport : public media::cast::CastTransport {
 public:
  FakeTransport() {}
  ~FakeTransport() final {}

  void TakeSentFrames(std::vector<media::cast::EncodedFrame>* frames) {
    frames->swap(sent_frames_);
    sent_frames_.clear();
  }

  void TakeCanceledFrameIds(std::vector<media::cast::FrameId>* frame_ids) {
    frame_ids->swap(canceled_frame_ids_);
    canceled_frame_ids_.clear();
  }

  media::cast::FrameId WaitForKickstart() {
    base::RunLoop run_loop;
    kickstarted_callback_ = run_loop.QuitClosure();
    run_loop.Run();
    return kickstarted_frame_id_;
  }

 protected:
  void InsertFrame(uint32_t ssrc,
                   const media::cast::EncodedFrame& frame) final {
    sent_frames_.push_back(frame);
  }

  void CancelSendingFrames(
      uint32_t ssrc,
      const std::vector<media::cast::FrameId>& frame_ids) final {
    for (media::cast::FrameId frame_id : frame_ids)
      canceled_frame_ids_.push_back(frame_id);
  }

  void ResendFrameForKickstart(uint32_t ssrc,
                               media::cast::FrameId frame_id) final {
    kickstarted_frame_id_ = frame_id;
    if (!kickstarted_callback_.is_null())
      std::move(kickstarted_callback_).Run();
  }

  // The rest of the interface is not used for these tests.
  void SendSenderReport(
      uint32_t ssrc,
      base::TimeTicks current_time,
      media::cast::RtpTimeTicks current_time_as_rtp_timestamp) final {}
  void AddValidRtpReceiver(uint32_t rtp_sender_ssrc,
                           uint32_t rtp_receiver_ssrc) final {}
  void InitializeRtpReceiverRtcpBuilder(
      uint32_t rtp_receiver_ssrc,
      const media::cast::RtcpTimeData& time_data) final {}
  void AddCastFeedback(const media::cast::RtcpCastMessage& cast_message,
                       base::TimeDelta target_delay) final {}
  void AddPli(const media::cast::RtcpPliMessage& pli_message) final {}
  void AddRtcpEvents(
      const media::cast::ReceiverRtcpEventSubscriber::RtcpEvents& e) final {}
  void AddRtpReceiverReport(const media::cast::RtcpReportBlock& b) final {}
  void SendRtcpFromRtpReceiver() final {}
  void SetOptions(const base::DictionaryValue& options) final {}

 private:
  std::vector<media::cast::EncodedFrame> sent_frames_;
  std::vector<media::cast::FrameId> canceled_frame_ids_;

  base::RepeatingClosure kickstarted_callback_;
  media::cast::FrameId kickstarted_frame_id_;

  DISALLOW_COPY_AND_ASSIGN(FakeTransport);
};

}  // namespace

class RemotingSenderTest : public ::testing::Test {
 protected:
  RemotingSenderTest()
      : cast_environment_(new media::cast::CastEnvironment(
            base::DefaultTickClock::GetInstance(),
            scoped_task_environment_.GetMainThreadTaskRunner(),
            scoped_task_environment_.GetMainThreadTaskRunner(),
            scoped_task_environment_.GetMainThreadTaskRunner())),
        expecting_error_callback_run_(false),
        receiver_ssrc_(-1) {
    const MojoCreateDataPipeOptions data_pipe_options{
        sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1,
        kDataPipeCapacity};
    mojo::ScopedDataPipeConsumerHandle consumer_end;
    CHECK_EQ(MOJO_RESULT_OK,
             mojo::CreateDataPipe(&data_pipe_options, &producer_end_,
                                  &consumer_end));

    media::cast::FrameSenderConfig video_config =
        media::cast::GetDefaultVideoSenderConfig();
    video_config.rtp_payload_type = media::cast::RtpPayloadType::REMOTE_VIDEO;
    video_config.codec = media::cast::CODEC_VIDEO_REMOTE;
    receiver_ssrc_ = video_config.receiver_ssrc;
    remoting_sender_ = std::make_unique<RemotingSender>(
        cast_environment_, &transport_, video_config, std::move(consumer_end),
        mojo::MakeRequest(&sender_),
        base::BindOnce(
            [](bool expecting_error_callback_run) {
              CHECK(expecting_error_callback_run);
            },
            expecting_error_callback_run_));

    // Give CastRemotingSender a small RTT measurement to prevent kickstart
    // testing from taking too long.
    remoting_sender_->OnMeasuredRoundTripTime(
        base::TimeDelta::FromMilliseconds(1));
    RunPendingTasks();
  }

  ~RemotingSenderTest() override {}

  void TearDown() final {
    remoting_sender_.reset();
    // Allow any pending tasks to run before destruction.
    RunPendingTasks();
  }

  // Allow pending tasks, such as Mojo method calls, to execute.
  void RunPendingTasks() { scoped_task_environment_.RunUntilIdle(); }

 protected:
  media::cast::FrameId latest_acked_frame_id() const {
    return remoting_sender_->latest_acked_frame_id_;
  }

  int NumberOfFramesInFlight() {
    return remoting_sender_->GetUnacknowledgedFrameCount();
  }

  size_t GetSizeOfNextFrameData() {
    return remoting_sender_->next_frame_data_.size();
  }

  bool IsFlowRestartPending() const {
    return remoting_sender_->flow_restart_pending_;
  }

  bool ProduceDataChunk(size_t offset, size_t size) WARN_UNUSED_RESULT {
    std::vector<uint8_t> fake_chunk(size);
    for (size_t i = 0; i < size; ++i)
      fake_chunk[i] = static_cast<uint8_t>(offset + i);
    uint32_t num_bytes = fake_chunk.size();
    return producer_end_->WriteData(fake_chunk.data(), &num_bytes,
                                    MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) ==
           MOJO_RESULT_OK;
  }

  void SendFrame(uint32_t size) { remoting_sender_->SendFrame(size); }

  void CancelInFlightData() { remoting_sender_->CancelInFlightData(); }

  void TakeSentFrames(std::vector<media::cast::EncodedFrame>* frames) {
    transport_.TakeSentFrames(frames);
  }

  bool ExpectOneFrameWasSent(size_t expected_payload_size) {
    std::vector<media::cast::EncodedFrame> frames;
    transport_.TakeSentFrames(&frames);
    EXPECT_EQ(1u, frames.size());
    if (frames.empty())
      return false;
    return ExpectCorrectFrameData(expected_payload_size, frames.front());
  }

  void AckUpToAndIncluding(media::cast::FrameId frame_id) {
    media::cast::RtcpCastMessage cast_feedback(receiver_ssrc_);
    cast_feedback.ack_frame_id = frame_id;
    remoting_sender_->OnReceivedCastFeedback(cast_feedback);
  }

  void AckOldestInFlightFrames(int count) {
    AckUpToAndIncluding(latest_acked_frame_id() + count);
  }

  // Blocks the caller indefinitely, until a kickstart frame is sent, and then
  // returns the FrameId of the kickstarted-frame.
  media::cast::FrameId WaitForKickstart() {
    return transport_.WaitForKickstart();
  }

  bool ExpectNoFramesCanceled() {
    std::vector<media::cast::FrameId> frame_ids;
    transport_.TakeCanceledFrameIds(&frame_ids);
    return frame_ids.empty();
  }

  bool ExpectFramesCanceled(media::cast::FrameId first_frame_id,
                            media::cast::FrameId last_frame_id) {
    std::vector<media::cast::FrameId> frame_ids;
    transport_.TakeCanceledFrameIds(&frame_ids);
    auto begin = frame_ids.begin();
    auto end = frame_ids.end();
    for (auto fid = first_frame_id; fid <= last_frame_id; ++fid) {
      auto new_end = std::remove(begin, end, fid);
      if (new_end == end)
        return false;
      end = new_end;
    }
    return begin == end;
  }

  static bool ExpectCorrectFrameData(size_t expected_payload_size,
                                     const media::cast::EncodedFrame& frame) {
    if (expected_payload_size != frame.data.size()) {
      ADD_FAILURE() << "Expected frame data size != frame.data.size(): "
                    << expected_payload_size << " vs " << frame.data.size();
      return false;
    }
    for (size_t i = 0; i < expected_payload_size; ++i) {
      if (static_cast<uint8_t>(frame.data[i]) != static_cast<uint8_t>(i)) {
        ADD_FAILURE() << "Frame data byte mismatch at offset " << i;
        return false;
      }
    }
    return true;
  }

 private:
  base::test::ScopedTaskEnvironment scoped_task_environment_;
  const scoped_refptr<media::cast::CastEnvironment> cast_environment_;
  FakeTransport transport_;
  std::unique_ptr<RemotingSender> remoting_sender_;
  media::mojom::RemotingDataStreamSenderPtr sender_;
  mojo::ScopedDataPipeProducerHandle producer_end_;
  bool expecting_error_callback_run_;
  uint32_t receiver_ssrc_;

  DISALLOW_COPY_AND_ASSIGN(RemotingSenderTest);
};

TEST_F(RemotingSenderTest, SendsFramesViaMojoDataPipe) {
  // One 256-byte chunk pushed through the data pipe to make one frame.
  ASSERT_TRUE(ProduceDataChunk(0, 256));
  SendFrame(256);
  RunPendingTasks();
  EXPECT_TRUE(ExpectOneFrameWasSent(256));
  AckOldestInFlightFrames(1);
  EXPECT_EQ(media::cast::FrameId::first(), latest_acked_frame_id());

  // Four 256-byte chunks pushed through the data pipe to make one frame.
  SendFrame(1024);
  for (int i = 0; i < 4; ++i) {
    ASSERT_TRUE(ProduceDataChunk(i * 256, 256));
  }
  RunPendingTasks();
  EXPECT_TRUE(ExpectOneFrameWasSent(1024));
  AckOldestInFlightFrames(1);
  EXPECT_EQ(media::cast::FrameId::first() + 1, latest_acked_frame_id());

  // 10 differently-sized chunks pushed through the data pipe to make one frame
  // that is larger than the data pipe's total capacity.
  SendFrame(6665);
  size_t offset = 0;
  for (int i = 0; i < 10; ++i) {
    const size_t chunk_size = 500 + i * 37;
    ASSERT_TRUE(ProduceDataChunk(offset, chunk_size));
    RunPendingTasks();
    offset += chunk_size;
  }
  RunPendingTasks();
  EXPECT_TRUE(ExpectOneFrameWasSent(6665));
  AckOldestInFlightFrames(1);
  EXPECT_EQ(media::cast::FrameId::first() + 2, latest_acked_frame_id());
}

TEST_F(RemotingSenderTest, SendsMultipleFramesWithDelayedAcks) {
  // Send 4 frames.
  for (int i = 0; i < 4; ++i) {
    ASSERT_TRUE(ProduceDataChunk(0, 16));
    SendFrame(16);
  }
  RunPendingTasks();
  EXPECT_EQ(4, NumberOfFramesInFlight());
  EXPECT_TRUE(ExpectNoFramesCanceled());

  // Ack one frame.
  AckOldestInFlightFrames(1);
  EXPECT_EQ(3, NumberOfFramesInFlight());
  EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first(),
                                   media::cast::FrameId::first()));

  // Ack all.
  AckOldestInFlightFrames(3);
  EXPECT_EQ(0, NumberOfFramesInFlight());
  EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first() + 1,
                                   media::cast::FrameId::first() + 3));
}

TEST_F(RemotingSenderTest, KickstartsIfAckNotTimely) {
  // Send first frame and don't Ack it. Expect the first frame to be
  // kickstarted.
  ASSERT_TRUE(ProduceDataChunk(0, 16));
  SendFrame(16);
  EXPECT_EQ(media::cast::FrameId::first(), WaitForKickstart());
  EXPECT_EQ(1, NumberOfFramesInFlight());

  // Send 3 more frames and don't Ack them either. Expect the 4th frame to be
  // kickstarted.
  for (int i = 0; i < 3; ++i) {
    ASSERT_TRUE(ProduceDataChunk(0, 16));
    SendFrame(16);
  }
  EXPECT_EQ(media::cast::FrameId::first() + 3, WaitForKickstart());
  EXPECT_EQ(4, NumberOfFramesInFlight());

  // Ack the first two frames and wait for another kickstart (for the 4th frame
  // again).
  AckOldestInFlightFrames(2);
  EXPECT_EQ(2, NumberOfFramesInFlight());
  EXPECT_EQ(media::cast::FrameId::first() + 3, WaitForKickstart());
}

TEST_F(RemotingSenderTest, CancelsUnsentFrame) {
  EXPECT_EQ(0u, GetSizeOfNextFrameData());
  SendFrame(16);
  SendFrame(32);
  CancelInFlightData();

  // Provide the data. Both frames should not be sent out.
  ASSERT_TRUE(ProduceDataChunk(0, 16));
  RunPendingTasks();
  ASSERT_TRUE(ProduceDataChunk(0, 32));
  RunPendingTasks();
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Since no frames were sent, none should have been passed to the
  // CastTransport, and none should have been canceled.
  std::vector<media::cast::EncodedFrame> frames;
  TakeSentFrames(&frames);
  EXPECT_TRUE(frames.empty());
  EXPECT_TRUE(ExpectNoFramesCanceled());
}

// http://crbug.com/647423
#define MAYBE_CancelsFramesInFlight DISABLED_CancelsFramesInFlight
TEST_F(RemotingSenderTest, MAYBE_CancelsFramesInFlight) {
  EXPECT_TRUE(IsFlowRestartPending());

  // Send 10 frames.
  for (int i = 0; i < 10; ++i) {
    ASSERT_TRUE(ProduceDataChunk(0, 16));
    SendFrame(16);
  }
  RunPendingTasks();
  EXPECT_FALSE(IsFlowRestartPending());
  EXPECT_EQ(10, NumberOfFramesInFlight());

  // Ack the first frame.
  AckOldestInFlightFrames(1);
  EXPECT_FALSE(IsFlowRestartPending());
  EXPECT_EQ(9, NumberOfFramesInFlight());
  EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first(),
                                   media::cast::FrameId::first()));

  // Cancel all in-flight data. This should cause the remaining 9 frames to be
  // canceled.
  CancelInFlightData();
  RunPendingTasks();
  EXPECT_TRUE(IsFlowRestartPending());
  EXPECT_EQ(0, NumberOfFramesInFlight());
  EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first() + 1,
                                   media::cast::FrameId::first() + 9));

  // Send one more frame and ack it.
  ASSERT_TRUE(ProduceDataChunk(0, 16));
  SendFrame(16);
  RunPendingTasks();
  EXPECT_FALSE(IsFlowRestartPending());
  EXPECT_EQ(1, NumberOfFramesInFlight());
  AckOldestInFlightFrames(1);
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Check that the dependency metadata was set correctly to indicate a frame
  // that immediately follows a CancelInFlightData() operation.
  std::vector<media::cast::EncodedFrame> frames;
  TakeSentFrames(&frames);
  ASSERT_EQ(11u, frames.size());
  for (size_t i = 0; i < 11; ++i) {
    const media::cast::EncodedFrame& frame = frames[i];
    EXPECT_EQ(media::cast::FrameId::first() + i, frame.frame_id);
    if (i == 0 || i == 10)
      EXPECT_EQ(media::cast::EncodedFrame::KEY, frame.dependency);
    else
      EXPECT_EQ(media::cast::EncodedFrame::DEPENDENT, frame.dependency);
  }
}

TEST_F(RemotingSenderTest, WaitsForDataBeforeConsumingFromDataPipe) {
  // Queue up and issue Mojo calls to consume three frames. Since no data has
  // been pushed into the pipe yet no frames should be sent.
  for (int i = 0; i < 3; ++i) {
    SendFrame(4);
  }
  RunPendingTasks();
  EXPECT_TRUE(IsFlowRestartPending());
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Push the data for one frame into the data pipe. This should trigger input
  // processing and allow one frame to be sent.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  RunPendingTasks();  // Allow Mojo Watcher to signal CastRemotingSender.
  EXPECT_FALSE(IsFlowRestartPending());
  EXPECT_EQ(1, NumberOfFramesInFlight());

  // Now push the data for the other two frames into the data pipe and expect
  // two more frames to be sent.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  RunPendingTasks();  // Allow Mojo Watcher to signal CastRemotingSender.
  EXPECT_FALSE(IsFlowRestartPending());
  EXPECT_EQ(3, NumberOfFramesInFlight());
}

TEST_F(RemotingSenderTest, WaitsForDataThenDiscardsCanceledData) {
  // Queue up and issue Mojo calls to consume data chunks and send three
  // frames. Since no data has been pushed into the pipe yet no frames should be
  // sent.
  for (int i = 0; i < 3; ++i) {
    SendFrame(4);
  }
  RunPendingTasks();
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Cancel all in-flight data.
  CancelInFlightData();
  RunPendingTasks();

  // Now, push the data for one frame into the data pipe. Because of the
  // cancellation, no frames should be sent.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  RunPendingTasks();  // Allow Mojo Watcher to signal CastRemotingSender.
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Now push the data for the other two frames into the data pipe and still no
  // frames should be sent.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  RunPendingTasks();  // Allow Mojo Watcher to signal CastRemotingSender.
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Now issue calls to send another frame and then push the data for it into
  // the data pipe. Expect to see the frame gets sent since it was provided
  // after the CancelInFlightData().
  SendFrame(4);
  RunPendingTasks();
  EXPECT_EQ(0, NumberOfFramesInFlight());
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  RunPendingTasks();  // Allow Mojo Watcher to signal CastRemotingSender.
  EXPECT_EQ(1, NumberOfFramesInFlight());
}

TEST_F(RemotingSenderTest, StopsConsumingWhileTooManyFramesAreInFlight) {
  EXPECT_TRUE(IsFlowRestartPending());

  // Send out the maximum possible number of unacked frames, but don't ack any
  // yet.
  for (int i = 0; i < media::cast::kMaxUnackedFrames; ++i) {
    ASSERT_TRUE(ProduceDataChunk(0, 4));
    SendFrame(4);
  }
  RunPendingTasks();
  EXPECT_FALSE(IsFlowRestartPending());
  EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
  // Note: All frames should have been sent to the Transport, and so
  // CastRemotingSender's single-frame data buffer should be empty.
  EXPECT_EQ(0u, GetSizeOfNextFrameData());

  // When the client provides one more frame, CastRemotingSender will begin
  // queuing input operations instead of sending the the frame to the
  // CastTransport.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  SendFrame(4);
  RunPendingTasks();
  EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
  // Note: The unsent frame resides in CastRemotingSender's single-frame data
  // buffer.
  EXPECT_EQ(4u, GetSizeOfNextFrameData());

  // Ack the the first frame and expect sending to resume, with one more frame
  // being sent to the CastTransport.
  AckOldestInFlightFrames(1);
  EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
  // Note: Only one frame was backlogged, and so CastRemotingSender's
  // single-frame data buffer should be empty.
  EXPECT_EQ(0u, GetSizeOfNextFrameData());

  // Attempting to send another frame will once again cause CastRemotingSender
  // to queue input operations.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  SendFrame(4);
  RunPendingTasks();
  EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
  // Note: Once again, CastRemotingSender's single-frame data buffer contains an
  // unsent frame.
  EXPECT_EQ(4u, GetSizeOfNextFrameData());

  // Send more frames: Some number of frames will queue-up inside the Mojo data
  // pipe (the exact number depends on the data pipe's capacity, and how Mojo
  // manages memory internally). At some point, attempting to produce and push
  // another frame will fail because the data pipe is full.
  int num_frames_in_data_pipe = 0;
  while (ProduceDataChunk(0, 768)) {
    ++num_frames_in_data_pipe;
    SendFrame(768);
    RunPendingTasks();
    EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
    // Note: CastRemotingSender's single-frame data buffer should still contain
    // the unsent 4-byte frame.
    EXPECT_EQ(4u, GetSizeOfNextFrameData());
  }
  EXPECT_LT(0, num_frames_in_data_pipe);

  // Ack one frame at a time until the backlog in the Mojo data pipe has
  // cleared.
  int remaining_frames_in_data_pipe = num_frames_in_data_pipe;
  while (remaining_frames_in_data_pipe > 0) {
    AckOldestInFlightFrames(1);
    RunPendingTasks();
    --remaining_frames_in_data_pipe;
    EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
    EXPECT_EQ(768u, GetSizeOfNextFrameData());
  }

  // Ack one more frame. There should no longer be a backlog on the input side
  // of things.
  AckOldestInFlightFrames(1);
  RunPendingTasks();  // No additional Mojo method calls should be made here.
  EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
  // The single-frame data buffer should be empty to indicate no input backlog.
  EXPECT_EQ(0u, GetSizeOfNextFrameData());

  // Ack all but one frame.
  AckOldestInFlightFrames(NumberOfFramesInFlight() - 1);
  EXPECT_EQ(1, NumberOfFramesInFlight());
  // ..and one more frame can be sent immediately.
  ASSERT_TRUE(ProduceDataChunk(0, 4));
  SendFrame(4);
  RunPendingTasks();
  EXPECT_EQ(2, NumberOfFramesInFlight());
  // ...and ack these last two frames.
  AckOldestInFlightFrames(2);
  EXPECT_EQ(0, NumberOfFramesInFlight());

  // Finally, examine all frames that were sent to the CastTransport, and
  // confirm their metadata and data is valid.
  std::vector<media::cast::EncodedFrame> frames;
  TakeSentFrames(&frames);
  const size_t total_frames_sent =
      media::cast::kMaxUnackedFrames + 2 + num_frames_in_data_pipe + 1;
  ASSERT_EQ(total_frames_sent, frames.size());
  media::cast::RtpTimeTicks last_rtp_timestamp =
      media::cast::RtpTimeTicks() - media::cast::RtpTimeDelta::FromTicks(1);
  for (size_t i = 0; i < total_frames_sent; ++i) {
    const media::cast::EncodedFrame& frame = frames[i];
    EXPECT_EQ(media::cast::FrameId::first() + i, frame.frame_id);
    if (i == 0) {
      EXPECT_EQ(media::cast::EncodedFrame::KEY, frame.dependency);
      EXPECT_EQ(media::cast::FrameId::first() + i, frame.referenced_frame_id);
    } else {
      EXPECT_EQ(media::cast::EncodedFrame::DEPENDENT, frame.dependency);
      EXPECT_EQ(media::cast::FrameId::first() + i - 1,
                frame.referenced_frame_id);
    }

    // RTP timestamp must be monotonically increasing.
    EXPECT_GT(frame.rtp_timestamp, last_rtp_timestamp);
    last_rtp_timestamp = frame.rtp_timestamp;

    size_t expected_frame_size = 4;
    if ((i >= media::cast::kMaxUnackedFrames + 2u) &&
        (i < media::cast::kMaxUnackedFrames + 2u + num_frames_in_data_pipe)) {
      expected_frame_size = 768;
    }
    EXPECT_TRUE(ExpectCorrectFrameData(expected_frame_size, frame));
  }
}

}  // namespace mirroring
