// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h"

#include <cpp-string/string_printf.h>

#include <limits>
#include <variant>

#include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "pw_bluetooth_sapphire/internal/host/common/log.h"
#include "pw_bluetooth_sapphire/internal/host/common/random.h"
#include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
#include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
#include "pw_unit_test/framework.h"

namespace bt {
namespace {

constexpr uint16_t kGattUuid = 0x1801;
constexpr uint16_t kEddystoneUuid = 0xFEAA;
constexpr uint16_t kHeartRateServiceUuid = 0x180D;

constexpr uint16_t kId1As16 = 0x0212;
constexpr uint16_t kId2As16 = 0x1122;

constexpr size_t kRandomDataSize = 100;

TEST(AdvertisingDataTest, MakeEmpty) {
  AdvertisingData data;

  EXPECT_EQ(0u, data.CalculateBlockSize());
}

TEST(AdvertisingDataTest, CopyLeavesNoRemnants) {
  AdvertisingData data;
  data.SetFlags(0x4);
  data.SetTxPower(4);
  data.SetAppearance(0x4567);
  EXPECT_TRUE(data.SetLocalName("fuchsia"));
  EXPECT_TRUE(data.AddUri("http://fuchsia.cl"));

  StaticByteBuffer bytes(0x01, 0x02, 0x03);
  EXPECT_TRUE(data.SetManufacturerData(0x0123, bytes.view()));

  auto service_uuid = UUID(kId1As16);
  EXPECT_TRUE(data.AddServiceUuid(service_uuid));

  StaticByteBuffer service_bytes(0x01, 0x02);
  EXPECT_TRUE(data.SetServiceData(service_uuid, service_bytes.view()));

  AdvertisingData new_data;
  new_data.SetTxPower(1);
  new_data.Copy(&data);

  EXPECT_EQ(1, data.tx_power().value());
  EXPECT_FALSE(data.appearance().has_value());
  EXPECT_FALSE(data.local_name().has_value());
  EXPECT_FALSE(data.flags().has_value());
  EXPECT_EQ(0u, data.uris().size());
  EXPECT_EQ(0u, data.manufacturer_data_ids().size());
  EXPECT_EQ(0u, data.service_uuids().size());
  EXPECT_EQ(0u, data.service_data_uuids().size());
}

TEST(AdvertisingDataTest, EncodeKnownURI) {
  AdvertisingData data;
  EXPECT_TRUE(data.AddUri("https://abc.xyz"));

  StaticByteBuffer bytes(
      0x0B, 0x24, 0x17, '/', '/', 'a', 'b', 'c', '.', 'x', 'y', 'z');

  EXPECT_EQ(bytes.size(), data.CalculateBlockSize());
  DynamicByteBuffer block(data.CalculateBlockSize());
  data.WriteBlock(&block, std::nullopt);
  EXPECT_TRUE(ContainersEqual(bytes, block));
}

TEST(AdvertisingDataTest, EncodeUnknownURI) {
  AdvertisingData data;
  EXPECT_TRUE(data.AddUri("flubs:xyz"));

  StaticByteBuffer bytes(
      0x0B, 0x24, 0x01, 'f', 'l', 'u', 'b', 's', ':', 'x', 'y', 'z');

  size_t block_size = data.CalculateBlockSize();
  EXPECT_EQ(bytes.size(), block_size);
  DynamicByteBuffer block(block_size);
  data.WriteBlock(&block, std::nullopt);
  EXPECT_TRUE(ContainersEqual(bytes, block));
}

TEST(AdvertisingDataTest, CompressServiceUUIDs) {
  AdvertisingData data;
  std::unordered_set<UUID> uuids{UUID(kId1As16), UUID(kId2As16)};
  for (auto& uuid : uuids) {
    SCOPED_TRACE(bt_str(uuid));
    EXPECT_TRUE(data.AddServiceUuid(uuid));
  }

  uint8_t expected_block_size = 1                          // length byte
                                + 1                        // type byte
                                + (sizeof(uint16_t) * 2);  // 2 16-bit UUIDs
  EXPECT_EQ(expected_block_size, data.CalculateBlockSize());

  StaticByteBuffer expected_header{expected_block_size - 1,
                                   DataType::kIncomplete16BitServiceUuids};

  DynamicByteBuffer block(expected_block_size);
  data.WriteBlock(&block, std::nullopt);

  EXPECT_TRUE(
      ContainersEqual(expected_header, block.view(/*pos=*/0, /*size=*/2)));
  auto to_uuid = [](const ByteBuffer& b, size_t pos) {
    return UUID(b.view(pos, /*size=*/2).To<uint16_t>());
  };
  EXPECT_TRUE(uuids.find(to_uuid(block, 2)) != uuids.end());
  EXPECT_TRUE(uuids.find(to_uuid(block, 4)) != uuids.end());
}

TEST(AdvertisingDataTest, ParseBlock) {
  StaticByteBuffer bytes(
      // Complete 16-bit UUIDs
      0x05,
      0x03,
      0x12,
      0x02,
      0x22,
      0x11,
      // Incomplete list of 32-bit UUIDs
      0x05,
      0x04,
      0x34,
      0x12,
      0x34,
      0x12,
      // Local name
      0x09,
      0x09,
      'T',
      'e',
      's',
      't',
      0xF0,
      0x9F,
      0x92,
      0x96,
      // TX Power
      0x02,
      0x0A,
      0x8F);

  AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
  ASSERT_EQ(fit::ok(), data);

  EXPECT_EQ(3u, data->service_uuids().size());
  EXPECT_TRUE(data->local_name());
  EXPECT_EQ("Test💖", data->local_name()->name);
  EXPECT_TRUE(data->tx_power());
  EXPECT_EQ(-113, *(data->tx_power()));
}

TEST(AdvertisingDataTest, ParseBlockUnknownDataType) {
  AdvertisingData expected_ad;
  constexpr uint8_t lower_byte = 0x12, upper_byte = 0x22;
  constexpr uint16_t uuid_value = (upper_byte << 8) + lower_byte;
  // The only field present in the expected AD is one complete 16-bit UUID.
  EXPECT_TRUE(expected_ad.AddServiceUuid(UUID(uuid_value)));

  StaticByteBuffer bytes{// Complete 16-bit UUIDs
                         0x03,
                         0x03,
                         lower_byte,
                         upper_byte,
                         // 0x40, the second octet, is not a recognized DataType
                         // (see common/supplement_data.h).
                         0x05,
                         0x40,
                         0x34,
                         0x12,
                         0x34,
                         0x12};
  AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
  ASSERT_EQ(fit::ok(), data);

  // The second field of `bytes` was valid (in that its length byte matched its
  // length), but its Data Type was unknown, so it should be ignored (i.e. the
  // only field in the `data` should be the single 16-bit UUID, matching
  // expected AD).
  EXPECT_EQ(expected_ad, *data);
}

TEST(AdvertisingDataTest, ParseBlockNameTooLong) {
  // A block with a name of exactly kMaxNameLength (==248) bytes should be
  // parsed correctly.
  {
    StaticByteBuffer<2> leading_bytes{kMaxNameLength + 1,
                                      DataType::kCompleteLocalName};
    auto bytes = DynamicByteBuffer(kMaxNameLength + 2);
    bytes.Write(leading_bytes);
    DynamicByteBuffer name(kMaxNameLength);
    name.Fill('a');
    bytes.Write(name, /*pos=*/2);
    AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
    ASSERT_EQ(fit::ok(), result);
    EXPECT_EQ(result->local_name()->name, std::string(kMaxNameLength, 'a'));
  }
  // Repeat previous test with shortened name.
  {
    auto leading_bytes =
        StaticByteBuffer<2>{kMaxNameLength + 1, DataType::kShortenedLocalName};
    auto bytes = DynamicByteBuffer(kMaxNameLength + 2);
    bytes.Write(leading_bytes);
    DynamicByteBuffer name(kMaxNameLength);
    name.Fill('a');
    bytes.Write(name, /*pos=*/2);
    AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
    ASSERT_EQ(fit::ok(), result);
    EXPECT_EQ(result->local_name()->name, std::string(kMaxNameLength, 'a'));
  }
  // A block with a name of kMaxNameLength+1 (==249) bytes should be rejected.
  {
    StaticByteBuffer<2> leading_bytes{kMaxNameLength + 2,
                                      DataType::kCompleteLocalName};
    auto bytes = DynamicByteBuffer(kMaxNameLength + 3);
    bytes.Write(leading_bytes);
    DynamicByteBuffer name(kMaxNameLength + 1);
    name.Fill('a');
    bytes.Write(name, /*pos=*/2);
    AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
    ASSERT_TRUE(result.is_error());
    EXPECT_EQ(AdvertisingData::ParseError::kLocalNameTooLong,
              result.error_value());
  }
  // Repeat previous test with shortened name.
  {
    auto leading_bytes =
        StaticByteBuffer<2>{kMaxNameLength + 2, DataType::kShortenedLocalName};
    auto bytes = DynamicByteBuffer(kMaxNameLength + 3);
    bytes.Write(leading_bytes);
    DynamicByteBuffer name(kMaxNameLength + 1);
    name.Fill('a');
    bytes.Write(name, /*pos=*/2);
    AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
    ASSERT_TRUE(result.is_error());
    EXPECT_EQ(AdvertisingData::ParseError::kLocalNameTooLong,
              result.error_value());
  }
}

TEST(AdvertisingDataTest, ManufacturerZeroLength) {
  StaticByteBuffer bytes(
      // Complete 16-bit UUIDs
      0x05,
      0x03,
      0x12,
      0x02,
      0x22,
      0x11,
      // Manufacturer Data with no data
      0x03,
      0xFF,
      0x34,
      0x12);

  EXPECT_EQ(0u, AdvertisingData().manufacturer_data_ids().size());

  AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
  ASSERT_EQ(fit::ok(), data);

  EXPECT_EQ(1u, data->manufacturer_data_ids().count(0x1234));
  EXPECT_EQ(0u, data->manufacturer_data(0x1234).size());
}

TEST(AdvertisingDataTest, ServiceData) {
  // A typical Eddystone-URL beacon advertisement
  // to "https://fuchsia.cl"
  StaticByteBuffer bytes(
      // Complete 16-bit UUIDs, 0xFEAA
      0x03,
      0x03,
      0xAA,
      0xFE,
      // Eddystone Service (0xFEAA) Data:
      0x10,
      0x16,
      0xAA,
      0xFE,
      0x10,  // Eddystone-Uri type
      0xEE,  // TX Power level -18dBm
      0x03,  // "https://"
      'f',
      'u',
      'c',
      'h',
      's',
      'i',
      'a',
      '.',
      'c',
      'l');

  EXPECT_EQ(0u, AdvertisingData().service_data_uuids().size());

  AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
  ASSERT_EQ(fit::ok(), data);

  UUID eddystone(uint16_t{0xFEAA});

  EXPECT_EQ(1u, data->service_data_uuids().size());
  EXPECT_EQ(13u, data->service_data(eddystone).size());

  EXPECT_TRUE(ContainersEqual(bytes.view(8), data->service_data(eddystone)));
}

// Per CSS v9 Part A 1.1.1, "A packet or data block shall not contain more than
// one instance for each Service UUID data size". We enforce this by failing to
// parse AdvertisingData with UUIDs of a particular size which exceed the amount
// that can fit in one TLV field.
TEST(AdvertisingDataTest, TooManyUuidsOfSizeRejected) {
  // Space for the maximum # of 16 bit UUIDs + length + type fields.
  const uint64_t kMaxAllowed16BitUuidsSize =
      (2 + kMax16BitUuids * UUIDElemSize::k16Bit);
  // Space for one more UUID + type and length fields
  const uint64_t kExpectedBuffSize =
      kMaxAllowed16BitUuidsSize + (2 + UUIDElemSize::k16Bit);

  DynamicByteBuffer bytes(kExpectedBuffSize);
  uint64_t offset = 0;
  // Write first TLV field with maximum # of UUIDs
  bytes.Write(StaticByteBuffer{
      kMax16BitUuids * UUIDElemSize::k16Bit + 1,                  // Size byte
      static_cast<uint8_t>(DataType::kComplete16BitServiceUuids)  // Type byte
  });
  offset += 2;
  for (uint16_t i = 0; i < kMax16BitUuids; ++i) {
    UUID uuid(static_cast<uint16_t>(i + 'a'));
    bytes.Write(uuid.CompactView(), offset);
    offset += uuid.CompactSize();
  }
  // Verify that we successfully parse an AD with the maximum amount of 16 bit
  // UUIDs
  AdvertisingData::ParseResult adv_result = AdvertisingData::FromBytes(
      bytes.view(/*pos=*/0, /*size=*/kMaxAllowed16BitUuidsSize));
  ASSERT_EQ(fit::ok(), adv_result);
  EXPECT_EQ(kMax16BitUuids, adv_result->service_uuids().size());
  // Write second Complete 16 bit Service UUIDs TLV field with one more UUID
  bytes.Write(
      StaticByteBuffer{
          UUIDElemSize::k16Bit + 1,  // Size byte
          static_cast<uint8_t>(
              DataType::kComplete16BitServiceUuids)  // Type byte
      },
      offset);
  offset += 2;
  UUID uuid(static_cast<uint16_t>(kMax16BitUuids + 'a'));
  bytes.Write(uuid.CompactView(), offset);

  adv_result = AdvertisingData::FromBytes(bytes);
  ASSERT_TRUE(adv_result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kUuidsMalformed,
            adv_result.error_value());
}

TEST(AdvertisingDataTest, Missing) {
  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(DynamicByteBuffer());
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kMissing, result.error_value());
}

TEST(AdvertisingDataTest, InvalidTlvFormat) {
  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(StaticByteBuffer(0x03));
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kInvalidTlvFormat,
            result.error_value());
}

