/*
 * Copyright 2019 The Android Open Source Project
 *
 * 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
 *
 *      http://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 <gtest/gtest.h>

#include <forward_list>
#include <memory>

#define PACKET_TESTING

#include "packet/bit_inserter.h"
#include "packet/parser/test/big_endian_test_packets.h"
#include "packet/parser/test/six_bytes.h"
#include "packet/parser/test/test_packets.h"
#include "packet/raw_builder.h"

using ::bluetooth::packet::BitInserter;
using ::bluetooth::packet::kLittleEndian;
using ::bluetooth::packet::RawBuilder;
using ::bluetooth::packet::parser::test::SixBytes;
using std::vector;

namespace {
vector<uint8_t> child_two_two_three = {
        0x20 /* Reserved : 4, FourBits::TWO */,
        0x03 /* FourBits::THREE, Reserved : 4 */,
};
vector<uint8_t> child = {
        0x12 /* fixed */,
        0x02 /* Size of the payload */,
        0xa1 /* First byte of the payload */,
        0xa2,
        0xb1 /* footer */,
};
vector<uint8_t> child_with_six_bytes = {
        0x34 /* TwoBytes */,
        0x12,
        0xa1 /* First byte of the six_bytes */,
        0xa2,
        0xa3,
        0xa4,
        0xa5,
        0xa6,
        0xb1 /* Second six_bytes*/,
        0xb2,
        0xb3,
        0xb4,
        0xb5,
        0xb6,
};

}  // namespace

namespace bluetooth {
namespace packet {
namespace parser {
using namespace test;

TEST(GeneratedPacketTest, testChildTwoTwoThree) {
  auto packet = ChildTwoTwoThreeBuilder::Create();

  ASSERT_EQ(child_two_two_three.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), child_two_two_three.size());
  for (size_t i = 0; i < child_two_two_three.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentView wrong_view = ParentView::Create(packet_bytes_view);
  ASSERT_FALSE(wrong_view.IsValid());

  ParentTwoView parent_view = ParentTwoView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  ASSERT_EQ(FourBits::TWO, parent_view.GetFourBits());

  ChildTwoTwoView child_view = ChildTwoTwoView::Create(parent_view);
  ASSERT_TRUE(child_view.IsValid());
  ASSERT_EQ(FourBits::THREE, child_view.GetMoreBits());

  ChildTwoTwoThreeView grandchild_view = ChildTwoTwoThreeView::Create(child_view);
  ASSERT_TRUE(grandchild_view.IsValid());
}

TEST(GeneratedPacketTest, testChild) {
  uint16_t field_name = 0xa2a1;
  uint8_t footer = 0xb1;
  auto packet = ChildBuilder::Create(field_name, footer);

  ASSERT_EQ(child.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), child.size());
  for (size_t i = 0; i < child.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), child[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentView parent_view = ParentView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  auto payload = parent_view.GetPayload();

  ASSERT_EQ(child[1 /* skip fixed field */], payload.size());
  for (size_t i = 0; i < payload.size(); i++) {
    ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]);
  }

  ChildView child_view = ChildView::Create(parent_view);
  ASSERT_TRUE(child_view.IsValid());

  ASSERT_EQ(field_name, child_view.GetFieldName());
}

TEST(GeneratedPacketTest, testValidateWayTooSmall) {
  std::vector<uint8_t> too_small_bytes = {0x34};
  auto too_small =
          std::make_shared<std::vector<uint8_t>>(too_small_bytes.begin(), too_small_bytes.end());

  ParentWithSixBytesView invalid_parent =
          ParentWithSixBytesView::Create(PacketView<kLittleEndian>(too_small));
  ASSERT_FALSE(invalid_parent.IsValid());
  ChildWithSixBytesView invalid = ChildWithSixBytesView::Create(
          ParentWithSixBytesView::Create(PacketView<kLittleEndian>(too_small)));
  ASSERT_FALSE(invalid.IsValid());
}

TEST(GeneratedPacketTest, testValidateTooSmall) {
  std::vector<uint8_t> too_small_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11};
  auto too_small =
          std::make_shared<std::vector<uint8_t>>(too_small_bytes.begin(), too_small_bytes.end());

  ParentWithSixBytesView valid_parent =
          ParentWithSixBytesView::Create(PacketView<kLittleEndian>(too_small));
  ASSERT_TRUE(valid_parent.IsValid());
  ChildWithSixBytesView invalid = ChildWithSixBytesView::Create(
          ParentWithSixBytesView::Create(PacketView<kLittleEndian>(too_small)));
  ASSERT_FALSE(invalid.IsValid());
}

TEST(GeneratedPacketTest, testValidateJustRight) {
  std::vector<uint8_t> just_right_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05,
                                           0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
  auto just_right =
          std::make_shared<std::vector<uint8_t>>(just_right_bytes.begin(), just_right_bytes.end());

  ChildWithSixBytesView valid = ChildWithSixBytesView::Create(
          ParentWithSixBytesView::Create(PacketView<kLittleEndian>(just_right)));
  ASSERT_TRUE(valid.IsValid());
}

TEST(GeneratedPacketTest, testValidateTooBig) {
  std::vector<uint8_t> too_big_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x20};
  auto too_big = std::make_shared<std::vector<uint8_t>>(too_big_bytes.begin(), too_big_bytes.end());

  ChildWithSixBytesView lenient = ChildWithSixBytesView::Create(
          ParentWithSixBytesView::Create(PacketView<kLittleEndian>(too_big)));
  ASSERT_TRUE(lenient.IsValid());
}

TEST(GeneratedPacketTest, testValidateDeath) {
  auto packet = ChildTwoTwoThreeBuilder::Create();

  ASSERT_EQ(child_two_two_three.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), child_two_two_three.size());
  for (size_t i = 0; i < child_two_two_three.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentView wrong_view = ParentView::Create(packet_bytes_view);
  ASSERT_DEATH(wrong_view.GetPayload(), "validated");
}

TEST(GeneratedPacketTest, testValidatedParentDeath) {
  uint16_t field_name = 0xa2a1;
  uint8_t footer = 0xb1;
  auto packet = ChildBuilder::Create(field_name, footer);

  ASSERT_EQ(child.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), child.size());
  for (size_t i = 0; i < child.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), child[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentView parent_view = ParentView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  auto payload = parent_view.GetPayload();

  ASSERT_EQ(child[1 /* skip fixed field */], payload.size());
  for (size_t i = 0; i < payload.size(); i++) {
    ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]);
  }

  ChildView child_view = ChildView::Create(parent_view);
  ASSERT_DEATH(child_view.GetFieldName(), "validated");
}

vector<uint8_t> middle_four_bits = {
        0x95,  // low_two = ONE, next_four = FIVE, straddle = TEN
        0x8a,  // straddle = TEN, four_more = TWO, high_two = TWO
};

TEST(GeneratedPacketTest, testMiddleFourBitsPacket) {
  TwoBits low_two = TwoBits::ONE;
  FourBits next_four = FourBits::FIVE;
  FourBits straddle = FourBits::TEN;
  FourBits four_more = FourBits::TWO;
  TwoBits high_two = TwoBits::TWO;

  auto packet = MiddleFourBitsBuilder::Create(low_two, next_four, straddle, four_more, high_two);

  ASSERT_EQ(middle_four_bits.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), middle_four_bits.size());
  for (size_t i = 0; i < middle_four_bits.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), middle_four_bits[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  MiddleFourBitsView view = MiddleFourBitsView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  ASSERT_EQ(low_two, view.GetLowTwo());
  ASSERT_EQ(next_four, view.GetNextFour());
  ASSERT_EQ(straddle, view.GetStraddle());
  ASSERT_EQ(four_more, view.GetFourMore());
  ASSERT_EQ(high_two, view.GetHighTwo());
}

TEST(GeneratedPacketTest, testChildWithSixBytes) {
  SixBytes six_bytes_a{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};
  SixBytes six_bytes_b{{0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6}};
  auto packet = ChildWithSixBytesBuilder::Create(six_bytes_a, six_bytes_b);

  ASSERT_EQ(child_with_six_bytes.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), child_with_six_bytes.size());
  for (size_t i = 0; i < child_with_six_bytes.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), child_with_six_bytes[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentWithSixBytesView parent_view = ParentWithSixBytesView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  ASSERT_EQ(six_bytes_a, parent_view.GetSixBytes());

  ChildWithSixBytesView child_view = ChildWithSixBytesView::Create(parent_view);
  ASSERT_TRUE(child_view.IsValid());

  ASSERT_EQ(six_bytes_a, child_view.GetSixBytes());
  ASSERT_EQ(six_bytes_a, ((ParentWithSixBytesView)child_view).GetSixBytes());
  ASSERT_EQ(six_bytes_b, child_view.GetChildSixBytes());
}

