text: // Copyright 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 "cast/streaming/packet_util.h"
  
  #include "absl/types/span.h"
  #include "gtest/gtest.h"
  
  namespace openscreen {
  namespace cast {
  namespace {
  
  // Tests that a simple RTCP packet containing only a Sender Report can be
  // identified.
  TEST(PacketUtilTest, InspectsRtcpPacketFromSender) {
    // clang-format off
    const uint8_t kSenderReportPacket[] = {
      0b10000000,  // Version=2, Padding=no, ItemCount=0.
      200,  // RTCP Packet type.
      0x00, 0x06,  // Length of remainder of packet, in 32-bit words.
      1, 2, 3, 4,  // SSRC of sender.
      0xe0, 0x73, 0x2e, 0x54,  // NTP Timestamp (late evening on 2019-04-30).
          0x80, 0x00, 0x00, 0x00,
      0x00, 0x14, 0x99, 0x70,  // RTP Timestamp (15 seconds, 90kHz timebase).
      0x00, 0x00, 0x01, 0xff,  // Sender's Packet Count.
      0x00, 0x07, 0x11, 0x0d,  // Sender's Octet Count.
    };
    // clang-format on
    const Ssrc kSenderSsrc = 0x01020304;
  
    const auto result = InspectPacketForRouting(kSenderReportPacket);
    EXPECT_EQ(ApparentPacketType::RTCP, result.first);
    EXPECT_EQ(kSenderSsrc, result.second);
  }
  
  // Tests that compound RTCP packets containing a Receiver Report and/or a Cast
  // Feedback message can be identified.
  TEST(PacketUtilTest, InspectsRtcpPacketFromReceiver) {
    // clang-format off
    const uint8_t kReceiverReportPacket[] = {
      0b10000001,  // Version=2, Padding=no, ItemCount=1.
      201,  // RTCP Packet type.
      0x00, 0x01,  // Length of remainder of packet, in 32-bit words.
      9, 8, 7, 6,  // SSRC of receiver.
    };
    const uint8_t kCastFeedbackPacket[] = {
      // Cast Feedback
      0b10000000 | 15,  // Version=2, Padding=no, Subtype=15.
      206,  // RTCP Packet type byte.
      0x00, 0x04,  // Length of remainder of packet, in 32-bit words.
      9, 8, 7, 6,  // SSRC of receiver.
      1, 2, 3, 4,  // SSRC of sender.
      'C', 'A', 'S', 'T',
      0x0a,  // Checkpoint Frame ID (lower 8 bits).
      0x00,  // Number of "Loss Fields"
      0x00, 0x28,  // Current Playout Delay in milliseconds.
    };
    // clang-format on
    const Ssrc kReceiverSsrc = 0x09080706;
  
    {
      const auto result = InspectPacketForRouting(kReceiverReportPacket);
      EXPECT_EQ(ApparentPacketType::RTCP, result.first);
      EXPECT_EQ(kReceiverSsrc, result.second);
    }
  
    {
      const auto result = InspectPacketForRouting(kCastFeedbackPacket);
      EXPECT_EQ(ApparentPacketType::RTCP, result.first);
      EXPECT_EQ(kReceiverSsrc, result.second);
    }
  
    const absl::Span<const uint8_t> kCompoundCombinations[2][2] = {
        {kReceiverReportPacket, kCastFeedbackPacket},
        {kCastFeedbackPacket, kReceiverReportPacket},
    };
    for (const auto& combo : kCompoundCombinations) {
      uint8_t compound_packet[sizeof(kReceiverReportPacket) +
                              sizeof(kCastFeedbackPacket)];
      memcpy(compound_packet, combo[0].data(), combo[0].size());
      memcpy(compound_packet + combo[0].size(), combo[1].data(), combo[1].size());
  
      const auto result = InspectPacketForRouting(compound_packet);
      EXPECT_EQ(ApparentPacketType::RTCP, result.first);
      EXPECT_EQ(kReceiverSsrc, result.second);
    }
  }
  
  // Tests that a RTP packet can be identified.
  TEST(PacketUtilTest, InspectsRtpPacket) {
    // clang-format off
    const uint8_t kInput[] = {
      0b10000000,  // Version/Padding byte.
      96,  // Payload type byte.
      0xbe, 0xef,  // Sequence number.
      9, 8, 7, 6,  // RTP timestamp.
      1, 2, 3, 4,  // SSRC.
      0b10000000,  // Is key frame, no extensions.
      5,  // Frame ID.
      0xa, 0xb,  // Packet ID.
      0xa, 0xc,  // Max packet ID.
      0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8,  // Payload.
    };
    // clang-format on
    const Ssrc kSenderSsrc = 0x01020304;
  
    const auto result = InspectPacketForRouting(kInput);
    EXPECT_EQ(ApparentPacketType::RTP, result.first);
    EXPECT_EQ(kSenderSsrc, result.second);
  }
  
  // Tests that a RTP packet with the "127 payload type" hack can be identified as
  // valid. See comments in rtp_defines.h for the RtpPayloadType enum definition,
  // for further details.
  TEST(PacketUtilTest, InspectsAndroidAudioRtpPacket) {
    // clang-format off
    const uint8_t kInput[] = {
      0b10000000,  // Version/Padding byte.
      127,  // Payload type byte.
      0xbe, 0xef,  // Sequence number.
      9, 8, 7, 6,  // RTP timestamp.
      1, 2, 3, 4,  // SSRC.
      0b10000000,  // Is key frame, no extensions.
      5,  // Frame ID.
      0xa, 0xb,  // Packet ID.
      0xa, 0xc,  // Max packet ID.
      0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8,  // Payload.
    };
    // clang-format on
    const Ssrc kSenderSsrc = 0x01020304;
  
    const auto result = InspectPacketForRouting(kInput);
    EXPECT_EQ(ApparentPacketType::RTP, result.first);
    EXPECT_EQ(kSenderSsrc, result.second);
  }
  
  // Tests that a malformed RTP packet can be identified.
  TEST(PacketUtilTest, InspectsMalformedRtpPacket) {
    // clang-format off
    const uint8_t kInput[] = {
      0b11000000,  // BAD: Version/Padding byte.
      96,  // Payload type byte.
      0xbe, 0xef,  // Sequence number.
      9, 8, 7, 6,  // RTP timestamp.
      1, 2, 3, 4,  // SSRC.
      0b10000000,  // Is key frame, no extensions.
      5,  // Frame ID.
      0xa, 0xb,  // Packet ID.
      0xa, 0xc,  // Max packet ID.
      0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8,  // Payload.
    };
    // clang-format on
  
    const auto result = InspectPacketForRouting(kInput);
    EXPECT_EQ(ApparentPacketType::UNKNOWN, result.first);
  }
  
  // Tests that an empty packet is classified as unknown.
  TEST(PacketUtilTest, InspectsEmptyPacket) {
    const uint8_t kInput[] = {};
  
    const auto result =
        InspectPacketForRouting(absl::Span<const uint8_t>(kInput, 0));
    EXPECT_EQ(ApparentPacketType::UNKNOWN, result.first);
  }
  
  // Tests that a packet with garbage is classified as unknown.
  TEST(PacketUtilTest, InspectsGarbagePacket) {
    // clang-format off
    const uint8_t kInput[] = {
      0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
      0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
      0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
      0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
    };
    // clang-format on
  
    const auto result = InspectPacketForRouting(kInput);
    EXPECT_EQ(ApparentPacketType::UNKNOWN, result.first);
  }
  
  }  // namespace
  }  // namespace cast
  }  // namespace openscreen
  