TEST(AdvertisingDataTest, TxPowerLevelMalformed) {
  StaticByteBuffer service_data{/*length=*/0x01,
                                static_cast<uint8_t>(DataType::kTxPowerLevel)};
  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(service_data);
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kTxPowerLevelMalformed,
            result.error_value());
}

TEST(AdvertisingDataTest, UuidsMalformed) {
  StaticByteBuffer service_data{
      0x02,  // Length
      static_cast<uint8_t>(DataType::kComplete16BitServiceUuids),
      0x12  // The length of a valid 16-bit UUID byte array be a multiple of 2
            // (and 1 % 2 == 1).
  };
  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(service_data);
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kUuidsMalformed, result.error_value());
}

TEST(AdvertisingDataTest, ManufacturerSpecificDataTooSmall) {
  StaticByteBuffer service_data{
      0x02,  // Length
      static_cast<uint8_t>(DataType::kManufacturerSpecificData),
      0x12  // Manufacturer-specific data must be at least 2 bytes
  };
  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(service_data);
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kManufacturerSpecificDataTooSmall,
            result.error_value());
}

TEST(AdvertisingDataTest, DecodeServiceDataWithIncompleteUuid) {
  StaticByteBuffer service_data(
      0x02,                                               // Length
      static_cast<uint8_t>(DataType::kServiceData16Bit),  // Data type
      0xAA  // First byte of incomplete UUID
  );

  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(service_data);
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kServiceDataTooSmall,
            result.error_value());
}