namespace {
vector<uint8_t> parent_with_sum = {
        0x11 /* TwoBytes */,
        0x12,
        0x21 /* Sum Bytes */,
        0x22,
        0x43 /* Sum, excluding TwoBytes */,
        0x00,
};

}  // namespace

TEST(GeneratedPacketTest, testParentWithSum) {
  uint16_t two_bytes = 0x1211;
  uint16_t sum_bytes = 0x2221;
  auto packet = ParentWithSumBuilder::Create(two_bytes, sum_bytes,
                                             std::make_unique<packet::RawBuilder>());

  ASSERT_EQ(parent_with_sum.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), parent_with_sum.size());
  for (size_t i = 0; i < parent_with_sum.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), parent_with_sum[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentWithSumView parent_view = ParentWithSumView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());

  // Corrupt checksum
  packet_bytes->back()++;
  PacketView<kLittleEndian> corrupted_bytes_view(packet_bytes);
  ParentWithSumView corrupted_view = ParentWithSumView::Create(corrupted_bytes_view);
  ASSERT_FALSE(corrupted_view.IsValid());
}

namespace {
vector<uint8_t> child_with_nested_sum = {
        0x11 /* TwoBytes */,
        0x12,
        0x21 /* Sum Bytes */,
        0x22,
        0x31 /* More Bytes */,
        0x32,
        0x33,
        0x34,
        0xca /* Nested Sum */,
        0x00,
        0xd7 /* Sum, excluding TwoBytes */,
        0x01,
};

}  // namespace

