/*
 *  Copyright (c) 2016 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/rtp_rtcp/source/rtcp_packet/sdes.h"

#include "rtc_base/strings/string_builder.h"
#include "test/gtest.h"
#include "test/rtcp_packet_parser.h"

using webrtc::rtcp::Sdes;

namespace webrtc {
namespace {
const uint32_t kSenderSsrc = 0x12345678;
const uint8_t kPadding = 0;
const uint8_t kTerminatorTag = 0;
const uint8_t kCnameTag = 1;
const uint8_t kNameTag = 2;
const uint8_t kEmailTag = 3;
}  // namespace

TEST(RtcpPacketSdesTest, CreateAndParseWithoutChunks) {
  Sdes sdes;

  rtc::Buffer packet = sdes.Build();
  Sdes parsed;
  EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));

  EXPECT_EQ(0u, parsed.chunks().size());
}

TEST(RtcpPacketSdesTest, CreateAndParseWithOneChunk) {
  const std::string kCname = "alice@host";

  Sdes sdes;
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc, kCname));

  rtc::Buffer packet = sdes.Build();
  Sdes sdes_parsed;
  EXPECT_TRUE(test::ParseSinglePacket(packet, &sdes_parsed));
  const Sdes& parsed = sdes_parsed;  // Ensure accessors are const.

  EXPECT_EQ(1u, parsed.chunks().size());
  EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc);
  EXPECT_EQ(kCname, parsed.chunks()[0].cname);
}

TEST(RtcpPacketSdesTest, CreateAndParseWithMultipleChunks) {
  Sdes sdes;
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 0, "a"));
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 1, "ab"));
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 2, "abc"));
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 3, "abcd"));
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 4, "abcde"));
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 5, "abcdef"));

  rtc::Buffer packet = sdes.Build();
  Sdes parsed;
  EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));

  EXPECT_EQ(6u, parsed.chunks().size());
  EXPECT_EQ(kSenderSsrc + 5, parsed.chunks()[5].ssrc);
  EXPECT_EQ("abcdef", parsed.chunks()[5].cname);
}

TEST(RtcpPacketSdesTest, CreateWithTooManyChunks) {
  const size_t kMaxChunks = (1 << 5) - 1;
  Sdes sdes;
  for (size_t i = 0; i < kMaxChunks; ++i) {
    uint32_t ssrc = kSenderSsrc + i;
    rtc::StringBuilder oss;
    oss << "cname" << i;
    EXPECT_TRUE(sdes.AddCName(ssrc, oss.str()));
  }
  EXPECT_FALSE(sdes.AddCName(kSenderSsrc + kMaxChunks, "foo"));
}

TEST(RtcpPacketSdesTest, CreateAndParseCnameItemWithEmptyString) {
  Sdes sdes;
  EXPECT_TRUE(sdes.AddCName(kSenderSsrc, ""));

  rtc::Buffer packet = sdes.Build();
  Sdes parsed;
  EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));

  EXPECT_EQ(1u, parsed.chunks().size());
  EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc);
  EXPECT_EQ("", parsed.chunks()[0].cname);
}

TEST(RtcpPacketSdesTest, ParseSkipsNonCNameField) {
  const uint8_t kName[] = "abc";
  const uint8_t kCname[] = "de";
  const uint8_t kValidPacket[] = {
      0x81, 202,       0x00,      0x04,           0x12,     0x34,     0x56,
      0x78, kNameTag,  3,         kName[0],       kName[1], kName[2], kCnameTag,
      2,    kCname[0], kCname[1], kTerminatorTag, kPadding, kPadding};
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kValidPacket) % 4);
  ASSERT_EQ(kValidPacket[3] + 1u, sizeof(kValidPacket) / 4);

  Sdes parsed;
  EXPECT_TRUE(test::ParseSinglePacket(kValidPacket, &parsed));

  EXPECT_EQ(1u, parsed.chunks().size());
  EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc);
  EXPECT_EQ("de", parsed.chunks()[0].cname);
}

TEST(RtcpPacketSdesTest, ParseSkipsChunksWithoutCName) {
  const uint8_t kName[] = "ab";
  const uint8_t kEmail[] = "de";
  const uint8_t kCname[] = "def";
  const uint8_t kPacket[] = {
      0x82,           202,      0x00,      0x07,      0x12,
      0x34,           0x56,     0x78,  // 1st chunk.
      kNameTag,       3,        kName[0],  kName[1],  kName[2],
      kEmailTag,      2,        kEmail[0], kEmail[1], kTerminatorTag,
      kPadding,       kPadding, 0x23,      0x45,      0x67,
      0x89,  // 2nd chunk.
      kCnameTag,      3,        kCname[0], kCname[1], kCname[2],
      kTerminatorTag, kPadding, kPadding};
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kPacket) % 4);
  ASSERT_EQ(kPacket[3] + 1u, sizeof(kPacket) / 4);

  Sdes parsed;
  EXPECT_TRUE(test::ParseSinglePacket(kPacket, &parsed));
  ASSERT_EQ(1u, parsed.chunks().size());
  EXPECT_EQ(0x23456789u, parsed.chunks()[0].ssrc);
  EXPECT_EQ("def", parsed.chunks()[0].cname);
}

TEST(RtcpPacketSdesTest, ParseFailsWithoutChunkItemTerminator) {
  const uint8_t kName[] = "abc";
  const uint8_t kCname[] = "d";
  // No place for next chunk item.
  const uint8_t kInvalidPacket[] = {
      0x81,     202, 0x00,     0x03,     0x12,     0x34,      0x56, 0x78,
      kNameTag, 3,   kName[0], kName[1], kName[2], kCnameTag, 1,    kCname[0]};
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
  ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);

  Sdes parsed;
  EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
}

TEST(RtcpPacketSdesTest, ParseFailsWithDamagedChunkItem) {
  const uint8_t kName[] = "ab";
  const uint8_t kCname[] = "d";
  // Next chunk item has non-terminator type, but not the size.
  const uint8_t kInvalidPacket[] = {
      0x81,     202, 0x00,     0x03,     0x12,      0x34, 0x56,      0x78,
      kNameTag, 2,   kName[0], kName[1], kCnameTag, 1,    kCname[0], kEmailTag};
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
  ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);

  Sdes parsed;
  EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
}

TEST(RtcpPacketSdesTest, ParseFailsWithTooLongChunkItem) {
  const uint8_t kName[] = "abc";
  const uint8_t kCname[] = "d";
  // Last chunk item has length that goes beyond the buffer end.
  const uint8_t kInvalidPacket[] = {
      0x81,     202, 0x00,     0x03,     0x12,     0x34,      0x56, 0x78,
      kNameTag, 3,   kName[0], kName[1], kName[2], kCnameTag, 2,    kCname[0]};
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
  ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);

  Sdes parsed;
  EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
}

TEST(RtcpPacketSdesTest, ParseFailsWithTwoCNames) {
  const uint8_t kCname1[] = "a";
  const uint8_t kCname2[] = "de";
  const uint8_t kInvalidPacket[] = {
      0x81,       202,           0x00, 0x03,       0x12,      0x34, 0x56,
      0x78,       kCnameTag,     1,    kCname1[0], kCnameTag, 2,    kCname2[0],
      kCname2[1], kTerminatorTag};
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
  ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);

  Sdes parsed;
  EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
}

TEST(RtcpPacketSdesTest, ParseFailsWithTooLittleSpaceForNextChunk) {
  const uint8_t kCname[] = "a";
  const uint8_t kEmail[] = "de";
  // Two chunks are promised in the header, but no place for the second chunk.
  const uint8_t kInvalidPacket[] = {
      0x82,           202,  0x00,      0x04,      0x12, 0x34,      0x56,
      0x78,  // 1st chunk.
      kCnameTag,      1,    kCname[0], kEmailTag, 2,    kEmail[0], kEmail[1],
      kTerminatorTag, 0x23, 0x45,      0x67,      0x89};  // 2nd chunk.
  // Sanity checks packet was assembled correctly.
  ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
  ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);

  Sdes parsed;
  EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
}

TEST(RtcpPacketSdesTest, ParsedSdesCanBeReusedForBuilding) {
  Sdes source;
  const std::string kAlice = "alice@host";
  const std::string kBob = "bob@host";
  source.AddCName(kSenderSsrc, kAlice);

  rtc::Buffer packet1 = source.Build();
  Sdes middle;
  test::ParseSinglePacket(packet1, &middle);

  EXPECT_EQ(source.BlockLength(), middle.BlockLength());

  middle.AddCName(kSenderSsrc + 1, kBob);

  rtc::Buffer packet2 = middle.Build();
  Sdes destination;
  test::ParseSinglePacket(packet2, &destination);

  EXPECT_EQ(middle.BlockLength(), destination.BlockLength());

  EXPECT_EQ(2u, destination.chunks().size());
  EXPECT_EQ(kSenderSsrc, destination.chunks()[0].ssrc);
  EXPECT_EQ(kAlice, destination.chunks()[0].cname);
  EXPECT_EQ(kSenderSsrc + 1, destination.chunks()[1].ssrc);
  EXPECT_EQ(kBob, destination.chunks()[1].cname);
}
}  // namespace webrtc