TEST(AdvertisingDataTest, AppearanceMalformed) {
  StaticByteBuffer service_data{
      0x02,  // Length
      static_cast<uint8_t>(DataType::kAppearance),
      0x12  // Appearance is supposed to be 2 bytes
  };
  AdvertisingData::ParseResult result =
      AdvertisingData::FromBytes(service_data);
  ASSERT_TRUE(result.is_error());
  EXPECT_EQ(AdvertisingData::ParseError::kAppearanceMalformed,
            result.error_value());
}
TEST(AdvertisingDataTest, Equality) {
  AdvertisingData one, two;

  UUID gatt(kGattUuid);
  UUID eddy(kEddystoneUuid);

  // Service UUIDs
  EXPECT_EQ(two, one);
  EXPECT_TRUE(one.AddServiceUuid(gatt));
  EXPECT_NE(two, one);
  EXPECT_TRUE(two.AddServiceUuid(gatt));
  EXPECT_EQ(two, one);

  // Even when the bytes are the same but from different places
  StaticByteBuffer bytes(0x01, 0x02, 0x03, 0x04);
  StaticByteBuffer same(0x01, 0x02, 0x03, 0x04);
  EXPECT_TRUE(two.SetManufacturerData(0x0123, bytes.view()));
  EXPECT_NE(two, one);
  EXPECT_TRUE(one.SetManufacturerData(0x0123, same.view()));
  EXPECT_EQ(two, one);

  // When TX Power is different
  two.SetTxPower(-34);
  EXPECT_NE(two, one);
  one.SetTxPower(-30);
  EXPECT_NE(two, one);
  one.SetTxPower(-34);
  EXPECT_EQ(two, one);

  // Even if the fields were added in different orders
  AdvertisingData three, four;
  EXPECT_TRUE(three.AddServiceUuid(eddy));
  EXPECT_TRUE(three.AddServiceUuid(gatt));
  EXPECT_NE(three, four);

  EXPECT_TRUE(four.AddServiceUuid(gatt));
  EXPECT_TRUE(four.AddServiceUuid(eddy));
  EXPECT_EQ(three, four);
}