TEST(GeneratedPacketTest, testChildWithNestedSum) {
  uint16_t two_bytes = 0x1211;
  uint16_t sum_bytes = 0x2221;
  uint32_t more_bytes = 0x34333231;
  auto packet = ChildWithNestedSumBuilder::Create(two_bytes, sum_bytes, more_bytes);

  ASSERT_EQ(child_with_nested_sum.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(packet_bytes->size(), child_with_nested_sum.size());
  for (size_t i = 0; i < child_with_nested_sum.size(); i++) {
    ASSERT_EQ(packet_bytes->at(i), child_with_nested_sum[i]);
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentWithSumView parent_view = ParentWithSumView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());

  ChildWithNestedSumView child_view = ChildWithNestedSumView::Create(parent_view);
  ASSERT_TRUE(child_view.IsValid());

  ASSERT_EQ(more_bytes, child_view.GetMoreBytes());
}

TEST(GeneratedPacketTest, testSizedWithSumBadSize) {
  vector<uint8_t> size_too_big{0x01, 0x02, 0x23, 0x11, 0x22, 0x33, 0x66, 0x00};

  auto shared_bytes = std::make_shared<std::vector<uint8_t>>(size_too_big);
  PacketView<kLittleEndian> packet_bytes_view(shared_bytes);

  auto sws = SizedWithSumView::Create(packet_bytes_view);
  ASSERT_FALSE(sws.IsValid());
}

namespace {
vector<uint8_t> parent_size_modifier = {
        0x02 /* Size */,
        0x11 /* TwoBytes */,
        0x12,
};

}  // namespace

TEST(GeneratedPacketTest, testParentSizeModifier) {
  uint16_t two_bytes = 0x1211;
  auto packet = ParentSizeModifierBuilder::Create(std::make_unique<RawBuilder>(), two_bytes);

  ASSERT_EQ(parent_size_modifier.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(parent_size_modifier.size(), packet_bytes->size());
  for (size_t i = 0; i < parent_size_modifier.size(); i++) {
    ASSERT_EQ(parent_size_modifier[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentSizeModifierView parent_view = ParentSizeModifierView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
}

namespace {
vector<uint8_t> child_size_modifier = {
        0x06 /* PayloadSize (TwoBytes + MoreBytes)*/,
        0x31 /* MoreBytes */,
        0x32,
        0x33,
        0x34,
        0x11 /* TwoBytes = 0x1211 */,
        0x12,
};

}  // namespace

TEST(GeneratedPacketTest, testChildSizeModifier) {
  uint16_t two_bytes = 0x1211;
  uint32_t more_bytes = 0x34333231;
  auto packet = ChildSizeModifierBuilder::Create(more_bytes);

  ASSERT_EQ(child_size_modifier.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(child_size_modifier.size(), packet_bytes->size());
  for (size_t i = 0; i < child_size_modifier.size(); i++) {
    ASSERT_EQ(child_size_modifier[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  ParentSizeModifierView parent_view = ParentSizeModifierView::Create(packet_bytes_view);
  ASSERT_TRUE(parent_view.IsValid());
  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());

  ChildSizeModifierView child_view = ChildSizeModifierView::Create(parent_view);
  ASSERT_TRUE(child_view.IsValid());

  ASSERT_EQ(more_bytes, child_view.GetMoreBytes());
}

namespace {
vector<uint8_t> fixed_array_enum{
        0x01,  // ONE
        0x00,
        0x02,  // TWO
        0x00,
        0x01,  // ONE_TWO
        0x02,
        0x02,  // TWO_THREE
        0x03,
        0xff,  // FFFF
        0xff,
};
}

TEST(GeneratedPacketTest, testFixedArrayEnum) {
  std::array<ForArrays, 5> fixed_array{{ForArrays::ONE, ForArrays::TWO, ForArrays::ONE_TWO,
                                        ForArrays::TWO_THREE, ForArrays::FFFF}};
  auto packet = FixedArrayEnumBuilder::Create(fixed_array);
  ASSERT_EQ(fixed_array_enum.size(), packet->size());

  // Verify that the packet is independent from the array.
  std::array<ForArrays, 5> copy_array(fixed_array);
  fixed_array[1] = ForArrays::ONE;

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(fixed_array_enum.size(), packet_bytes->size());
  for (size_t i = 0; i < fixed_array_enum.size(); i++) {
    ASSERT_EQ(fixed_array_enum[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = FixedArrayEnumView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetEnumArray();
  ASSERT_EQ(copy_array.size(), array.size());
  for (size_t i = 0; i < copy_array.size(); i++) {
    ASSERT_EQ(array[i], copy_array[i]);
  }
}

namespace {
vector<uint8_t> sized_array_enum{
        0x0a,  // _size_
        0x00,
        0x01,  // ONE
        0x00,
        0x02,  // TWO
        0x00,
        0x01,  // ONE_TWO
        0x02,
        0x02,  // TWO_THREE
        0x03,
        0xff,  // FFFF
        0xff,
};
}

TEST(GeneratedPacketTest, testSizedArrayEnum) {
  std::vector<ForArrays> sized_array{{ForArrays::ONE, ForArrays::TWO, ForArrays::ONE_TWO,
                                      ForArrays::TWO_THREE, ForArrays::FFFF}};
  auto packet = SizedArrayEnumBuilder::Create(sized_array);
  ASSERT_EQ(sized_array_enum.size(), packet->size());

  // Copy the original vector and modify it to make sure the packet is independent.
  std::vector<ForArrays> copy_array(sized_array);
  sized_array[1] = ForArrays::ONE;

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(sized_array_enum.size(), packet_bytes->size());
  for (size_t i = 0; i < sized_array_enum.size(); i++) {
    ASSERT_EQ(sized_array_enum[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = SizedArrayEnumView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetEnumArray();
  ASSERT_EQ(copy_array.size(), array.size());
  for (size_t i = 0; i < copy_array.size(); i++) {
    ASSERT_EQ(array[i], copy_array[i]);
  }
}

namespace {
vector<uint8_t> count_array_enum{
        0x03,  // _count_
        0x01,  // ONE
        0x00,
        0x02,  // TWO_THREE
        0x03,
        0xff,  // FFFF
        0xff,
};
}

TEST(GeneratedPacketTest, testCountArrayEnum) {
  std::vector<ForArrays> count_array{{ForArrays::ONE, ForArrays::TWO_THREE, ForArrays::FFFF}};
  auto packet = CountArrayEnumBuilder::Create(count_array);
  ASSERT_EQ(count_array_enum.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(count_array_enum.size(), packet_bytes->size());
  for (size_t i = 0; i < count_array_enum.size(); i++) {
    ASSERT_EQ(count_array_enum[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = CountArrayEnumView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetEnumArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i], count_array[i]);
  }
}

TEST(GeneratedPacketTest, testFixedSizeByteArray) {
  constexpr std::size_t byte_array_size = 32;
  std::array<uint8_t, byte_array_size> byte_array;
  for (uint8_t i = 0; i < byte_array_size; i++) {
    byte_array[i] = i;
  }

  constexpr int word_array_size = 8;
  std::array<uint32_t, word_array_size> word_array;
  for (uint32_t i = 0; i < word_array_size; i++) {
    word_array[i] = i;
  }

  auto packet = PacketWithFixedArraysOfBytesBuilder::Create(byte_array, word_array);
  ASSERT_EQ((size_t)(2 * (256 / 8)), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(byte_array_size + word_array_size * sizeof(uint32_t), packet_bytes->size());

  for (size_t i = 0; i < byte_array_size; i++) {
    ASSERT_EQ(byte_array[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = PacketWithFixedArraysOfBytesView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetFixed256bitInBytes();
  ASSERT_EQ(byte_array.size(), array.size());
  for (size_t i = 0; i < array.size(); i++) {
    ASSERT_EQ(array[i], byte_array[i]);
  }

  auto decoded_word_array = view.GetFixed256bitInWords();
  ASSERT_EQ(word_array.size(), decoded_word_array.size());
  for (size_t i = 0; i < decoded_word_array.size(); i++) {
    ASSERT_EQ(word_array[i], decoded_word_array[i]);
  }
}

vector<uint8_t> one_variable{
        0x03, 'o', 'n', 'e',  // "one"
};

TEST(GeneratedPacketTest, testOneVariableField) {
  Variable variable_one{"one"};

  auto packet = OneVariableBuilder::Create(variable_one);
  ASSERT_EQ(one_variable.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_variable.size(), packet_bytes->size());
  for (size_t i = 0; i < one_variable.size(); i++) {
    ASSERT_EQ(one_variable[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one->data, variable_one.data);
}

vector<uint8_t> fou_variable{
        0x04, 'f', 'o', 'u',  // too short
};

TEST(GeneratedPacketTest, testOneVariableFieldTooShort) {
  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(fou_variable);
  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one, nullptr);
}

vector<uint8_t> sized_array_variable{
        0x0e,                           // _size_
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
};

TEST(GeneratedPacketTest, testSizedArrayVariableLength) {
  std::vector<Variable> sized_array;
  sized_array.emplace_back("one");
  sized_array.emplace_back("two");
  sized_array.emplace_back("three");

  auto packet = SizedArrayVariableBuilder::Create(sized_array);
  ASSERT_EQ(sized_array_variable.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(sized_array_variable.size(), packet_bytes->size());
  for (size_t i = 0; i < sized_array_variable.size(); i++) {
    ASSERT_EQ(sized_array_variable[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = SizedArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(sized_array.size(), array.size());
  for (size_t i = 0; i < sized_array.size(); i++) {
    ASSERT_EQ(array[i].data, sized_array[i].data);
  }
}

vector<uint8_t> sized_array_variable_too_short{
        0x0e,                           // _size_
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x06, 't', 'h', 'r', 'e', 'e',  // "three" needs another letter to be length 6
};

TEST(GeneratedPacketTest, testSizedArrayVariableLengthLastBad) {
  std::vector<Variable> sized_array;
  sized_array.emplace_back("one");
  sized_array.emplace_back("two");

  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(sized_array_variable_too_short);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = SizedArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(sized_array.size(), array.size());
  for (size_t i = 0; i < sized_array.size(); i++) {
    ASSERT_EQ(array[i].data, sized_array[i].data);
  }
}

vector<uint8_t> sized_array_variable_first_too_short{
        0x0e,                           // _size_
        0x02, 'o', 'n', 'e',            // "on"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
};

TEST(GeneratedPacketTest, testSizedArrayVariableLengthFirstBad) {
  std::vector<Variable> sized_array;
  sized_array.emplace_back("on");

  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(sized_array_variable_first_too_short);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = SizedArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(sized_array.size(), array.size());
  for (size_t i = 0; i < sized_array.size(); i++) {
    ASSERT_EQ(array[i].data, sized_array[i].data);
  }
}

vector<uint8_t> fixed_array_variable{
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
        0x04, 'f', 'o', 'u', 'r',       // "four"
        0x04, 'f', 'i', 'v', 'e',       // "five"
};

TEST(GeneratedPacketTest, testFixedArrayVariableLength) {
  std::array<Variable, 5> fixed_array{std::string("one"), std::string("two"), std::string("three"),
                                      std::string("four"), std::string("five")};

  auto packet = FixedArrayVariableBuilder::Create(fixed_array);
  ASSERT_EQ(fixed_array_variable.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(fixed_array_variable.size(), packet_bytes->size());
  for (size_t i = 0; i < fixed_array_variable.size(); i++) {
    ASSERT_EQ(fixed_array_variable[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = FixedArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(fixed_array.size(), array.size());
  for (size_t i = 0; i < fixed_array.size(); i++) {
    ASSERT_EQ(array[i].data, fixed_array[i].data);
  }
}

vector<uint8_t> fixed_array_variable_too_short{
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
        0x04, 'f', 'o', 'u', 'r',       // "four"
        0x05, 'f', 'i', 'v', 'e',       // "five"
};

TEST(GeneratedPacketTest, testFixedArrayVariableLengthTooShort) {
  std::array<Variable, 5> fixed_array{std::string("one"), std::string("two"), std::string("three"),
                                      std::string("four")};

  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(fixed_array_variable_too_short);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = FixedArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(fixed_array.size(), array.size());
  for (size_t i = 0; i < fixed_array.size(); i++) {
    ASSERT_EQ(array[i].data, fixed_array[i].data);
  }
}

vector<uint8_t> count_array_variable{
        0x04,                           // _count_
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
        0x04, 'f', 'o', 'u', 'r',       // "four"
};

TEST(GeneratedPacketTest, testCountArrayVariableLength) {
  std::vector<Variable> count_array;
  count_array.emplace_back("one");
  count_array.emplace_back("two");
  count_array.emplace_back("three");
  count_array.emplace_back("four");

  auto packet = CountArrayVariableBuilder::Create(count_array);
  ASSERT_EQ(count_array_variable.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(count_array_variable.size(), packet_bytes->size());
  for (size_t i = 0; i < count_array_variable.size(); i++) {
    ASSERT_EQ(count_array_variable[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = CountArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i].data, count_array[i].data);
  }
}

vector<uint8_t> count_array_variable_extra{
        0x04,                           // _count_
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
        0x04, 'f', 'o', 'u', 'r',       // "four"
        0x04, 'x', 't', 'r', 'a',       // "xtra"
};

TEST(GeneratedPacketTest, testCountArrayVariableLengthExtraData) {
  std::vector<Variable> count_array;
  count_array.emplace_back("one");
  count_array.emplace_back("two");
  count_array.emplace_back("three");
  count_array.emplace_back("four");

  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(count_array_variable_extra);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = CountArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i].data, count_array[i].data);
  }
}

vector<uint8_t> count_array_variable_too_few{
        0x04,                           // _count_
        0x03, 'o', 'n', 'e',            // "one"
        0x03, 't', 'w', 'o',            // "two"
        0x05, 't', 'h', 'r', 'e', 'e',  // "three"
};

TEST(GeneratedPacketTest, testCountArrayVariableLengthMissingData) {
  std::vector<Variable> count_array;
  count_array.emplace_back("one");
  count_array.emplace_back("two");
  count_array.emplace_back("three");

  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(count_array_variable_too_few);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = CountArrayVariableView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetVariableArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i].data, count_array[i].data);
  }
}

vector<uint8_t> one_struct{
        0x01, 0x02, 0x03,  // id = 0x01, count = 0x0302
};

TEST(GeneratedPacketTest, testOneStruct) {
  TwoRelatedNumbers trn;
  trn.id_ = 1;
  trn.count_ = 0x0302;

  auto packet = OneStructBuilder::Create(trn);
  ASSERT_EQ(one_struct.size(), packet->size());

  // Copy the original struct, then modify it to verify independence from the packet.
  TwoRelatedNumbers copy_trn(trn);
  trn.id_ = 2;

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_struct.size(), packet_bytes->size());
  for (size_t i = 0; i < one_struct.size(); i++) {
    ASSERT_EQ(one_struct[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one.id_, copy_trn.id_);
  ASSERT_EQ(one.count_, copy_trn.count_);
}

vector<uint8_t> two_structs{
        0x01, 0x01, 0x02,  // id, id * 0x0201
        0x02, 0x02, 0x04,
};

TEST(GeneratedPacketTest, testTwoStructs) {
  std::vector<TwoRelatedNumbers> count_array;
  for (uint8_t i = 1; i < 3; i++) {
    TwoRelatedNumbers trn;
    trn.id_ = i;
    trn.count_ = 0x0201 * i;
    count_array.push_back(trn);
  }

  auto packet = TwoStructsBuilder::Create(count_array[0], count_array[1]);
  ASSERT_EQ(two_structs.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(two_structs.size(), packet_bytes->size());
  for (size_t i = 0; i < two_structs.size(); i++) {
    ASSERT_EQ(two_structs[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = TwoStructsView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one.id_, count_array[0].id_);
  ASSERT_EQ(one.count_, count_array[0].count_);
  auto two = view.GetTwo();
  ASSERT_EQ(two.id_, count_array[1].id_);
  ASSERT_EQ(two.count_, count_array[1].count_);
}

vector<uint8_t> array_or_vector_of_struct{
        0x04,              // _count_
        0x01, 0x01, 0x02,  // id, id * 0x0201
        0x02, 0x02, 0x04, 0x03, 0x03, 0x06, 0x04, 0x04, 0x08,
};

TEST(GeneratedPacketTest, testVectorOfStruct) {
  std::vector<TwoRelatedNumbers> count_array;
  for (uint8_t i = 1; i < 5; i++) {
    TwoRelatedNumbers trn;
    trn.id_ = i;
    trn.count_ = 0x0201 * i;
    count_array.push_back(trn);
  }

  // Make a copy
  std::vector<TwoRelatedNumbers> copy_array(count_array);

  auto packet = VectorOfStructBuilder::Create(count_array);

  // Change the original vector to make sure a copy was made.
  count_array[0].id_ = count_array[0].id_ + 1;

  ASSERT_EQ(array_or_vector_of_struct.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(array_or_vector_of_struct.size(), packet_bytes->size());
  for (size_t i = 0; i < array_or_vector_of_struct.size(); i++) {
    ASSERT_EQ(array_or_vector_of_struct[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = VectorOfStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(copy_array.size(), array.size());
  for (size_t i = 0; i < copy_array.size(); i++) {
    ASSERT_EQ(array[i].id_, copy_array[i].id_);
    ASSERT_EQ(array[i].count_, copy_array[i].count_);
  }
}

TEST(GeneratedPacketTest, testArrayOfStruct) {
  std::array<TwoRelatedNumbers, 4> count_array;
  for (uint8_t i = 1; i < 5; i++) {
    TwoRelatedNumbers trn;
    trn.id_ = i;
    trn.count_ = 0x0201 * i;
    count_array[i - 1] = trn;
  }

  // Make a copy
  std::array<TwoRelatedNumbers, 4> copy_array(count_array);

  auto packet = ArrayOfStructBuilder::Create(4, count_array);

  // Change the original vector to make sure a copy was made.
  count_array[0].id_ = count_array[0].id_ + 1;

  ASSERT_EQ(array_or_vector_of_struct.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(array_or_vector_of_struct.size(), packet_bytes->size());
  for (size_t i = 0; i < array_or_vector_of_struct.size(); i++) {
    ASSERT_EQ(array_or_vector_of_struct[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = ArrayOfStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(copy_array.size(), array.size());
  for (size_t i = 0; i < copy_array.size(); i++) {
    ASSERT_EQ(array[i].id_, copy_array[i].id_);
    ASSERT_EQ(array[i].count_, copy_array[i].count_);
  }
}

vector<uint8_t> one_fixed_types_struct{
        0x05,                                // four_bits = FIVE, reserved
        0xf3,                                // _fixed_
        0x0d,                                // id = 0x0d
        0x01, 0x02, 0x03,                    // array = { 1, 2, 3}
        0x06, 0x01,                          // example_checksum
        0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,  // six_bytes
};

TEST(GeneratedPacketTest, testOneFixedTypesStruct) {
  StructWithFixedTypes swf;
  swf.four_bits_ = FourBits::FIVE;
  swf.id_ = 0x0d;
  swf.array_ = {{0x01, 0x02, 0x03}};
  swf.six_bytes_ = SixBytes{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};

  auto packet = OneFixedTypesStructBuilder::Create(swf);
  ASSERT_EQ(one_fixed_types_struct.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_fixed_types_struct.size(), packet_bytes->size());
  for (size_t i = 0; i < one_fixed_types_struct.size(); i++) {
    ASSERT_EQ(one_fixed_types_struct[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneFixedTypesStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one.four_bits_, swf.four_bits_);
  ASSERT_EQ(one.id_, swf.id_);
  ASSERT_EQ(one.array_, swf.array_);
  ASSERT_EQ(one.six_bytes_, swf.six_bytes_);
}

vector<uint8_t> array_of_struct_and_another{
        0x03,              // _count_
        0x01, 0x01, 0x02,  // id, id * 0x0201
        0x02, 0x02, 0x04,  // 2
        0x03, 0x03, 0x06,  // 3
        0x04, 0x04, 0x08,  // Another
};

TEST(GeneratedPacketTest, testArrayOfStructAndAnother) {
  std::vector<TwoRelatedNumbers> count_array;
  for (uint8_t i = 1; i < 4; i++) {
    TwoRelatedNumbers trn;
    trn.id_ = i;
    trn.count_ = 0x0201 * i;
    count_array.push_back(trn);
  }
  TwoRelatedNumbers another;
  another.id_ = 4;
  another.count_ = 0x0201 * 4;

  auto packet = ArrayOfStructAndAnotherBuilder::Create(count_array, another);
  ASSERT_EQ(array_or_vector_of_struct.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(array_of_struct_and_another.size(), packet_bytes->size());
  for (size_t i = 0; i < array_of_struct_and_another.size(); i++) {
    ASSERT_EQ(array_of_struct_and_another[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = ArrayOfStructAndAnotherView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i].id_, count_array[i].id_);
    ASSERT_EQ(array[i].count_, count_array[i].count_);
  }
  auto nother = view.GetAnother();
  ASSERT_EQ(nother.id_, another.id_);
  ASSERT_EQ(nother.count_, another.count_);
}

DEFINE_AND_INSTANTIATE_OneArrayOfStructAndAnotherStructReflectionTest(array_of_struct_and_another);

TEST(GeneratedPacketTest, testOneArrayOfStructAndAnotherStruct) {
  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(array_of_struct_and_another);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneArrayOfStructAndAnotherStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one.array_.size(), 3ul);
  ASSERT_EQ(one.another_.id_, 4);
  ASSERT_EQ(one.another_.count_, 0x0804);
}

vector<uint8_t> sized_array_of_struct_and_another{
        0x09,              // _size_
        0x01, 0x01, 0x02,  // id, id * 0x0201
        0x02, 0x02, 0x04,  // 2
        0x03, 0x03, 0x06,  // 3
        0x04, 0x04, 0x08,  // Another
};

DEFINE_AND_INSTANTIATE_OneSizedArrayOfStructAndAnotherStructReflectionTest(
        sized_array_of_struct_and_another);

vector<uint8_t> bit_field_group_packet{
        // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
        0xf7,  // 0x77 | (0x5 & 0x1) << 7
        0xaa,  //  0x15 << 3 | (0x5 >> 1)
};

TEST(GeneratedPacketTest, testBitFieldGroupPacket) {
  uint8_t seven_bits = 0x77;
  uint8_t straddle = 0x5;
  uint8_t five_bits = 0x15;

  auto packet = BitFieldGroupPacketBuilder::Create(seven_bits, straddle, five_bits);
  ASSERT_EQ(bit_field_group_packet.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(bit_field_group_packet.size(), packet_bytes->size());
  for (size_t i = 0; i < bit_field_group_packet.size(); i++) {
    ASSERT_EQ(bit_field_group_packet[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = BitFieldGroupPacketView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  ASSERT_EQ(seven_bits, view.GetSevenBits());
  ASSERT_EQ(straddle, view.GetStraddle());
  ASSERT_EQ(five_bits, view.GetFiveBits());
}

vector<uint8_t> bit_field_packet{
        // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
        0xf7,  // 0x77 | (0x5 & 0x1) << 7
        0xaa,  //  0x15 << 3 | (0x5 >> 1)
};

TEST(GeneratedPacketTest, testBitFieldPacket) {
  BitField bit_field;
  bit_field.seven_bits_ = 0x77;
  bit_field.straddle_ = 0x5;
  bit_field.five_bits_ = 0x15;

  auto packet = BitFieldPacketBuilder::Create(bit_field);
  ASSERT_EQ(bit_field_packet.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(bit_field_packet.size(), packet_bytes->size());
  for (size_t i = 0; i < bit_field_packet.size(); i++) {
    ASSERT_EQ(bit_field_packet[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = BitFieldPacketView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  BitField bf = view.GetBitField();
  ASSERT_EQ(bf.seven_bits_, bit_field.seven_bits_);
  ASSERT_EQ(bf.straddle_, bit_field.straddle_);
  ASSERT_EQ(bf.five_bits_, bit_field.five_bits_);
}

vector<uint8_t> bit_field_group_after_unsized_array_packet{
        0x01, 0x02, 0x03, 0x04,  // byte array
        // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
        0xf7,  // 0x77 | (0x5 & 0x1) << 7
        0xaa,  //  0x15 << 3 | (0x5 >> 1)
};

TEST(GeneratedPacketTest, testBitFieldGroupAfterUnsizedArrayPacket) {
  std::vector<uint8_t> count_array;
  for (uint8_t i = 1; i < 5; i++) {
    count_array.push_back(i);
  }
  uint8_t seven_bits = 0x77;
  uint8_t straddle = 0x5;
  uint8_t five_bits = 0x15;

  auto packet = BitFieldGroupAfterUnsizedArrayPacketBuilder::Create(count_array, seven_bits,
                                                                    straddle, five_bits);
  ASSERT_EQ(bit_field_group_after_unsized_array_packet.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(bit_field_group_after_unsized_array_packet.size(), packet_bytes->size());
  for (size_t i = 0; i < bit_field_group_after_unsized_array_packet.size(); i++) {
    ASSERT_EQ(bit_field_group_after_unsized_array_packet[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto payload_view = BitFieldGroupAfterPayloadPacketView::Create(packet_bytes_view);
  ASSERT_TRUE(payload_view.IsValid());
  EXPECT_EQ(seven_bits, payload_view.GetSevenBits());
  EXPECT_EQ(straddle, payload_view.GetStraddle());
  EXPECT_EQ(five_bits, payload_view.GetFiveBits());

  auto view = BitFieldGroupAfterUnsizedArrayPacketView::Create(payload_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i], count_array[i]);
  }
  ASSERT_EQ(seven_bits, view.GetSevenBits());
  ASSERT_EQ(straddle, view.GetStraddle());
  ASSERT_EQ(five_bits, view.GetFiveBits());
}

vector<uint8_t> bit_field_after_unsized_array_packet{
        0x01, 0x02, 0x03, 0x04,  // byte array
        // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
        0xf7,  // 0x77 | (0x5 & 0x1) << 7
        0xaa,  //  0x15 << 3 | (0x5 >> 1)
};

TEST(GeneratedPacketTest, testBitFieldAfterUnsizedArrayPacket) {
  std::vector<uint8_t> count_array;
  for (uint8_t i = 1; i < 5; i++) {
    count_array.push_back(i);
  }
  BitField bit_field;
  bit_field.seven_bits_ = 0x77;
  bit_field.straddle_ = 0x5;
  bit_field.five_bits_ = 0x15;

  auto packet = BitFieldAfterUnsizedArrayPacketBuilder::Create(count_array, bit_field);
  ASSERT_EQ(bit_field_after_unsized_array_packet.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(bit_field_after_unsized_array_packet.size(), packet_bytes->size());
  for (size_t i = 0; i < bit_field_after_unsized_array_packet.size(); i++) {
    ASSERT_EQ(bit_field_after_unsized_array_packet[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto payload_view = BitFieldAfterPayloadPacketView::Create(packet_bytes_view);
  ASSERT_TRUE(payload_view.IsValid());
  BitField parent_bf = payload_view.GetBitField();
  ASSERT_EQ(parent_bf.seven_bits_, bit_field.seven_bits_);
  ASSERT_EQ(parent_bf.straddle_, bit_field.straddle_);
  ASSERT_EQ(parent_bf.five_bits_, bit_field.five_bits_);

  auto view = BitFieldAfterUnsizedArrayPacketView::Create(payload_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i], count_array[i]);
  }
  BitField bf = view.GetBitField();
  ASSERT_EQ(bf.seven_bits_, bit_field.seven_bits_);
  ASSERT_EQ(bf.straddle_, bit_field.straddle_);
  ASSERT_EQ(bf.five_bits_, bit_field.five_bits_);
}

vector<uint8_t> bit_field_array_packet{
        0x06,  // _size_(array)
        // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
        0xf7,  // 0x77 | (0x5 & 0x1) << 7
        0xaa,  //  0x15 << 3 | (0x5 >> 1)

        // seven_bits_ = 0x78, straddle_ = 0x6, five_bits_ = 0x16
        0x78,  // 0x78 | (0x6 & 0x1) << 7
        0xb3,  //  0x16 << 3 | (0x6 >> 1)

        // seven_bits_ = 0x79, straddle_ = 0x7, five_bits_ = 0x17
        0xf9,  // 0x79 | (0x7 & 0x1) << 7
        0xbb,  //  0x17 << 3 | (0x7 >> 1)
};

TEST(GeneratedPacketTest, testBitFieldArrayPacket) {
  std::vector<BitField> count_array;
  for (size_t i = 0; i < 3; i++) {
    BitField bf;
    bf.seven_bits_ = 0x77 + i;
    bf.straddle_ = 0x5 + i;
    bf.five_bits_ = 0x15 + i;
    count_array.push_back(bf);
  }

  auto packet = BitFieldArrayPacketBuilder::Create(count_array);
  ASSERT_EQ(bit_field_array_packet.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(bit_field_array_packet.size(), packet_bytes->size());
  for (size_t i = 0; i < bit_field_array_packet.size(); i++) {
    ASSERT_EQ(bit_field_array_packet[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = BitFieldArrayPacketView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i].seven_bits_, count_array[i].seven_bits_);
    ASSERT_EQ(array[i].straddle_, count_array[i].straddle_);
    ASSERT_EQ(array[i].five_bits_, count_array[i].five_bits_);
  }
}

TEST(GeneratedPacketTest, testNewBitFieldArrayPacket) {
  PacketView<kLittleEndian> packet_bytes_view(
          std::make_shared<std::vector<uint8_t>>(bit_field_array_packet));
  auto view = BitFieldArrayPacketView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());

  auto packet = BitFieldArrayPacketBuilder::Create(view.GetArray());
  ASSERT_EQ(bit_field_array_packet.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(*packet_bytes, bit_field_array_packet);
}

std::vector<uint8_t> child_two_two_two_ = {0x20, 0x02};
std::vector<uint8_t> child_two_two_three_ = {0x20, 0x03};
std::vector<uint8_t> child_two_two_four_ = {0x20, 0x04};

DEFINE_AND_INSTANTIATE_ParentTwoReflectionTest(child_two_two_two_, child_two_two_three_,
                                               child_two_two_four_);

DEFINE_AND_INSTANTIATE_ChildTwoTwoReflectionTest(child_two_two_two_, child_two_two_three_,
                                                 child_two_two_four_);

DEFINE_AND_INSTANTIATE_ChildTwoTwoThreeReflectionTest(child_two_two_three_);

std::vector<uint8_t> one_versionless_struct_packet = {0x01};
std::vector<uint8_t> one_versioned_struct_packet = {0x02, 0x03 /* version */, 0x04, 0x05, 0x06};
std::vector<uint8_t> one_version_one_struct_packet = {0x03, 0x01 /* version */, 0x02};
std::vector<uint8_t> one_version_two_struct_packet = {0x03, 0x02 /* version */, 0x03, 0x04};
DEFINE_AND_INSTANTIATE_OneVersionlessStructPacketReflectionTest(one_versionless_struct_packet,
                                                                one_versioned_struct_packet,
                                                                one_version_one_struct_packet,
                                                                one_version_two_struct_packet);
DEFINE_AND_INSTANTIATE_OneVersionedStructPacketReflectionTest(one_versioned_struct_packet,
                                                              one_version_one_struct_packet,
                                                              one_version_two_struct_packet);
DEFINE_AND_INSTANTIATE_OneVersionOneStructPacketReflectionTest(one_version_one_struct_packet);
DEFINE_AND_INSTANTIATE_OneVersionTwoStructPacketReflectionTest(one_version_two_struct_packet);

vector<uint8_t> one_struct_be{
        0x01, 0x02, 0x03,  // id = 0x01, count = 0x0203
};

TEST(GeneratedPacketTest, testOneStructBe) {
  TwoRelatedNumbersBe trn;
  trn.id_ = 1;
  trn.count_ = 0x0203;

  auto packet = OneStructBeBuilder::Create(trn);
  ASSERT_EQ(one_struct_be.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_struct_be.size(), packet_bytes->size());
  for (size_t i = 0; i < one_struct_be.size(); i++) {
    ASSERT_EQ(one_struct_be[i], packet_bytes->at(i));
  }

  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneStructBeView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one.id_, trn.id_);
  ASSERT_EQ(one.count_, trn.count_);
}

vector<uint8_t> two_structs_be{
        0x01, 0x01, 0x02,  // id, id * 0x0102
        0x02, 0x02, 0x04,
};

TEST(GeneratedPacketTest, testTwoStructsBe) {
  std::vector<TwoRelatedNumbersBe> count_array;
  for (uint8_t i = 1; i < 3; i++) {
    TwoRelatedNumbersBe trn;
    trn.id_ = i;
    trn.count_ = 0x0102 * i;
    count_array.push_back(trn);
  }

  auto packet = TwoStructsBeBuilder::Create(count_array[0], count_array[1]);
  ASSERT_EQ(two_structs_be.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(two_structs_be.size(), packet_bytes->size());
  for (size_t i = 0; i < two_structs_be.size(); i++) {
    ASSERT_EQ(two_structs_be[i], packet_bytes->at(i));
  }

  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = TwoStructsBeView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOne();
  ASSERT_EQ(one.id_, count_array[0].id_);
  ASSERT_EQ(one.count_, count_array[0].count_);
  auto two = view.GetTwo();
  ASSERT_EQ(two.id_, count_array[1].id_);
  ASSERT_EQ(two.count_, count_array[1].count_);
}

vector<uint8_t> array_of_struct_be{
        0x04,              // _count_
        0x01, 0x01, 0x02,  // id, id * 0x0102
        0x02, 0x02, 0x04, 0x03, 0x03, 0x06, 0x04, 0x04, 0x08,
};

TEST(GeneratedPacketTest, testArrayOfStructBe) {
  std::vector<TwoRelatedNumbersBe> count_array;
  for (uint8_t i = 1; i < 5; i++) {
    TwoRelatedNumbersBe trn;
    trn.id_ = i;
    trn.count_ = 0x0102 * i;
    count_array.push_back(trn);
  }

  auto packet = ArrayOfStructBeBuilder::Create(count_array);

  ASSERT_EQ(array_of_struct_be.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(array_of_struct_be.size(), packet_bytes->size());
  for (size_t i = 0; i < array_of_struct_be.size(); i++) {
    ASSERT_EQ(array_of_struct_be[i], packet_bytes->at(i));
  }

  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = ArrayOfStructBeView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto array = view.GetArray();
  ASSERT_EQ(count_array.size(), array.size());
  for (size_t i = 0; i < count_array.size(); i++) {
    ASSERT_EQ(array[i].id_, count_array[i].id_);
    ASSERT_EQ(array[i].count_, count_array[i].count_);
  }
}

vector<uint8_t> one_four_byte_struct{
        0x04,                    // struct_type_ = FourByte
        0xd1, 0xd2, 0xd3, 0xd4,  // four_bytes_
};

TEST(GeneratedPacketTest, testOneFourByteStruct) {
  FourByteStruct four_byte_struct(0xd4d3d2d1);

  auto packet = OneFourByteStructBuilder::Create(four_byte_struct);
  ASSERT_EQ(one_four_byte_struct.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_four_byte_struct.size(), packet_bytes->size());
  for (size_t i = 0; i < one_four_byte_struct.size(); i++) {
    ASSERT_EQ(one_four_byte_struct[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneFourByteStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  ASSERT_EQ(StructType::FOUR_BYTE, view.GetOneStruct().struct_type_);
  ASSERT_EQ(four_byte_struct.four_bytes_, view.GetOneStruct().four_bytes_);
}

vector<uint8_t> generic_struct_two{
        0x02,        // struct_type_ = TwoByte
        0x01, 0x02,  // two_bytes_
};

TEST(GeneratedPacketTest, testOneGenericStructTwo) {
  TwoByteStruct two_byte_struct(0x0201);
  std::unique_ptr<TwoByteStruct> two_byte_struct_ptr =
          std::make_unique<TwoByteStruct>(two_byte_struct);

  auto packet = OneGenericStructBuilder::Create(std::move(two_byte_struct_ptr));
  ASSERT_EQ(generic_struct_two.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(generic_struct_two.size(), packet_bytes->size());
  for (size_t i = 0; i < generic_struct_two.size(); i++) {
    ASSERT_EQ(generic_struct_two[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneGenericStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto base_struct = view.GetBaseStruct();
  ASSERT_NE(nullptr, base_struct);
  ASSERT_TRUE(TwoByteStruct::IsInstance(*base_struct));
  TwoByteStruct* two_byte = static_cast<TwoByteStruct*>(base_struct.get());
  ASSERT_NE(nullptr, two_byte);
  ASSERT_TRUE(TwoByteStruct::IsInstance(*two_byte));
  ASSERT_EQ(two_byte_struct.two_bytes_, 0x0201);
  uint16_t val = two_byte->two_bytes_;
  ASSERT_EQ(val, 0x0201);
  ASSERT_EQ(two_byte_struct.two_bytes_, ((TwoByteStruct*)base_struct.get())->two_bytes_);
}

vector<uint8_t> generic_struct_four{
        0x04,                    // struct_type_ = FourByte
        0x01, 0x02, 0x03, 0x04,  // four_bytes_
};

TEST(GeneratedPacketTest, testOneGenericStructFour) {
  FourByteStruct four_byte_struct(0x04030201);
  std::unique_ptr<FourByteStruct> four_byte_struct_p =
          std::make_unique<FourByteStruct>(four_byte_struct);
  ASSERT_EQ(four_byte_struct.four_bytes_, four_byte_struct_p->four_bytes_);

  auto packet = OneGenericStructBuilder::Create(std::move(four_byte_struct_p));
  ASSERT_EQ(generic_struct_four.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(generic_struct_four.size(), packet_bytes->size());
  for (size_t i = 0; i < generic_struct_four.size(); i++) {
    ASSERT_EQ(generic_struct_four[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneGenericStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto base_struct = view.GetBaseStruct();
  ASSERT_NE(nullptr, base_struct);
  ASSERT_EQ(StructType::FOUR_BYTE, base_struct->struct_type_);
  ASSERT_EQ(four_byte_struct.four_bytes_, ((FourByteStruct*)base_struct.get())->four_bytes_);
}

vector<uint8_t> one_struct_array{
        0x04,                    // struct_type_ = FourByte
        0xa1, 0xa2, 0xa3, 0xa4,  // four_bytes_
        0x04,                    // struct_type_ = FourByte
        0xb2, 0xb2, 0xb3, 0xb4,  // four_bytes_
        0x02,                    // struct_type_ = TwoByte
        0xc3, 0xc2,              // two_bytes_
        0x04,                    // struct_type_ = TwoByte
        0xd4, 0xd2, 0xd3, 0xd4,  // four_bytes_
};

TEST(GeneratedPacketTest, testOneGenericStructArray) {
  std::vector<std::unique_ptr<UnusedParentStruct>> parent_vector;
  std::unique_ptr<FourByteStruct> fbs;
  std::unique_ptr<TwoByteStruct> tbs;
  fbs = std::make_unique<FourByteStruct>(0xa4a3a2a1);
  parent_vector.push_back(std::move(fbs));
  fbs = std::make_unique<FourByteStruct>(0xb4b3b2b2);
  parent_vector.push_back(std::move(fbs));
  tbs = std::make_unique<TwoByteStruct>(0xc2c3);
  parent_vector.push_back(std::move(tbs));
  fbs = std::make_unique<FourByteStruct>(0xd4d3d2d4);
  parent_vector.push_back(std::move(fbs));

  std::vector<std::unique_ptr<UnusedParentStruct>> vector_copy;
  for (auto& s : parent_vector) {
    if (s->struct_type_ == StructType::TWO_BYTE) {
      vector_copy.push_back(std::make_unique<TwoByteStruct>(*(TwoByteStruct*)s.get()));
    }
    if (s->struct_type_ == StructType::FOUR_BYTE) {
      vector_copy.push_back(std::make_unique<FourByteStruct>(*(FourByteStruct*)s.get()));
    }
  }

  auto packet = OneGenericStructArrayBuilder::Create(std::move(parent_vector));
  ASSERT_EQ(one_struct_array.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_struct_array.size(), packet_bytes->size());
  for (size_t i = 0; i < one_struct_array.size(); i++) {
    ASSERT_EQ(one_struct_array[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneGenericStructArrayView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto an_array = view.GetAnArray();
  ASSERT_EQ(vector_copy.size(), an_array.size());
  for (size_t i = 0; i < vector_copy.size(); i++) {
    ASSERT_NE(nullptr, an_array[i]);
    ASSERT_EQ(vector_copy[i]->struct_type_, an_array[i]->struct_type_);
    if (vector_copy[i]->struct_type_ == StructType::FOUR_BYTE) {
      ASSERT_EQ(FourByteStruct::Specialize(vector_copy[i].get())->four_bytes_,
                FourByteStruct::Specialize(an_array[i].get())->four_bytes_);
    } else {
      ASSERT_EQ(TwoByteStruct::Specialize(vector_copy[i].get())->two_bytes_,
                TwoByteStruct::Specialize(an_array[i].get())->two_bytes_);
    }
  }
}

TEST(GeneratedPacketTest, testOneGenericStructFourArray) {
  std::array<std::unique_ptr<UnusedParentStruct>, 4> parent_vector;
  std::unique_ptr<FourByteStruct> fbs;
  std::unique_ptr<TwoByteStruct> tbs;
  fbs = std::make_unique<FourByteStruct>(0xa4a3a2a1);
  parent_vector[0] = std::move(fbs);
  fbs = std::make_unique<FourByteStruct>(0xb4b3b2b2);
  parent_vector[1] = std::move(fbs);
  tbs = std::make_unique<TwoByteStruct>(0xc2c3);
  parent_vector[2] = std::move(tbs);
  fbs = std::make_unique<FourByteStruct>(0xd4d3d2d4);
  parent_vector[3] = std::move(fbs);

  std::array<std::unique_ptr<UnusedParentStruct>, 4> vector_copy;
  size_t index = 0;
  for (auto& s : parent_vector) {
    if (s->struct_type_ == StructType::TWO_BYTE) {
      vector_copy[index] = std::make_unique<TwoByteStruct>(*(TwoByteStruct*)s.get());
    }
    if (s->struct_type_ == StructType::FOUR_BYTE) {
      vector_copy[index] = std::make_unique<FourByteStruct>(*(FourByteStruct*)s.get());
    }
    index++;
  }

  auto packet = OneGenericStructFourArrayBuilder::Create(std::move(parent_vector));
  ASSERT_EQ(one_struct_array.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_struct_array.size(), packet_bytes->size());
  for (size_t i = 0; i < one_struct_array.size(); i++) {
    ASSERT_EQ(one_struct_array[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneGenericStructFourArrayView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto an_array = view.GetAnArray();
  ASSERT_EQ(vector_copy.size(), an_array.size());
  for (size_t i = 0; i < vector_copy.size(); i++) {
    ASSERT_NE(nullptr, an_array[i]);
    ASSERT_EQ(vector_copy[i]->struct_type_, an_array[i]->struct_type_);
    if (vector_copy[i]->struct_type_ == StructType::FOUR_BYTE) {
      ASSERT_EQ(FourByteStruct::Specialize(vector_copy[i].get())->four_bytes_,
                FourByteStruct::Specialize(an_array[i].get())->four_bytes_);
    } else {
      ASSERT_EQ(TwoByteStruct::Specialize(vector_copy[i].get())->two_bytes_,
                TwoByteStruct::Specialize(an_array[i].get())->two_bytes_);
    }
  }
}

vector<uint8_t> one_struct_array_after_fixed{
        0x01, 0x02,              // two_bytes = 0x0201
        0x04,                    // struct_type_ = FourByte
        0xa1, 0xa2, 0xa3, 0xa4,  // four_bytes_
        0x04,                    // struct_type_ = FourByte
        0xb2, 0xb2, 0xb3, 0xb4,  // four_bytes_
        0x02,                    // struct_type_ = TwoByte
        0xc3, 0xc2,              // two_bytes_
        0x04,                    // struct_type_ = TwoByte
        0xd4, 0xd2, 0xd3, 0xd4,  // four_bytes_
};

DEFINE_AND_INSTANTIATE_OneGenericStructArrayAfterFixedReflectionTest(one_struct_array_after_fixed);

vector<uint8_t> one_length_type_value_struct{
        // _size_(value):16 type value
        0x04, 0x00, 0x01, 'o', 'n', 'e',            // ONE
        0x04, 0x00, 0x02, 't', 'w', 'o',            // TWO
        0x06, 0x00, 0x03, 't', 'h', 'r', 'e', 'e',  // THREE
};

DEFINE_AND_INSTANTIATE_OneLengthTypeValueStructReflectionTest(one_length_type_value_struct);

TEST(GeneratedPacketTest, testOneLengthTypeValueStruct) {
  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
          std::make_shared<std::vector<uint8_t>>(one_length_type_value_struct);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneLengthTypeValueStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  auto one = view.GetOneArray();
  size_t entry_id = 0;
  for (const auto& entry : one) {
    switch (entry_id++) {
      case 0:
        ASSERT_EQ(entry.type_, DataType::ONE);
        ASSERT_EQ(entry.value_, std::vector<uint8_t>({'o', 'n', 'e'}));
        break;
      case 1:
        ASSERT_EQ(entry.type_, DataType::TWO);
        ASSERT_EQ(entry.value_, std::vector<uint8_t>({'t', 'w', 'o'}));
        break;
      case 2:
        ASSERT_EQ(entry.type_, DataType::THREE);
        ASSERT_EQ(entry.value_, std::vector<uint8_t>({'t', 'h', 'r', 'e', 'e'}));
        break;
      default:
        ASSERT_EQ(entry.type_, DataType::UNUSED);
    }
  }
}

vector<uint8_t> one_length_type_value_struct_padded_10{
        0x20,                                                        // _size_(payload),
        0x14,                                                        // valid bytes
        0x04, 0x00, 0x01, 'o',  'n',  'e',                           // ONE
        0x04, 0x00, 0x02, 't',  'w',  'o',                           // TWO
        0x06, 0x00, 0x03, 't',  'h',  'r',  'e',  'e',               // THREE
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // padding to 30
        0x20,                                                        // after padding
};

vector<uint8_t> one_length_type_value_struct_padded_18{
        0x20,                                                        // _size_(payload),
        0x0C,                                                        // valid bytes
        0x04, 0x00, 0x01, 'o',  'n',  'e',                           // ONE
        0x04, 0x00, 0x02, 't',  'w',  'o',                           // TWO
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              // padding to 20
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // padding to 30
        0x20,                                                        // after padding
};

// TODO: Revisit LTV parsing.  Right now, the padding bytes are parsed
// DEFINE_AND_INSTANTIATE_OneLengthTypeValueStructPaddedReflectionTest(one_length_type_value_struct_padded_10,
//                                                                     one_length_type_value_struct_padded_18);

TEST(GeneratedPacketTest, testOneLengthTypeValueStructPaddedGeneration) {
  std::vector<LengthTypeValueStruct> ltv_vector;
  LengthTypeValueStruct ltv;
  ltv.type_ = DataType::ONE;
  ltv.value_ = {
          'o',
          'n',
          'e',
  };
  ltv_vector.push_back(ltv);
  ltv.type_ = DataType::TWO;
  ltv.value_ = {
          't',
          'w',
          'o',
  };
  ltv_vector.push_back(ltv);
  uint8_t after_padding = 0x20;

  auto packet = OneLengthTypeValueStructPaddedBuilder::Create(12, ltv_vector, after_padding);
  ASSERT_EQ(one_length_type_value_struct_padded_18.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_length_type_value_struct_padded_18.size(), packet_bytes->size());
  for (size_t i = 0; i < one_length_type_value_struct_padded_18.size(); i++) {
    ASSERT_EQ(one_length_type_value_struct_padded_18[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view =
          OneLengthTypeValueStructPaddedView::Create(SizedParentView::Create(packet_bytes_view));
  ASSERT_TRUE(view.IsValid());
  auto an_array = view.GetOneArray();
  // TODO: Revisit LTV parsing.  Right now, the padding bytes are parsed
  ASSERT_LE(ltv_vector.size(), an_array.size());
  for (size_t i = 0; i < ltv_vector.size(); i++) {
    ASSERT_EQ(ltv_vector[i].type_, an_array[i].type_);
    ASSERT_EQ(ltv_vector[i].value_, an_array[i].value_);
  }
  ASSERT_EQ(after_padding, view.GetAfterPadding());
}

vector<uint8_t> byte_sized{
        0x11,                                            // 1
        0x21, 0x22,                                      // 2
        0x31, 0x32, 0x33,                                // 3
        0x41, 0x42, 0x43, 0x44,                          // 4
        0x51, 0x52, 0x53, 0x54, 0x55,                    // 5
        0x61, 0x62, 0x63, 0x64, 0x65, 0x66,              // 6
        0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,        // 7
        0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,  // 8
};

TEST(GeneratedPacketTest, testByteSizedFields) {
  uint64_t array[9]{
          0xbadbadbad,
          0x11,                // 1
          0x2221,              // 2
          0x333231,            // 3
          0x44434241,          // 4
          0x5554535251,        // 5
          0x666564636261,      // 6
          0x77767574737271,    // 7
          0x8887868584838281,  // 8
  };
  auto packet = ByteSizedFieldsBuilder::Create(array[1], array[2], array[3], array[4], array[5],
                                               array[6], array[7], array[8]);
  ASSERT_EQ(byte_sized.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(byte_sized.size(), packet_bytes->size());
  for (size_t i = 0; i < byte_sized.size(); i++) {
    ASSERT_EQ(byte_sized[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = ByteSizedFieldsView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());
  ASSERT_EQ(array[1], view.GetOne());
  ASSERT_EQ(array[2], view.GetTwo());
  ASSERT_EQ(array[3], view.GetThree());
  ASSERT_EQ(array[4], view.GetFour());
  ASSERT_EQ(array[5], view.GetFive());
  ASSERT_EQ(array[6], view.GetSix());
  ASSERT_EQ(array[7], view.GetSeven());
  ASSERT_EQ(array[8], view.GetEight());
}

DEFINE_AND_INSTANTIATE_ByteSizedFieldsReflectionTest(byte_sized);

TEST(GeneratedPacketTest, testOneGenericStructArrayNoZeroEmpty) {
  auto too_few_bytes = std::make_shared<std::vector<uint8_t>>(0);
  auto view = OneGenericStructArrayNoZeroView::Create(PacketView<kLittleEndian>(too_few_bytes));
  for (size_t i = 0; i < 10; i++) {
    if (view.IsValid()) {
      view.GetAnArray().size();
    }
    too_few_bytes->push_back(0);
    view = OneGenericStructArrayNoZeroView::Create(PacketView<kLittleEndian>(too_few_bytes));
  }

  std::vector<uint8_t> a_two_byte_struct = {
          static_cast<uint8_t>(StructTypeNoZero::TWO_BYTE),
          0x01,
          0x02,
  };
  too_few_bytes = std::make_shared<std::vector<uint8_t>>(a_two_byte_struct);
  view = OneGenericStructArrayNoZeroView::Create(PacketView<kLittleEndian>(too_few_bytes));
  ASSERT_TRUE(view.IsValid());
  ASSERT_EQ(1ul, view.GetAnArray().size());
}

TEST(GeneratedPacketTest, testToStringOutput) {
  std::vector<TwoRelatedNumbersBe> count_array;
  for (uint8_t i = 1; i < 5; i++) {
    TwoRelatedNumbersBe trn;
    trn.id_ = i;
    trn.count_ = 0x0102 * i;
    count_array.push_back(trn);
  }

  auto packet = ArrayOfStructBeBuilder::Create(count_array);

  ASSERT_EQ(array_of_struct_be.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(array_of_struct_be.size(), packet_bytes->size());
  for (size_t i = 0; i < array_of_struct_be.size(); i++) {
    ASSERT_EQ(array_of_struct_be[i], packet_bytes->at(i));
  }

  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = ArrayOfStructBeView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());

  ASSERT_EQ(
          "ArrayOfStructBe { array_count = 0x4, array = VECTOR[TwoRelatedNumbersBe { id = 0x1, "
          "count = 0x102 }, "
          "TwoRelatedNumbersBe { id = 0x2, count = 0x204 }, TwoRelatedNumbersBe { id = 0x3, count "
          "= 0x306 }, "
          "TwoRelatedNumbersBe { id = 0x4, count = 0x408 }] }",
          view.ToString());
}

TEST(GeneratedPacketTest, testEnumText) {
  EXPECT_EQ("FIVE(0x5)", FourBitsText(FourBits::FIVE));
  EXPECT_EQ("THREE(0x3)", TwoBitsText(TwoBits::THREE));
  EXPECT_EQ("FOUR_BYTE(0x04)", StructTypeText(StructType::FOUR_BYTE));
  EXPECT_EQ("TWO_THREE(0x0302)", ForArraysText(ForArrays::TWO_THREE));
}

TEST(GeneratedPacketTest, testToStringOneFixedTypesStruct) {
  StructWithFixedTypes swf;
  swf.four_bits_ = FourBits::FIVE;
  swf.id_ = 0x0d;
  swf.array_ = {{0x01, 0x02, 0x03}};
  swf.six_bytes_ = SixBytes{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};

  auto packet = OneFixedTypesStructBuilder::Create(swf);
  ASSERT_EQ(one_fixed_types_struct.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  ASSERT_EQ(one_fixed_types_struct.size(), packet_bytes->size());
  for (size_t i = 0; i < one_fixed_types_struct.size(); i++) {
    ASSERT_EQ(one_fixed_types_struct[i], packet_bytes->at(i));
  }

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = OneFixedTypesStructView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());

  ASSERT_EQ(
          "OneFixedTypesStruct { one = StructWithFixedTypes { four_bits = FIVE(0x5), id = 0xd, "
          "array = ARRAY[0x1, 0x2, 0x3], example_checksum = CHECKSUM, six_bytes = SixBytes } }",
          view.ToString());
}

TEST(GeneratedPacketTest, testCreateOptional) {
  auto packet = ChildTwoTwoThreeBuilder::Create();

  ASSERT_EQ(child_two_two_three.size(), packet->size());

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto wrong_view = ParentView::CreateOptional(packet_bytes_view);
  ASSERT_FALSE(wrong_view.has_value());

  auto parent_view = ParentTwoView::CreateOptional(packet_bytes_view);
  ASSERT_EQ(FourBits::TWO, parent_view->GetFourBits());

  auto child_view = ChildTwoTwoView::CreateOptional(*parent_view);
  ASSERT_EQ(FourBits::THREE, child_view->GetMoreBits());

  auto grandchild_view = ChildTwoTwoThreeView::CreateOptional(*child_view);
  ASSERT_TRUE(grandchild_view.has_value());
}

TEST(GeneratedPacketTest, testStructWithShadowedNames) {
  uint32_t four_bytes = 0x01020304;
  StructType struct_type = StructType::TWO_BYTE;
  auto ebs = AtLeastFourByteStruct(four_bytes, struct_type);

  ASSERT_EQ(ebs.four_bytes_, four_bytes);
  ASSERT_EQ(ebs.struct_type_, struct_type);
}

TEST(GeneratedPacketTest, testArrays) {
  std::vector<uint8_t> bytes({1, 2, 3});
  std::vector<uint16_t> sixteens({0x0111, 0x0212, 0x0313});
  std::vector<uint32_t> thirtytwos({0x01112131, 0x02122232, 0x03132333});
  auto packet = ArraysBuilder::Create(bytes, sixteens, thirtytwos);

  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter it(*packet_bytes);
  packet->Serialize(it);

  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
  auto view = ArraysView::Create(packet_bytes_view);
  ASSERT_TRUE(view.IsValid());

  auto bytes_from_packet = view.GetBytes();
  ASSERT_EQ(bytes.size(), bytes_from_packet.size());
  for (size_t i = 0; i < bytes.size(); i++) {
    ASSERT_EQ(bytes[i], bytes_from_packet[i]);
  }

  auto sixteens_from_packet = view.GetSixteens();
  ASSERT_EQ(sixteens.size(), sixteens_from_packet.size());
  for (size_t i = 0; i < sixteens.size(); i++) {
    ASSERT_EQ(sixteens[i], sixteens_from_packet[i]);
  }

  auto thirtytwos_from_packet = view.GetThirtytwos();
  ASSERT_EQ(thirtytwos.size(), thirtytwos_from_packet.size());
  for (size_t i = 0; i < thirtytwos.size(); i++) {
    ASSERT_EQ(thirtytwos[i], thirtytwos_from_packet[i]);
  }
}
}  // namespace parser
}  // namespace packet
}  // namespace bluetooth