TEST(AdvertisingDataTest, Copy) {
  UUID gatt(kGattUuid);
  UUID eddy(kEddystoneUuid);
  StaticByteBuffer<kRandomDataSize> rand_data;
  random_generator()->Get(rand_data.mutable_subspan());

  AdvertisingData source;
  EXPECT_TRUE(source.AddUri("http://fuchsia.cl"));
  EXPECT_TRUE(source.AddUri("https://ru.st"));
  EXPECT_TRUE(source.SetManufacturerData(0x0123, rand_data.view()));
  EXPECT_TRUE(source.AddServiceUuid(gatt));
  EXPECT_TRUE(source.AddServiceUuid(eddy));

  AdvertisingData dest;
  source.Copy(&dest);

  EXPECT_EQ(source, dest);

  // Modifying the source shouldn't mess with the copy
  EXPECT_TRUE(source.SetLocalName("fuchsia"));
  EXPECT_FALSE(dest.local_name());

  StaticByteBuffer bytes(0x01, 0x02, 0x03);
  EXPECT_TRUE(source.SetManufacturerData(0x0123, bytes.view()));
  EXPECT_TRUE(ContainersEqual(rand_data, dest.manufacturer_data(0x0123)));
}

TEST(AdvertisingDataTest, Move) {
  UUID gatt(kGattUuid);
  UUID eddy(kEddystoneUuid);
  StaticByteBuffer<kRandomDataSize> rand_data;
  random_generator()->Get(rand_data.mutable_subspan());

  UUID heart_rate_uuid(kHeartRateServiceUuid);
  int8_t tx_power = 18;          // arbitrary TX power
  uint16_t appearance = 0x4567;  // arbitrary appearance value
  uint8_t flags = 0x48;          // arbitrary flags value
  AdvertisingData source;
  EXPECT_TRUE(source.SetLocalName("test"));
  source.SetFlags(flags);
  source.SetTxPower(tx_power);
  source.SetAppearance(appearance);
  EXPECT_TRUE(source.AddUri("http://fuchsia.cl"));
  EXPECT_TRUE(source.AddUri("https://ru.st"));
  EXPECT_TRUE(source.SetManufacturerData(0x0123, rand_data.view()));
  EXPECT_TRUE(source.AddServiceUuid(gatt));
  EXPECT_TRUE(source.AddServiceUuid(eddy));
  EXPECT_TRUE(source.SetServiceData(heart_rate_uuid, rand_data.view()));

  auto verify_advertising_data = [&](const AdvertisingData& dest,
                                     const char* type) {
    SCOPED_TRACE(type);
    // Dest should have the data we set.
    EXPECT_EQ("test", dest.local_name()->name);
    EXPECT_EQ(tx_power, dest.tx_power().value());
    EXPECT_EQ(appearance, dest.appearance().value());
    EXPECT_EQ(
        std::unordered_set<std::string>({"http://fuchsia.cl", "https://ru.st"}),
        dest.uris());
    EXPECT_TRUE(ContainersEqual(rand_data, dest.manufacturer_data(0x0123)));
    EXPECT_EQ(std::unordered_set<UUID>({gatt, eddy}), dest.service_uuids());
    EXPECT_TRUE(ContainersEqual(rand_data, dest.service_data(heart_rate_uuid)));
    EXPECT_EQ(flags, dest.flags().value());
  };

  AdvertisingData move_constructed(std::move(source));

  // source should be empty.
  EXPECT_EQ(AdvertisingData(), source);
  verify_advertising_data(move_constructed, "move_constructed");

  AdvertisingData move_assigned{};
  move_assigned = std::move(move_constructed);
  EXPECT_EQ(AdvertisingData(), move_constructed);
  verify_advertising_data(move_assigned, "move_assigned");
}

TEST(AdvertisingDataTest, Flags) {
  // A zero-byte flags is allowed, and sets the flags field to zeroes.
  StaticByteBuffer flags_empty(0x01, DataType::kFlags);
  // Extra bytes are accepted but ignored.
  StaticByteBuffer flags_extra(0x04, DataType::kFlags, 0x03, 0x42, 0x49);

  AdvertisingData::ParseResult data = AdvertisingData::FromBytes(flags_empty);
  ASSERT_EQ(fit::ok(), data);
  ASSERT_TRUE(data->flags().has_value());
  ASSERT_EQ(0x00, data->flags().value());

  data = AdvertisingData::FromBytes(flags_extra);
  ASSERT_EQ(fit::ok(), data);
  ASSERT_TRUE(data->flags().has_value());
  ASSERT_EQ(0x03, data->flags().value());
}

TEST(AdvertisingDataTest, Uris) {
  // The encoding scheme is represented by the first UTF-8 code-point in the URI
  // string. Per
  // https://www.bluetooth.com/specifications/assigned-numbers/uri-scheme-name-string-mapping/,
  // 0xBA is the highest code point corresponding to an encoding scheme.
  // However, 0xBA > 0x7F, so representing too-large encoding schemes (i.e.
  // code-points > 0xBA) in UTF-8 requires two bytes.
  const uint8_t kLargestKnownSchemeByte1 = 0xC2,
                kLargestKnownSchemeByte2 = 0xBA;
  // These bytes represent the (valid) UTF-8 code point for the (unknown
  // encoding scheme) U+00BB.
  const uint8_t kUnknownSchemeByte1 = 0xC2, kUnknownSchemeByte2 = 0xBB;
  StaticByteBuffer bytes(
      // Uri: "https://abc.xyz"
      0x0B,
      DataType::kURI,
      0x17,
      '/',
      '/',
      'a',
      'b',
      'c',
      '.',
      'x',
      'y',
      'z',
      // Empty URI should be ignored:
      0x01,
      DataType::kURI,
      // Uri: "flubs:abc"
      0x0B,
      DataType::kURI,
      0x01,
      'f',
      'l',
      'u',
      'b',
      's',
      ':',
      'a',
      'b',
      'c',
      // Uri: "ms-settings-cloudstorage:flub"
      0x07,
      DataType::kURI,
      kLargestKnownSchemeByte1,
      kLargestKnownSchemeByte2,
      'f',
      'l',
      'u',
      'b',
      // Invalid URI should be ignored - UTF-8 U+00BB doesn't correspond to an
      // encoding scheme.
      0x07,
      DataType::kURI,
      kUnknownSchemeByte1,
      kUnknownSchemeByte2,
      'f',
      'l',
      'u',
      'b',
      // Invalid URI should be ignored - UTF-8 U+0000 doesn't correspond to an
      // encoding scheme.
      0x03,
      DataType::kURI,
      0x00,
      0x00);

  AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
  ASSERT_EQ(fit::ok(), data);

  auto uris = data->uris();
  EXPECT_EQ(3u, uris.size());

  EXPECT_TRUE(std::find(uris.begin(), uris.end(), "https://abc.xyz") !=
              uris.end());
  EXPECT_TRUE(std::find(uris.begin(), uris.end(), "flubs:abc") != uris.end());
  EXPECT_TRUE(std::find(uris.begin(),
                        uris.end(),
                        "ms-settings-cloudstorage:flub") != uris.end());
}

// Tests writing a fully populated |AdvertisingData| to
// an output buffer succeeds.
TEST(AdvertisingDataTest, WriteBlockSuccess) {
  AdvertisingData data;

  data.SetTxPower(4);
  data.SetAppearance(0x4567);
  EXPECT_TRUE(data.SetLocalName("fuchsia"));

  StaticByteBuffer bytes(0x01, 0x02, 0x03);
  EXPECT_TRUE(data.SetManufacturerData(0x0123, bytes.view()));

  auto service_uuid = UUID(kId1As16);
  StaticByteBuffer service_bytes(0x01, 0x02);
  EXPECT_TRUE(data.AddServiceUuid(service_uuid));
  EXPECT_TRUE(data.SetServiceData(service_uuid, service_bytes.view()));

  EXPECT_TRUE(data.AddUri("http://fuchsia.cl"));

  DynamicByteBuffer write_buf(data.CalculateBlockSize());
  EXPECT_TRUE(data.WriteBlock(&write_buf, std::nullopt));

  StaticByteBuffer expected_buf(0x02,
                                0x0a,
                                0x04,  // tx_power_level_: 4
                                0x03,
                                0x19,
                                0x67,
                                0x45,  // appearance_: 0x4567
                                0x08,
                                0x09,
                                0x66,
                                0x75,
                                0x63,
                                0x68,
                                0x73,
                                0x69,
                                0x61,  // local_name_: "fuchsia"
                                0x06,
                                0xff,
                                0x23,
                                0x01,
                                0x01,
                                0x02,
                                0x03,  // manufacturer_data_
                                0x05,
                                0x16,
                                0x12,
                                0x02,
                                0x01,
                                0x02,  // service_data_
                                0x0e,
                                0x24,
                                0x16,
                                0x2f,
                                0x2f,
                                0x66,
                                0x75,
                                0x63,
                                0x68,
                                0x73,
                                0x69,
                                0x61,
                                0x2e,
                                0x63,
                                0x6c,
                                0x03,
                                0x02,
                                0x12,
                                0x02);  // uris_
  EXPECT_TRUE(ContainersEqual(expected_buf, write_buf));
}

// Tests writing |AdvertisingData| to an output buffer that
// is too small fails gracefully and returns early.
TEST(AdvertisingDataTest, WriteBlockSmallBufError) {
  AdvertisingData data;

  data.SetTxPower(4);
  data.SetAppearance(0x4567);
  EXPECT_TRUE(data.SetLocalName("fuchsia"));

  DynamicByteBuffer write_buf(data.CalculateBlockSize() - 1);
  // The buffer is too small. No write should occur, and should return false.
  EXPECT_FALSE(data.WriteBlock(&write_buf, std::nullopt));
}

// Tests writing a fully populated |AdvertisingData| with provided flags to
// an output buffer succeeds.
TEST(AdvertisingDataTest, WriteBlockWithFlagsSuccess) {
  AdvertisingData data;

  data.SetTxPower(4);
  data.SetAppearance(0x4567);
  EXPECT_TRUE(data.SetLocalName("fuchsia"));

  StaticByteBuffer bytes(0x01, 0x02, 0x03);
  EXPECT_TRUE(data.SetManufacturerData(0x0123, bytes.view()));

  auto service_uuid = UUID(kId1As16);
  StaticByteBuffer service_bytes(0x01, 0x02);
  EXPECT_TRUE(data.AddServiceUuid(service_uuid));
  EXPECT_TRUE(data.SetServiceData(service_uuid, service_bytes.view()));

  EXPECT_TRUE(data.AddUri("http://fuchsia.cl"));

  DynamicByteBuffer write_buf(data.CalculateBlockSize(/*include_flags=*/true));
  EXPECT_TRUE(data.WriteBlock(&write_buf, AdvFlag::kLEGeneralDiscoverableMode));

  StaticByteBuffer expected_buf(0x02,
                                0x01,
                                0x02,  // flags: 2
                                0x02,
                                0x0a,
                                0x04,  // tx_power_level_: 4
                                0x03,
                                0x19,
                                0x67,
                                0x45,  // appearance_: 0x4567
                                0x08,
                                0x09,
                                0x66,
                                0x75,
                                0x63,
                                0x68,
                                0x73,
                                0x69,
                                0x61,  // local_name_: "fuchsia"
                                0x06,
                                0xff,
                                0x23,
                                0x01,
                                0x01,
                                0x02,
                                0x03,  // manufacturer_data_
                                0x05,
                                0x16,
                                0x12,
                                0x02,
                                0x01,
                                0x02,  // service_data_
                                0x0e,
                                0x24,
                                0x16,
                                0x2f,
                                0x2f,
                                0x66,
                                0x75,
                                0x63,
                                0x68,
                                0x73,
                                0x69,
                                0x61,
                                0x2e,
                                0x63,
                                0x6c,
                                0x03,
                                0x02,
                                0x12,
                                0x02);  // uris_
  EXPECT_TRUE(ContainersEqual(expected_buf, write_buf));
}

TEST(AdvertisingDataTest, WriteBlockWithFlagsBufError) {
  AdvertisingData data;

  data.SetTxPower(6);
  EXPECT_TRUE(data.SetLocalName("Fuchsia"));
  data.SetAppearance(0x1234);

  DynamicByteBuffer write_buf(data.CalculateBlockSize(/*include_flags=*/true) -
                              1);
  EXPECT_FALSE(
      data.WriteBlock(&write_buf, AdvFlag::kLEGeneralDiscoverableMode));
}

// Adds `n_(consecutively_increasing)_uuids` to `input` and returns the "next"
// UUID in the sequence. UUIDs may wrap around - this is OK, as we only care
// that they are all distinct.
UUID AddNDistinctUuids(AdvertisingData& input,
                       std::variant<uint16_t, uint32_t, UInt128> starting_uuid,
                       uint8_t n_uuids) {
  UUID next;
  for (uint8_t i = 0; true; ++i) {
    std::visit(
        [&](auto arg) {
          using T = std::decay_t<decltype(arg)>;
          if constexpr (std::is_same_v<T, UInt128>) {
            arg[0] += i;
            next = UUID(arg);
          } else {
            next = UUID(static_cast<T>(arg + i));
          }
        },
        starting_uuid);
    SCOPED_TRACE(
        bt_lib_cpp_string::StringPrintf("i: %du UUID: %s", i, bt_str(next)));
    if (i >= n_uuids) {
      return next;
    }
    EXPECT_TRUE(input.AddServiceUuid(next));
  }
}

TEST(AdvertisingDataTest, SetFieldsWithTooLongParameters) {
  AdvertisingData data;
  {
    // Use the https URI encoding scheme. This prefix will be compressed to one
    // byte when encoded.
    std::string uri = "https:";
    uri += std::string(kMaxEncodedUriLength - 1, '.');
    EXPECT_TRUE(data.AddUri(uri));
    uri += '.';
    EXPECT_FALSE(data.AddUri(uri));
  }
  // Attempt to set slightly too long service data.
  {
    UUID two_byte_uuid{kHeartRateServiceUuid};
    DynamicByteBuffer long_data(kMaxEncodedServiceDataLength - 1);
    long_data.Fill(0xAB);
    EXPECT_FALSE(data.SetServiceData(two_byte_uuid, long_data));
    // An empty DynamicByteBuffer represents unset service data per the header.
    EXPECT_TRUE(
        ContainersEqual(DynamicByteBuffer(), data.service_data(two_byte_uuid)));
    // Now use a view that is just small enough to fit when encoded
    BufferView view = long_data.view(/*pos=*/0, /*size=*/long_data.size() - 1);
    EXPECT_TRUE(data.SetServiceData(two_byte_uuid, view));
    EXPECT_TRUE(ContainersEqual(view, data.service_data(two_byte_uuid)));
  }
  // Attempt to set slightly too long manufacturer data.
  {
    uint16_t manufacturer_id{0xABBA};
    DynamicByteBuffer long_data(kMaxManufacturerDataLength + 1);
    long_data.Fill(0xAB);
    EXPECT_FALSE(data.SetManufacturerData(manufacturer_id, long_data.view()));
    // An empty DynamicByteBuffer represents unset service data per the header.
    EXPECT_TRUE(ContainersEqual(DynamicByteBuffer(),
                                data.manufacturer_data(manufacturer_id)));
    // Now use a view that is just small enough to fit when encoded
    BufferView view = long_data.view(/*pos=*/0, /*size=*/long_data.size() - 1);
    EXPECT_TRUE(data.SetManufacturerData(manufacturer_id, view));
    EXPECT_TRUE(ContainersEqual(view, data.manufacturer_data(manufacturer_id)));
  }
  // Ensure that service UUIDs are truncated when they do not fit.
  {
    uint16_t starting_16bit_uuid = 0x0001;
    UUID should_fail = AddNDistinctUuids(
        data,
        std::variant<uint16_t, uint32_t, UInt128>{starting_16bit_uuid},
        kMax16BitUuids);
    EXPECT_FALSE(data.AddServiceUuid(should_fail));
    EXPECT_TRUE(data.service_uuids().find(should_fail) ==
                data.service_uuids().end());

    // This value must not fit in a 16 bit number in order to count as a "32
    // bit" UUID
    uint32_t starting_32bit_uuid = std::numeric_limits<uint16_t>::max() + 1;
    should_fail = AddNDistinctUuids(
        data,
        std::variant<uint16_t, uint32_t, UInt128>{starting_32bit_uuid},
        kMax32BitUuids);
    EXPECT_FALSE(data.AddServiceUuid(should_fail));
    EXPECT_TRUE(data.service_uuids().find(should_fail) ==
                data.service_uuids().end());

    UInt128 starting_128bit_uuid = {0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB,
                                    0xAB};
    should_fail = AddNDistinctUuids(
        data,
        std::variant<uint16_t, uint32_t, UInt128>{starting_128bit_uuid},
        kMax128BitUuids);
    EXPECT_FALSE(data.AddServiceUuid(should_fail));
    EXPECT_TRUE(data.service_uuids().find(should_fail) ==
                data.service_uuids().end());
  }
  // Ensures names exceeding kMaxNameLength are rejected.
  {
    std::string name_that_fits(kMaxNameLength, 'a');
    std::string too_long_name(kMaxNameLength + 1, 'b');
    EXPECT_TRUE(data.SetLocalName(name_that_fits));
    EXPECT_EQ(name_that_fits, data.local_name()->name);
    EXPECT_FALSE(data.SetLocalName(too_long_name));
    EXPECT_EQ(name_that_fits, data.local_name()->name);
  }
  // Write the data out to ensure no assertions are triggered
  DynamicByteBuffer block(data.CalculateBlockSize());
  EXPECT_TRUE(data.WriteBlock(&block, std::nullopt));
}

// Tests that setting a complete local name overwrites an existing shortened
// local name and that setting a shortened local name has no effect if a
// complete local name is currently stored.
TEST(AdvertisingDataTest, CompleteLocalNameFavored) {
  AdvertisingData data;
  std::string short_name = "short";
  std::string complete_name = "complete";

  EXPECT_TRUE(data.SetLocalName(short_name, /*is_complete=*/false));
  EXPECT_EQ(short_name, data.local_name()->name);
  EXPECT_TRUE(data.SetLocalName(complete_name, /*is_complete=*/true));
  EXPECT_EQ(complete_name, data.local_name()->name);

  EXPECT_FALSE(data.SetLocalName(short_name, /*is_complete=*/false));
  EXPECT_EQ(complete_name, data.local_name()->name);
}

// Tests that even when the maximum number of distinct UUIDs for a certain size
// have been added to an AD, we do not reject additional UUIDs that are
// duplicates of already-added UUIDs.
TEST(AdvertisingDataTest, AddDuplicateServiceUuidsWhenFullSucceeds) {
  AdvertisingData data;
  uint16_t starting_16bit_uuid = 0x0001;
  UUID should_fail = AddNDistinctUuids(
      data,
      std::variant<uint16_t, uint32_t, UInt128>{starting_16bit_uuid},
      kMax16BitUuids);
  // Verify that adding another distinct UUID fails - i.e. we are at the limit.
  EXPECT_FALSE(data.AddServiceUuid(should_fail));
  EXPECT_TRUE(data.service_uuids().find(should_fail) ==
              data.service_uuids().end());
  // Verify that we are notified of success when adding an existing UUID
  EXPECT_TRUE(data.AddServiceUuid(UUID(starting_16bit_uuid)));
}
}  // namespace
}  // namespace bt
