// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <capnp/test-import.capnp.h>
#include "message.h"
#include <kj/debug.h>
#include <gtest/gtest.h>
#include "test-util.h"

namespace capnp {
namespace _ {  // private
namespace {

template <typename T, typename U>
void checkList(T reader, std::initializer_list<U> expected) {
  ASSERT_EQ(expected.size(), reader.size());
  for (uint i = 0; i < expected.size(); i++) {
    EXPECT_EQ(expected.begin()[i], reader[i]);
  }
}

template <typename T>
void checkList(T reader, std::initializer_list<float> expected) {
  ASSERT_EQ(expected.size(), reader.size());
  for (uint i = 0; i < expected.size(); i++) {
    EXPECT_FLOAT_EQ(expected.begin()[i], reader[i]);
  }
}

template <typename T>
void checkList(T reader, std::initializer_list<double> expected) {
  ASSERT_EQ(expected.size(), reader.size());
  for (uint i = 0; i < expected.size(); i++) {
    EXPECT_DOUBLE_EQ(expected.begin()[i], reader[i]);
  }
}

TEST(Encoding, AllTypes) {
  MallocMessageBuilder builder;

  initTestMessage(builder.initRoot<TestAllTypes>());
  checkTestMessage(builder.getRoot<TestAllTypes>());
  checkTestMessage(builder.getRoot<TestAllTypes>().asReader());

  SegmentArrayMessageReader reader(builder.getSegmentsForOutput());

  checkTestMessage(reader.getRoot<TestAllTypes>());

  ASSERT_EQ(1u, builder.getSegmentsForOutput().size());

  checkTestMessage(readMessageUnchecked<TestAllTypes>(builder.getSegmentsForOutput()[0].begin()));

  EXPECT_EQ(builder.getSegmentsForOutput()[0].size() - 1,  // -1 for root pointer
            reader.getRoot<TestAllTypes>().totalSizeInWords());
}

TEST(Encoding, AllTypesMultiSegment) {
  MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE);

  initTestMessage(builder.initRoot<TestAllTypes>());
  checkTestMessage(builder.getRoot<TestAllTypes>());
  checkTestMessage(builder.getRoot<TestAllTypes>().asReader());

  SegmentArrayMessageReader reader(builder.getSegmentsForOutput());

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Encoding, Defaults) {
  AlignedData<1> nullRoot = {{0, 0, 0, 0, 0, 0, 0, 0}};
  kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(nullRoot.words, 1)};
  SegmentArrayMessageReader reader(kj::arrayPtr(segments, 1));

  checkTestMessage(reader.getRoot<TestDefaults>());
  checkTestMessage(readMessageUnchecked<TestDefaults>(nullRoot.words));
}

TEST(Encoding, DefaultInitialization) {
  MallocMessageBuilder builder;

  checkTestMessage(builder.getRoot<TestDefaults>());  // first pass initializes to defaults
  checkTestMessage(builder.getRoot<TestDefaults>().asReader());

  checkTestMessage(builder.getRoot<TestDefaults>());  // second pass just reads the initialized structure
  checkTestMessage(builder.getRoot<TestDefaults>().asReader());

  SegmentArrayMessageReader reader(builder.getSegmentsForOutput());

  checkTestMessage(reader.getRoot<TestDefaults>());
}

TEST(Encoding, DefaultInitializationMultiSegment) {
  MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE);

  // first pass initializes to defaults
  checkTestMessage(builder.getRoot<TestDefaults>());
  checkTestMessage(builder.getRoot<TestDefaults>().asReader());

  // second pass just reads the initialized structure
  checkTestMessage(builder.getRoot<TestDefaults>());
  checkTestMessage(builder.getRoot<TestDefaults>().asReader());

  SegmentArrayMessageReader reader(builder.getSegmentsForOutput());

  checkTestMessage(reader.getRoot<TestDefaults>());
}

TEST(Encoding, DefaultsFromEmptyMessage) {
  AlignedData<1> emptyMessage = {{0, 0, 0, 0, 0, 0, 0, 0}};

  kj::ArrayPtr<const word> segments[1] = {kj::arrayPtr(emptyMessage.words, 1)};
  SegmentArrayMessageReader reader(kj::arrayPtr(segments, 1));

  checkTestMessage(reader.getRoot<TestDefaults>());
  checkTestMessage(readMessageUnchecked<TestDefaults>(emptyMessage.words));
}

TEST(Encoding, GenericObjects) {
  MallocMessageBuilder builder;
  auto root = builder.getRoot<test::TestObject>();

  initTestMessage(root.initObjectField<TestAllTypes>());
  checkTestMessage(root.getObjectField<TestAllTypes>());
  checkTestMessage(root.asReader().getObjectField<TestAllTypes>());

  root.setObjectField<Text>("foo");
  EXPECT_EQ("foo", root.getObjectField<Text>());
  EXPECT_EQ("foo", root.asReader().getObjectField<Text>());

  root.setObjectField<Data>(data("foo"));
  EXPECT_EQ(data("foo"), root.getObjectField<Data>());
  EXPECT_EQ(data("foo"), root.asReader().getObjectField<Data>());

  {
    root.setObjectField<List<uint32_t>>({123, 456, 789});

    {
      List<uint32_t>::Builder list = root.getObjectField<List<uint32_t>>();
      ASSERT_EQ(3u, list.size());
      EXPECT_EQ(123u, list[0]);
      EXPECT_EQ(456u, list[1]);
      EXPECT_EQ(789u, list[2]);
    }

    {
      List<uint32_t>::Reader list = root.asReader().getObjectField<List<uint32_t>>();
      ASSERT_EQ(3u, list.size());
      EXPECT_EQ(123u, list[0]);
      EXPECT_EQ(456u, list[1]);
      EXPECT_EQ(789u, list[2]);
    }
  }

  {
    root.setObjectField<List<Text>>({"foo", "bar"});

    {
      List<Text>::Builder list = root.getObjectField<List<Text>>();
      ASSERT_EQ(2u, list.size());
      EXPECT_EQ("foo", list[0]);
      EXPECT_EQ("bar", list[1]);
    }

    {
      List<Text>::Reader list = root.asReader().getObjectField<List<Text>>();
      ASSERT_EQ(2u, list.size());
      EXPECT_EQ("foo", list[0]);
      EXPECT_EQ("bar", list[1]);
    }
  }

  {
    {
      List<TestAllTypes>::Builder list = root.initObjectField<List<TestAllTypes>>(2);
      ASSERT_EQ(2u, list.size());
      initTestMessage(list[0]);
    }

    {
      List<TestAllTypes>::Builder list = root.getObjectField<List<TestAllTypes>>();
      ASSERT_EQ(2u, list.size());
      checkTestMessage(list[0]);
      checkTestMessageAllZero(list[1]);
    }

    {
      List<TestAllTypes>::Reader list = root.asReader().getObjectField<List<TestAllTypes>>();
      ASSERT_EQ(2u, list.size());
      checkTestMessage(list[0]);
      checkTestMessageAllZero(list[1]);
    }
  }
}

#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#define EXPECT_NONFATAL_FAILURE(code) code
#else
#define EXPECT_NONFATAL_FAILURE EXPECT_ANY_THROW
#endif

#ifdef NDEBUG
#define EXPECT_DEBUG_ANY_THROW(EXP)
#else
#define EXPECT_DEBUG_ANY_THROW EXPECT_ANY_THROW
#endif

TEST(Encoding, Unions) {
  MallocMessageBuilder builder;
  TestUnion::Builder root = builder.getRoot<TestUnion>();

  EXPECT_EQ(TestUnion::Union0::U0F0S0, root.getUnion0().which());
  EXPECT_EQ(Void::VOID, root.getUnion0().getU0f0s0());
  EXPECT_DEBUG_ANY_THROW(root.getUnion0().getU0f0s1());

  root.getUnion0().setU0f0s1(true);
  EXPECT_EQ(TestUnion::Union0::U0F0S1, root.getUnion0().which());
  EXPECT_TRUE(root.getUnion0().getU0f0s1());
  EXPECT_DEBUG_ANY_THROW(root.getUnion0().getU0f0s0());

  root.getUnion0().setU0f0s8(123);
  EXPECT_EQ(TestUnion::Union0::U0F0S8, root.getUnion0().which());
  EXPECT_EQ(123, root.getUnion0().getU0f0s8());
  EXPECT_DEBUG_ANY_THROW(root.getUnion0().getU0f0s1());
}

struct UnionState {
  uint discriminants[4];
  int dataOffset;

  UnionState(std::initializer_list<uint> discriminants, int dataOffset)
      : dataOffset(dataOffset) {
    memcpy(this->discriminants, discriminants.begin(), sizeof(this->discriminants));
  }

  bool operator==(const UnionState& other) const {
    for (uint i = 0; i < 4; i++) {
      if (discriminants[i] != other.discriminants[i]) {
        return false;
      }
    }

    return dataOffset == other.dataOffset;
  }
};

std::ostream& operator<<(std::ostream& os, const UnionState& us) {
  os << "UnionState({";

  for (uint i = 0; i < 4; i++) {
    if (i > 0) os << ", ";
    os << us.discriminants[i];
  }

  return os << "}, " << us.dataOffset << ")";
}

template <typename StructType, typename Func>
UnionState initUnion(Func&& initializer) {
  // Use the given setter to initialize the given union field and then return a struct indicating
  // the location of the data that was written as well as the values of the four union
  // discriminants.

  MallocMessageBuilder builder;
  initializer(builder.getRoot<StructType>());
  kj::ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0];

  KJ_ASSERT(segment.size() > 2, segment.size());

  // Find the offset of the first set bit after the union discriminants.
  int offset = 0;
  for (const uint8_t* p = reinterpret_cast<const uint8_t*>(segment.begin() + 2);
       p < reinterpret_cast<const uint8_t*>(segment.end()); p++) {
    if (*p != 0) {
      uint8_t bits = *p;
      while ((bits & 1) == 0) {
        ++offset;
        bits >>= 1;
      }
      goto found;
    }
    offset += 8;
  }
  offset = -1;

found:
  const uint8_t* discriminants = reinterpret_cast<const uint8_t*>(segment.begin() + 1);
  return UnionState({discriminants[0], discriminants[2], discriminants[4], discriminants[6]},
                    offset);
}

TEST(Encoding, UnionLayout) {
#define INIT_UNION(setter) \
  initUnion<TestUnion>([](TestUnion::Builder b) {b.setter;})

  EXPECT_EQ(UnionState({ 0,0,0,0},  -1), INIT_UNION(getUnion0().setU0f0s0(Void::VOID)));
  EXPECT_EQ(UnionState({ 1,0,0,0},   0), INIT_UNION(getUnion0().setU0f0s1(1)));
  EXPECT_EQ(UnionState({ 2,0,0,0},   0), INIT_UNION(getUnion0().setU0f0s8(1)));
  EXPECT_EQ(UnionState({ 3,0,0,0},   0), INIT_UNION(getUnion0().setU0f0s16(1)));
  EXPECT_EQ(UnionState({ 4,0,0,0},   0), INIT_UNION(getUnion0().setU0f0s32(1)));
  EXPECT_EQ(UnionState({ 5,0,0,0},   0), INIT_UNION(getUnion0().setU0f0s64(1)));
  EXPECT_EQ(UnionState({ 6,0,0,0}, 448), INIT_UNION(getUnion0().setU0f0sp("1")));

  EXPECT_EQ(UnionState({ 7,0,0,0},  -1), INIT_UNION(getUnion0().setU0f1s0(Void::VOID)));
  EXPECT_EQ(UnionState({ 8,0,0,0},   0), INIT_UNION(getUnion0().setU0f1s1(1)));
  EXPECT_EQ(UnionState({ 9,0,0,0},   0), INIT_UNION(getUnion0().setU0f1s8(1)));
  EXPECT_EQ(UnionState({10,0,0,0},   0), INIT_UNION(getUnion0().setU0f1s16(1)));
  EXPECT_EQ(UnionState({11,0,0,0},   0), INIT_UNION(getUnion0().setU0f1s32(1)));
  EXPECT_EQ(UnionState({12,0,0,0},   0), INIT_UNION(getUnion0().setU0f1s64(1)));
  EXPECT_EQ(UnionState({13,0,0,0}, 448), INIT_UNION(getUnion0().setU0f1sp("1")));

  EXPECT_EQ(UnionState({0, 0,0,0},  -1), INIT_UNION(getUnion1().setU1f0s0(Void::VOID)));
  EXPECT_EQ(UnionState({0, 1,0,0},  65), INIT_UNION(getUnion1().setU1f0s1(1)));
  EXPECT_EQ(UnionState({0, 2,0,0},  65), INIT_UNION(getUnion1().setU1f1s1(1)));
  EXPECT_EQ(UnionState({0, 3,0,0},  72), INIT_UNION(getUnion1().setU1f0s8(1)));
  EXPECT_EQ(UnionState({0, 4,0,0},  72), INIT_UNION(getUnion1().setU1f1s8(1)));
  EXPECT_EQ(UnionState({0, 5,0,0},  80), INIT_UNION(getUnion1().setU1f0s16(1)));
  EXPECT_EQ(UnionState({0, 6,0,0},  80), INIT_UNION(getUnion1().setU1f1s16(1)));
  EXPECT_EQ(UnionState({0, 7,0,0},  96), INIT_UNION(getUnion1().setU1f0s32(1)));
  EXPECT_EQ(UnionState({0, 8,0,0},  96), INIT_UNION(getUnion1().setU1f1s32(1)));
  EXPECT_EQ(UnionState({0, 9,0,0}, 128), INIT_UNION(getUnion1().setU1f0s64(1)));
  EXPECT_EQ(UnionState({0,10,0,0}, 128), INIT_UNION(getUnion1().setU1f1s64(1)));
  EXPECT_EQ(UnionState({0,11,0,0}, 512), INIT_UNION(getUnion1().setU1f0sp("1")));
  EXPECT_EQ(UnionState({0,12,0,0}, 512), INIT_UNION(getUnion1().setU1f1sp("1")));

  EXPECT_EQ(UnionState({0,13,0,0},  -1), INIT_UNION(getUnion1().setU1f2s0(Void::VOID)));
  EXPECT_EQ(UnionState({0,14,0,0}, 128), INIT_UNION(getUnion1().setU1f2s1(1)));
  EXPECT_EQ(UnionState({0,15,0,0}, 128), INIT_UNION(getUnion1().setU1f2s8(1)));
  EXPECT_EQ(UnionState({0,16,0,0}, 128), INIT_UNION(getUnion1().setU1f2s16(1)));
  EXPECT_EQ(UnionState({0,17,0,0}, 128), INIT_UNION(getUnion1().setU1f2s32(1)));
  EXPECT_EQ(UnionState({0,18,0,0}, 128), INIT_UNION(getUnion1().setU1f2s64(1)));
  EXPECT_EQ(UnionState({0,19,0,0}, 512), INIT_UNION(getUnion1().setU1f2sp("1")));

  EXPECT_EQ(UnionState({0,0,0,0}, 192), INIT_UNION(getUnion2().setU2f0s1(1)));
  EXPECT_EQ(UnionState({0,0,0,0}, 193), INIT_UNION(getUnion3().setU3f0s1(1)));
  EXPECT_EQ(UnionState({0,0,1,0}, 200), INIT_UNION(getUnion2().setU2f0s8(1)));
  EXPECT_EQ(UnionState({0,0,0,1}, 208), INIT_UNION(getUnion3().setU3f0s8(1)));
  EXPECT_EQ(UnionState({0,0,2,0}, 224), INIT_UNION(getUnion2().setU2f0s16(1)));
  EXPECT_EQ(UnionState({0,0,0,2}, 240), INIT_UNION(getUnion3().setU3f0s16(1)));
  EXPECT_EQ(UnionState({0,0,3,0}, 256), INIT_UNION(getUnion2().setU2f0s32(1)));
  EXPECT_EQ(UnionState({0,0,0,3}, 288), INIT_UNION(getUnion3().setU3f0s32(1)));
  EXPECT_EQ(UnionState({0,0,4,0}, 320), INIT_UNION(getUnion2().setU2f0s64(1)));
  EXPECT_EQ(UnionState({0,0,0,4}, 384), INIT_UNION(getUnion3().setU3f0s64(1)));

#undef INIT_UNION
}

TEST(Encoding, UnionDefault) {
  MallocMessageBuilder builder;
  TestUnionDefaults::Reader reader = builder.getRoot<TestUnionDefaults>().asReader();

  {
    auto field = reader.getS16s8s64s8Set();
    EXPECT_EQ(TestUnion::Union0::U0F0S16, field.getUnion0().which());
    EXPECT_EQ(TestUnion::Union1::U1F0S8 , field.getUnion1().which());
    EXPECT_EQ(TestUnion::Union2::U2F0S64, field.getUnion2().which());
    EXPECT_EQ(TestUnion::Union3::U3F0S8 , field.getUnion3().which());
    EXPECT_EQ(321, field.getUnion0().getU0f0s16());
    EXPECT_EQ(123, field.getUnion1().getU1f0s8());
    EXPECT_EQ(12345678901234567ll, field.getUnion2().getU2f0s64());
    EXPECT_EQ(55, field.getUnion3().getU3f0s8());
  }

  {
    auto field = reader.getS0sps1s32Set();
    EXPECT_EQ(TestUnion::Union0::U0F1S0 , field.getUnion0().which());
    EXPECT_EQ(TestUnion::Union1::U1F0SP , field.getUnion1().which());
    EXPECT_EQ(TestUnion::Union2::U2F0S1 , field.getUnion2().which());
    EXPECT_EQ(TestUnion::Union3::U3F0S32, field.getUnion3().which());
    EXPECT_EQ(Void::VOID, field.getUnion0().getU0f1s0());
    EXPECT_EQ("foo", field.getUnion1().getU1f0sp());
    EXPECT_EQ(true, field.getUnion2().getU2f0s1());
    EXPECT_EQ(12345678, field.getUnion3().getU3f0s32());
  }
}

// =======================================================================================

TEST(Encoding, ListDefaults) {
  MallocMessageBuilder builder;
  TestListDefaults::Builder root = builder.getRoot<TestListDefaults>();

  checkTestMessage(root.asReader());
  checkTestMessage(root);
  checkTestMessage(root.asReader());
}

TEST(Encoding, BuildListDefaults) {
  MallocMessageBuilder builder;
  TestListDefaults::Builder root = builder.getRoot<TestListDefaults>();

  initTestMessage(root);
  checkTestMessage(root.asReader());
  checkTestMessage(root);
  checkTestMessage(root.asReader());
}

TEST(Encoding, SmallStructLists) {
  // In this test, we will manually initialize TestListDefaults.lists to match the default
  // value and verify that we end up with the same encoding that the compiler produces.

  MallocMessageBuilder builder;
  auto root = builder.getRoot<TestListDefaults>();
  auto sl = root.initLists();

  // Verify that all the lists are actually empty.
  EXPECT_EQ(0u, sl.getList0 ().size());
  EXPECT_EQ(0u, sl.getList1 ().size());
  EXPECT_EQ(0u, sl.getList8 ().size());
  EXPECT_EQ(0u, sl.getList16().size());
  EXPECT_EQ(0u, sl.getList32().size());
  EXPECT_EQ(0u, sl.getList64().size());
  EXPECT_EQ(0u, sl.getListP ().size());
  EXPECT_EQ(0u, sl.getInt32ListList().size());
  EXPECT_EQ(0u, sl.getTextListList().size());
  EXPECT_EQ(0u, sl.getStructListList().size());

  { auto l = sl.initList0 (2); l[0].setF(Void::VOID);        l[1].setF(Void::VOID); }
  { auto l = sl.initList1 (4); l[0].setF(true);              l[1].setF(false);
                               l[2].setF(true);              l[3].setF(true); }
  { auto l = sl.initList8 (2); l[0].setF(123u);              l[1].setF(45u); }
  { auto l = sl.initList16(2); l[0].setF(12345u);            l[1].setF(6789u); }
  { auto l = sl.initList32(2); l[0].setF(123456789u);        l[1].setF(234567890u); }
  { auto l = sl.initList64(2); l[0].setF(1234567890123456u); l[1].setF(2345678901234567u); }
  { auto l = sl.initListP (2); l[0].setF("foo");             l[1].setF("bar"); }

  {
    auto l = sl.initInt32ListList(3);
    l.set(0, {1, 2, 3});
    l.set(1, {4, 5});
    l.set(2, {12341234});
  }

  {
    auto l = sl.initTextListList(3);
    l.set(0, {"foo", "bar"});
    l.set(1, {"baz"});
    l.set(2, {"qux", "corge"});
  }

  {
    auto l = sl.initStructListList(2);
    l.init(0, 2);
    l.init(1, 1);

    l[0][0].setInt32Field(123);
    l[0][1].setInt32Field(456);
    l[1][0].setInt32Field(789);
  }

  kj::ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0];

  // Initialize another message such that it copies the default value for that field.
  MallocMessageBuilder defaultBuilder;
  defaultBuilder.getRoot<TestListDefaults>().getLists();
  kj::ArrayPtr<const word> defaultSegment = defaultBuilder.getSegmentsForOutput()[0];

  // Should match...
  EXPECT_EQ(defaultSegment.size(), segment.size());

  for (size_t i = 0; i < std::min(segment.size(), defaultSegment.size()); i++) {
    EXPECT_EQ(reinterpret_cast<const uint64_t*>(defaultSegment.begin())[i],
              reinterpret_cast<const uint64_t*>(segment.begin())[i]);
  }
}

// =======================================================================================

TEST(Encoding, ListUpgrade) {
  MallocMessageBuilder builder;
  auto root = builder.initRoot<test::TestObject>();

  root.setObjectField<List<uint16_t>>({12, 34, 56});

  checkList(root.getObjectField<List<uint8_t>>(), {12, 34, 56});

  {
    auto l = root.getObjectField<List<test::TestLists::Struct8>>();
    ASSERT_EQ(3u, l.size());
    EXPECT_EQ(12u, l[0].getF());
    EXPECT_EQ(34u, l[1].getF());
    EXPECT_EQ(56u, l[2].getF());
  }

  checkList(root.getObjectField<List<uint16_t>>(), {12, 34, 56});

  auto reader = root.asReader();

  checkList(reader.getObjectField<List<uint8_t>>(), {12, 34, 56});

  {
    auto l = reader.getObjectField<List<test::TestLists::Struct8>>();
    ASSERT_EQ(3u, l.size());
    EXPECT_EQ(12u, l[0].getF());
    EXPECT_EQ(34u, l[1].getF());
    EXPECT_EQ(56u, l[2].getF());
  }

  {
    kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() {
      reader.getObjectField<List<uint32_t>>();
#if !KJ_NO_EXCEPTIONS
      ADD_FAILURE() << "Should have thrown an exception.";
#endif
    });

    EXPECT_TRUE(e != nullptr) << "Should have thrown an exception.";
  }

  {
    auto l = reader.getObjectField<List<test::TestLists::Struct32>>();
    ASSERT_EQ(3u, l.size());

    // These should return default values because the structs aren't big enough.
    EXPECT_EQ(0u, l[0].getF());
    EXPECT_EQ(0u, l[1].getF());
    EXPECT_EQ(0u, l[2].getF());
  }

  checkList(reader.getObjectField<List<uint16_t>>(), {12, 34, 56});
}

TEST(Encoding, BitListDowngrade) {
  MallocMessageBuilder builder;
  auto root = builder.initRoot<test::TestObject>();

  root.setObjectField<List<uint16_t>>({0x1201u, 0x3400u, 0x5601u, 0x7801u});

  checkList(root.getObjectField<List<bool>>(), {true, false, true, true});

  {
    auto l = root.getObjectField<List<test::TestLists::Struct1>>();
    ASSERT_EQ(4u, l.size());
    EXPECT_TRUE(l[0].getF());
    EXPECT_FALSE(l[1].getF());
    EXPECT_TRUE(l[2].getF());
    EXPECT_TRUE(l[3].getF());
  }

  checkList(root.getObjectField<List<uint16_t>>(), {0x1201u, 0x3400u, 0x5601u, 0x7801u});

  auto reader = root.asReader();

  checkList(reader.getObjectField<List<bool>>(), {true, false, true, true});

  {
    auto l = reader.getObjectField<List<test::TestLists::Struct1>>();
    ASSERT_EQ(4u, l.size());
    EXPECT_TRUE(l[0].getF());
    EXPECT_FALSE(l[1].getF());
    EXPECT_TRUE(l[2].getF());
    EXPECT_TRUE(l[3].getF());
  }

  checkList(reader.getObjectField<List<uint16_t>>(), {0x1201u, 0x3400u, 0x5601u, 0x7801u});
}

TEST(Encoding, BitListUpgrade) {
  MallocMessageBuilder builder;
  auto root = builder.initRoot<test::TestObject>();

  root.setObjectField<List<bool>>({true, false, true, true});

  {
    auto l = root.getObjectField<List<test::TestLists::Struct1>>();
    ASSERT_EQ(4u, l.size());
    EXPECT_TRUE(l[0].getF());
    EXPECT_FALSE(l[1].getF());
    EXPECT_TRUE(l[2].getF());
    EXPECT_TRUE(l[3].getF());
  }

  auto reader = root.asReader();

  {
    kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() {
      reader.getObjectField<List<uint8_t>>();
#if !KJ_NO_EXCEPTIONS
      ADD_FAILURE() << "Should have thrown an exception.";
#endif
    });

    EXPECT_TRUE(e != nullptr) << "Should have thrown an exception.";
  }

  {
    auto l = reader.getObjectField<List<test::TestFieldZeroIsBit>>();
    ASSERT_EQ(4u, l.size());
    EXPECT_TRUE(l[0].getBit());
    EXPECT_FALSE(l[1].getBit());
    EXPECT_TRUE(l[2].getBit());
    EXPECT_TRUE(l[3].getBit());

    // Other fields are defaulted.
    EXPECT_TRUE(l[0].getSecondBit());
    EXPECT_TRUE(l[1].getSecondBit());
    EXPECT_TRUE(l[2].getSecondBit());
    EXPECT_TRUE(l[3].getSecondBit());
    EXPECT_EQ(123u, l[0].getThirdField());
    EXPECT_EQ(123u, l[1].getThirdField());
    EXPECT_EQ(123u, l[2].getThirdField());
    EXPECT_EQ(123u, l[3].getThirdField());
  }

  checkList(reader.getObjectField<List<bool>>(), {true, false, true, true});
}

TEST(Encoding, UpgradeStructInBuilder) {
  MallocMessageBuilder builder;
  auto root = builder.initRoot<test::TestObject>();

  test::TestOldVersion::Reader oldReader;

  {
    auto oldVersion = root.initObjectField<test::TestOldVersion>();
    oldVersion.setOld1(123);
    oldVersion.setOld2("foo");
    auto sub = oldVersion.initOld3();
    sub.setOld1(456);
    sub.setOld2("bar");

    oldReader = oldVersion;
  }

  size_t size = builder.getSegmentsForOutput()[0].size();
  size_t size2;

  {
    auto newVersion = root.getObjectField<test::TestNewVersion>();

    // The old instance should have been zero'd.
    EXPECT_EQ(0, oldReader.getOld1());
    EXPECT_EQ("", oldReader.getOld2());
    EXPECT_EQ(0, oldReader.getOld3().getOld1());
    EXPECT_EQ("", oldReader.getOld3().getOld2());

    // Size should have increased due to re-allocating the struct.
    size_t size1 = builder.getSegmentsForOutput()[0].size();
    EXPECT_GT(size1, size);

    auto sub = newVersion.getOld3();

    // Size should have increased due to re-allocating the sub-struct.
    size2 = builder.getSegmentsForOutput()[0].size();
    EXPECT_GT(size2, size1);

    // Check contents.
    EXPECT_EQ(123, newVersion.getOld1());
    EXPECT_EQ("foo", newVersion.getOld2());
    EXPECT_EQ(987, newVersion.getNew1());
    EXPECT_EQ("baz", newVersion.getNew2());

    EXPECT_EQ(456, sub.getOld1());
    EXPECT_EQ("bar", sub.getOld2());
    EXPECT_EQ(987, sub.getNew1());
    EXPECT_EQ("baz", sub.getNew2());

    newVersion.setOld1(234);
    newVersion.setOld2("qux");
    newVersion.setNew1(321);
    newVersion.setNew2("quux");

    sub.setOld1(567);
    sub.setOld2("corge");
    sub.setNew1(654);
    sub.setNew2("grault");
  }

  // We set four small text fields and implicitly initialized two to defaults, so the size should
  // have raised by six words.
  size_t size3 = builder.getSegmentsForOutput()[0].size();
  EXPECT_EQ(size2 + 6, size3);

  {
    // Go back to old version.  It should have the values set on the new version.
    auto oldVersion = root.getObjectField<test::TestOldVersion>();
    EXPECT_EQ(234, oldVersion.getOld1());
    EXPECT_EQ("qux", oldVersion.getOld2());

    auto sub = oldVersion.getOld3();
    EXPECT_EQ(567, sub.getOld1());
    EXPECT_EQ("corge", sub.getOld2());

    // Overwrite the old fields.  The new fields should remain intact.
    oldVersion.setOld1(345);
    oldVersion.setOld2("garply");
    sub.setOld1(678);
    sub.setOld2("waldo");
  }

  // We set two small text fields, so the size should have raised by two words.
  size_t size4 = builder.getSegmentsForOutput()[0].size();
  EXPECT_EQ(size3 + 2, size4);

  {
    // Back to the new version again.
    auto newVersion = root.getObjectField<test::TestNewVersion>();
    EXPECT_EQ(345, newVersion.getOld1());
    EXPECT_EQ("garply", newVersion.getOld2());
    EXPECT_EQ(321, newVersion.getNew1());
    EXPECT_EQ("quux", newVersion.getNew2());

    auto sub = newVersion.getOld3();
    EXPECT_EQ(678, sub.getOld1());
    EXPECT_EQ("waldo", sub.getOld2());
    EXPECT_EQ(654, sub.getNew1());
    EXPECT_EQ("grault", sub.getNew2());
  }

  // Size should not have changed because we didn't write anything and the structs were already
  // the right size.
  EXPECT_EQ(size4, builder.getSegmentsForOutput()[0].size());
}

TEST(Encoding, UpgradeStructInBuilderMultiSegment) {
  // Exactly like the previous test, except that we force multiple segments.  Since we force a
  // separate segment for every object, every pointer is a far pointer, and far pointers are easily
  // transferred, so this is acutally not such a complicated case.

  MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE);
  auto root = builder.initRoot<test::TestObject>();

  // Start with a 1-word first segment and the root object in the second segment.
  size_t size = builder.getSegmentsForOutput().size();
  EXPECT_EQ(2u, size);

  {
    auto oldVersion = root.initObjectField<test::TestOldVersion>();
    oldVersion.setOld1(123);
    oldVersion.setOld2("foo");
    auto sub = oldVersion.initOld3();
    sub.setOld1(456);
    sub.setOld2("bar");
  }

  // Allocated two structs and two strings.
  size_t size2 = builder.getSegmentsForOutput().size();
  EXPECT_EQ(size + 4, size2);

  size_t size4;

  {
    auto newVersion = root.getObjectField<test::TestNewVersion>();

    // Allocated a new struct.
    size_t size3 = builder.getSegmentsForOutput().size();
    EXPECT_EQ(size2 + 1, size3);

    auto sub = newVersion.getOld3();

    // Allocated another new struct for its string field.
    size4 = builder.getSegmentsForOutput().size();
    EXPECT_EQ(size3 + 1, size4);

    // Check contents.
    EXPECT_EQ(123, newVersion.getOld1());
    EXPECT_EQ("foo", newVersion.getOld2());
    EXPECT_EQ(987, newVersion.getNew1());
    EXPECT_EQ("baz", newVersion.getNew2());

    EXPECT_EQ(456, sub.getOld1());
    EXPECT_EQ("bar", sub.getOld2());
    EXPECT_EQ(987, sub.getNew1());
    EXPECT_EQ("baz", sub.getNew2());

    newVersion.setOld1(234);
    newVersion.setOld2("qux");
    newVersion.setNew1(321);
    newVersion.setNew2("quux");

    sub.setOld1(567);
    sub.setOld2("corge");
    sub.setNew1(654);
    sub.setNew2("grault");
  }

  // Set four strings and implicitly initialized two.
  size_t size5 = builder.getSegmentsForOutput().size();
  EXPECT_EQ(size4 + 6, size5);

  {
    // Go back to old version.  It should have the values set on the new version.
    auto oldVersion = root.getObjectField<test::TestOldVersion>();
    EXPECT_EQ(234, oldVersion.getOld1());
    EXPECT_EQ("qux", oldVersion.getOld2());

    auto sub = oldVersion.getOld3();
    EXPECT_EQ(567, sub.getOld1());
    EXPECT_EQ("corge", sub.getOld2());

    // Overwrite the old fields.  The new fields should remain intact.
    oldVersion.setOld1(345);
    oldVersion.setOld2("garply");
    sub.setOld1(678);
    sub.setOld2("waldo");
  }

  // Set two new strings.
  size_t size6 = builder.getSegmentsForOutput().size();
  EXPECT_EQ(size5 + 2, size6);

  {
    // Back to the new version again.
    auto newVersion = root.getObjectField<test::TestNewVersion>();
    EXPECT_EQ(345, newVersion.getOld1());
    EXPECT_EQ("garply", newVersion.getOld2());
    EXPECT_EQ(321, newVersion.getNew1());
    EXPECT_EQ("quux", newVersion.getNew2());

    auto sub = newVersion.getOld3();
    EXPECT_EQ(678, sub.getOld1());
    EXPECT_EQ("waldo", sub.getOld2());
    EXPECT_EQ(654, sub.getNew1());
    EXPECT_EQ("grault", sub.getNew2());
  }

  // Size should not have changed because we didn't write anything and the structs were already
  // the right size.
  EXPECT_EQ(size6, builder.getSegmentsForOutput().size());
}

TEST(Encoding, UpgradeStructInBuilderFarPointers) {
  // Force allocation of a Far pointer.

  MallocMessageBuilder builder(7, AllocationStrategy::FIXED_SIZE);
  auto root = builder.initRoot<test::TestObject>();

  root.initObjectField<test::TestOldVersion>().setOld2("foo");

  // We should have allocated all but one word of the first segment.
  EXPECT_EQ(1u, builder.getSegmentsForOutput().size());
  EXPECT_EQ(6u, builder.getSegmentsForOutput()[0].size());

  // Now if we upgrade...
  EXPECT_EQ("foo", root.getObjectField<test::TestNewVersion>().getOld2());

  // We should have allocated the new struct in a new segment, but allocated the far pointer
  // landing pad back in the first segment.
  ASSERT_EQ(2u, builder.getSegmentsForOutput().size());
  EXPECT_EQ(7u, builder.getSegmentsForOutput()[0].size());
  EXPECT_EQ(6u, builder.getSegmentsForOutput()[1].size());
}

TEST(Encoding, UpgradeStructInBuilderDoubleFarPointers) {
  // Force allocation of a double-Far pointer.

  MallocMessageBuilder builder(6, AllocationStrategy::FIXED_SIZE);
  auto root = builder.initRoot<test::TestObject>();

  root.initObjectField<test::TestOldVersion>().setOld2("foo");

  // We should have allocated all of the first segment.
  EXPECT_EQ(1u, builder.getSegmentsForOutput().size());
  EXPECT_EQ(6u, builder.getSegmentsForOutput()[0].size());

  // Now if we upgrade...
  EXPECT_EQ("foo", root.getObjectField<test::TestNewVersion>().getOld2());

  // We should have allocated the new struct in a new segment, and also allocated the far pointer
  // landing pad in yet another segment.
  ASSERT_EQ(3u, builder.getSegmentsForOutput().size());
  EXPECT_EQ(6u, builder.getSegmentsForOutput()[0].size());
  EXPECT_EQ(6u, builder.getSegmentsForOutput()[1].size());
  EXPECT_EQ(2u, builder.getSegmentsForOutput()[2].size());
}

void checkList(List<test::TestOldVersion>::Reader reader,
               std::initializer_list<int64_t> expectedData,
               std::initializer_list<Text::Reader> expectedPointers) {
  ASSERT_EQ(expectedData.size(), reader.size());
  for (uint i = 0; i < expectedData.size(); i++) {
    EXPECT_EQ(expectedData.begin()[i], reader[i].getOld1());
    EXPECT_EQ(expectedPointers.begin()[i], reader[i].getOld2());
  }
}

void checkUpgradedList(test::TestObject::Builder root,
                       std::initializer_list<int64_t> expectedData,
                       std::initializer_list<Text::Reader> expectedPointers) {
  {
    auto builder = root.getObjectField<List<test::TestNewVersion>>();

    ASSERT_EQ(expectedData.size(), builder.size());
    for (uint i = 0; i < expectedData.size(); i++) {
      EXPECT_EQ(expectedData.begin()[i], builder[i].getOld1());
      EXPECT_EQ(expectedPointers.begin()[i], builder[i].getOld2());

      // Other fields shouldn't be set.
      EXPECT_EQ(0, builder[i].asReader().getOld3().getOld1());
      EXPECT_EQ("", builder[i].asReader().getOld3().getOld2());
      EXPECT_EQ(987, builder[i].getNew1());
      EXPECT_EQ("baz", builder[i].getNew2());

      // Write some new data.
      builder[i].setOld1(i * 123);
      builder[i].setOld2(kj::str("qux", i, '\0').begin());
      builder[i].setNew1(i * 456);
      builder[i].setNew2(kj::str("corge", i, '\0').begin());
    }
  }

  // Read the newly-written data as TestOldVersion to ensure it was updated.
  {
    auto builder = root.getObjectField<List<test::TestOldVersion>>();

    ASSERT_EQ(expectedData.size(), builder.size());
    for (uint i = 0; i < expectedData.size(); i++) {
      EXPECT_EQ(i * 123, builder[i].getOld1());
      EXPECT_EQ(Text::Reader(kj::str("qux", i, "\0").begin()), builder[i].getOld2());
    }
  }

  // Also read back as TestNewVersion again.
  {
    auto builder = root.getObjectField<List<test::TestNewVersion>>();

    ASSERT_EQ(expectedData.size(), builder.size());
    for (uint i = 0; i < expectedData.size(); i++) {
      EXPECT_EQ(i * 123, builder[i].getOld1());
      EXPECT_EQ(Text::Reader(kj::str("qux", i, '\0').begin()), builder[i].getOld2());
      EXPECT_EQ(i * 456, builder[i].getNew1());
      EXPECT_EQ(Text::Reader(kj::str("corge", i, '\0').begin()), builder[i].getNew2());
    }
  }
}

TEST(Encoding, UpgradeListInBuilder) {
  // Test every damned list upgrade.

  MallocMessageBuilder builder;
  auto root = builder.initRoot<test::TestObject>();

  // -----------------------------------------------------------------

  root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID});
  checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<bool>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint8_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
  checkUpgradedList(root, {0, 0, 0, 0}, {"", "", "", ""});

  // -----------------------------------------------------------------

  {
    root.setObjectField<List<bool>>({true, false, true, true});
    auto orig = root.asReader().getObjectField<List<bool>>();
    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
    checkList(root.getObjectField<List<bool>>(), {true, false, true, true});
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint8_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

    checkList(orig, {true, false, true, true});
    checkUpgradedList(root, {1, 0, 1, 1}, {"", "", "", ""});
    checkList(orig, {false, false, false, false});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------

  {
    root.setObjectField<List<uint8_t>>({0x12, 0x23, 0x33, 0x44});
    auto orig = root.asReader().getObjectField<List<uint8_t>>();
    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
    checkList(root.getObjectField<List<bool>>(), {false, true, true, false});
    checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44});
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

    checkList(orig, {0x12, 0x23, 0x33, 0x44});
    checkUpgradedList(root, {0x12, 0x23, 0x33, 0x44}, {"", "", "", ""});
    checkList(orig, {0, 0, 0, 0});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------

  {
    root.setObjectField<List<uint16_t>>({0x5612, 0x7823, 0xab33, 0xcd44});
    auto orig = root.asReader().getObjectField<List<uint16_t>>();
    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
    checkList(root.getObjectField<List<bool>>(), {false, true, true, false});
    checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44});
    checkList(root.getObjectField<List<uint16_t>>(), {0x5612, 0x7823, 0xab33, 0xcd44});
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

    checkList(orig, {0x5612, 0x7823, 0xab33, 0xcd44});
    checkUpgradedList(root, {0x5612, 0x7823, 0xab33, 0xcd44}, {"", "", "", ""});
    checkList(orig, {0, 0, 0, 0});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------

  {
    root.setObjectField<List<uint32_t>>({0x17595612, 0x29347823, 0x5923ab32, 0x1a39cd45});
    auto orig = root.asReader().getObjectField<List<uint32_t>>();
    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
    checkList(root.getObjectField<List<bool>>(), {false, true, false, true});
    checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x32, 0x45});
    checkList(root.getObjectField<List<uint16_t>>(), {0x5612, 0x7823, 0xab32, 0xcd45});
    checkList(root.getObjectField<List<uint32_t>>(), {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u});
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

    checkList(orig, {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u});
    checkUpgradedList(root, {0x17595612, 0x29347823, 0x5923ab32, 0x1a39cd45}, {"", "", "", ""});
    checkList(orig, {0u, 0u, 0u, 0u});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------

  {
    root.setObjectField<List<uint64_t>>({0x1234abcd8735fe21, 0x7173bc0e1923af36});
    auto orig = root.asReader().getObjectField<List<uint64_t>>();
    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID});
    checkList(root.getObjectField<List<bool>>(), {true, false});
    checkList(root.getObjectField<List<uint8_t>>(), {0x21, 0x36});
    checkList(root.getObjectField<List<uint16_t>>(), {0xfe21, 0xaf36});
    checkList(root.getObjectField<List<uint32_t>>(), {0x8735fe21u, 0x1923af36u});
    checkList(root.getObjectField<List<uint64_t>>(), {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull});
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

    checkList(orig, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull});
    checkUpgradedList(root, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}, {"", ""});
    checkList(orig, {0u, 0u});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------

  {
    root.setObjectField<List<Text>>({"foo", "bar", "baz"});
    auto orig = root.asReader().getObjectField<List<Text>>();
    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID});
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<bool>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint8_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
    EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
    checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz"});

    checkList(orig, {"foo", "bar", "baz"});
    checkUpgradedList(root, {0, 0, 0}, {"foo", "bar", "baz"});
    checkList(orig, {"", "", ""});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------

  {
    {
      auto l = root.initObjectField<List<test::TestOldVersion>>(3);
      l[0].setOld1(0x1234567890abcdef);
      l[1].setOld1(0x234567890abcdef1);
      l[2].setOld1(0x34567890abcdef12);
      l[0].setOld2("foo");
      l[1].setOld2("bar");
      l[2].setOld2("baz");
    }
    auto orig = root.asReader().getObjectField<List<test::TestOldVersion>>();

    checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID});
    checkList(root.getObjectField<List<bool>>(), {true, true, false});
    checkList(root.getObjectField<List<uint8_t>>(), {0xefu, 0xf1u, 0x12u});
    checkList(root.getObjectField<List<uint16_t>>(), {0xcdefu, 0xdef1u, 0xef12u});
    checkList(root.getObjectField<List<uint32_t>>(), {0x90abcdefu, 0x0abcdef1u, 0xabcdef12u});
    checkList(root.getObjectField<List<uint64_t>>(),
              {0x1234567890abcdefull, 0x234567890abcdef1ull, 0x34567890abcdef12ull});
    checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz"});

    checkList(orig, {0x1234567890abcdefull, 0x234567890abcdef1ull, 0x34567890abcdef12ull},
                    {"foo", "bar", "baz"});
    checkUpgradedList(root, {0x1234567890abcdefull, 0x234567890abcdef1ull, 0x34567890abcdef12ull},
                            {"foo", "bar", "baz"});
    checkList(orig, {0u, 0u, 0u}, {"", "", ""});  // old location zero'd during upgrade
  }

  // -----------------------------------------------------------------
  // OK, now we've tested upgrading every primitive list to every primitive list, every primitive
  // list to a multi-word struct, and a multi-word struct to every primitive list.  But we haven't
  // tried upgrading primitive lists to sub-word structs.

  // Upgrade from bool.
  root.setObjectField<List<bool>>({true, false, true, true});
  {
    auto orig = root.asReader().getObjectField<List<bool>>();
    checkList(orig, {true, false, true, true});
    auto l = root.getObjectField<List<test::TestLists::Struct16>>();
    checkList(orig, {false, false, false, false});  // old location zero'd during upgrade
    ASSERT_EQ(4u, l.size());
    EXPECT_EQ(1u, l[0].getF());
    EXPECT_EQ(0u, l[1].getF());
    EXPECT_EQ(1u, l[2].getF());
    EXPECT_EQ(1u, l[3].getF());
    l[0].setF(12573);
    l[1].setF(3251);
    l[2].setF(9238);
    l[3].setF(5832);
  }
  checkList(root.getObjectField<List<bool>>(), {true, true, false, false});
  checkList(root.getObjectField<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u});
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

  // Upgrade from multi-byte, sub-word data.
  root.setObjectField<List<uint16_t>>({12u, 34u, 56u, 78u});
  {
    auto orig = root.asReader().getObjectField<List<uint16_t>>();
    checkList(orig, {12u, 34u, 56u, 78u});
    auto l = root.getObjectField<List<test::TestLists::Struct32>>();
    checkList(orig, {0u, 0u, 0u, 0u});  // old location zero'd during upgrade
    ASSERT_EQ(4u, l.size());
    EXPECT_EQ(12u, l[0].getF());
    EXPECT_EQ(34u, l[1].getF());
    EXPECT_EQ(56u, l[2].getF());
    EXPECT_EQ(78u, l[3].getF());
    l[0].setF(0x65ac1235u);
    l[1].setF(0x13f12879u);
    l[2].setF(0x33423082u);
    l[3].setF(0x12988948u);
  }
  checkList(root.getObjectField<List<bool>>(), {true, true, false, false});
  checkList(root.getObjectField<List<uint8_t>>(), {0x35u, 0x79u, 0x82u, 0x48u});
  checkList(root.getObjectField<List<uint16_t>>(), {0x1235u, 0x2879u, 0x3082u, 0x8948u});
  checkList(root.getObjectField<List<uint32_t>>(),
            {0x65ac1235u, 0x13f12879u, 0x33423082u, 0x12988948u});
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

  // Upgrade from void -> data struct
  root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID});
  {
    auto l = root.getObjectField<List<test::TestLists::Struct16>>();
    ASSERT_EQ(4u, l.size());
    EXPECT_EQ(0u, l[0].getF());
    EXPECT_EQ(0u, l[1].getF());
    EXPECT_EQ(0u, l[2].getF());
    EXPECT_EQ(0u, l[3].getF());
    l[0].setF(12573);
    l[1].setF(3251);
    l[2].setF(9238);
    l[3].setF(5832);
  }
  checkList(root.getObjectField<List<bool>>(), {true, true, false, false});
  checkList(root.getObjectField<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u});
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());

  // Upgrade from void -> pointer struct
  root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID});
  {
    auto l = root.getObjectField<List<test::TestLists::StructP>>();
    ASSERT_EQ(4u, l.size());
    EXPECT_EQ("", l[0].getF());
    EXPECT_EQ("", l[1].getF());
    EXPECT_EQ("", l[2].getF());
    EXPECT_EQ("", l[3].getF());
    l[0].setF("foo");
    l[1].setF("bar");
    l[2].setF("baz");
    l[3].setF("qux");
  }
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<bool>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
  checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz", "qux"});

  // Verify that we cannot "side-grade" a pointer list to a data struct list, or a data list to
  // a pointer struct list.
  root.setObjectField<List<Text>>({"foo", "bar", "baz", "qux"});
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<test::TestLists::Struct32>>());
  root.setObjectField<List<uint32_t>>({12, 34, 56, 78});
  EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
}

// =======================================================================================
// Tests of generated code, not really of the encoding.
// TODO(cleanup):  Move to a different test?

TEST(Encoding, NestedTypes) {
  // This is more of a test of the generated code than the encoding.

  MallocMessageBuilder builder;
  TestNestedTypes::Reader reader = builder.getRoot<TestNestedTypes>().asReader();

  EXPECT_EQ(TestNestedTypes::NestedEnum::BAR, reader.getOuterNestedEnum());
  EXPECT_EQ(TestNestedTypes::NestedStruct::NestedEnum::QUUX, reader.getInnerNestedEnum());

  TestNestedTypes::NestedStruct::Reader nested = reader.getNestedStruct();
  EXPECT_EQ(TestNestedTypes::NestedEnum::BAR, nested.getOuterNestedEnum());
  EXPECT_EQ(TestNestedTypes::NestedStruct::NestedEnum::QUUX, nested.getInnerNestedEnum());
}

TEST(Encoding, Imports) {
  // Also just testing the generated code.

  MallocMessageBuilder builder;
  TestImport::Builder root = builder.getRoot<TestImport>();
  initTestMessage(root.initField());
  checkTestMessage(root.asReader().getField());
}

TEST(Encoding, Using) {
  MallocMessageBuilder builder;
  TestUsing::Reader reader = builder.getRoot<TestUsing>().asReader();
  EXPECT_EQ(TestNestedTypes::NestedEnum::BAR, reader.getOuterNestedEnum());
  EXPECT_EQ(TestNestedTypes::NestedStruct::NestedEnum::QUUX, reader.getInnerNestedEnum());
}

TEST(Encoding, StructSetters) {
  MallocMessageBuilder builder;
  auto root = builder.getRoot<TestAllTypes>();
  initTestMessage(root);

  {
    MallocMessageBuilder builder2;
    builder2.setRoot(root.asReader());
    checkTestMessage(builder2.getRoot<TestAllTypes>());
  }

  {
    MallocMessageBuilder builder2;
    auto root2 = builder2.getRoot<TestAllTypes>();
    root2.setStructField(root);
    checkTestMessage(root2.getStructField());
  }

  {
    MallocMessageBuilder builder2;
    auto root2 = builder2.getRoot<test::TestObject>();
    root2.setObjectField<test::TestAllTypes>(root);
    checkTestMessage(root2.getObjectField<test::TestAllTypes>());
  }
}

TEST(Encoding, ListSetters) {
  MallocMessageBuilder builder;
  auto root = builder.getRoot<TestListDefaults>();
  initTestMessage(root);

  {
    MallocMessageBuilder builder2;
    auto root2 = builder2.getRoot<TestListDefaults>();

    root2.getLists().setList0(root.getLists().getList0());
    root2.getLists().setList1(root.getLists().getList1());
    root2.getLists().setList8(root.getLists().getList8());
    root2.getLists().setList16(root.getLists().getList16());
    root2.getLists().setList32(root.getLists().getList32());
    root2.getLists().setList64(root.getLists().getList64());
    root2.getLists().setListP(root.getLists().getListP());

    {
      auto dst = root2.getLists().initInt32ListList(3);
      auto src = root.getLists().getInt32ListList();
      dst.set(0, src[0]);
      dst.set(1, src[1]);
      dst.set(2, src[2]);
    }

    {
      auto dst = root2.getLists().initTextListList(3);
      auto src = root.getLists().getTextListList();
      dst.set(0, src[0]);
      dst.set(1, src[1]);
      dst.set(2, src[2]);
    }

    {
      auto dst = root2.getLists().initStructListList(2);
      auto src = root.getLists().getStructListList();
      dst.set(0, src[0]);
      dst.set(1, src[1]);
    }
  }
}

TEST(Encoding, ZeroOldObject) {
  MallocMessageBuilder builder;

  auto root = builder.initRoot<TestAllTypes>();
  initTestMessage(root);

  auto oldRoot = root.asReader();
  checkTestMessage(oldRoot);

  auto oldSub = oldRoot.getStructField();
  auto oldSub2 = oldRoot.getStructList()[0];

  root = builder.initRoot<TestAllTypes>();
  checkTestMessageAllZero(oldRoot);
  checkTestMessageAllZero(oldSub);
  checkTestMessageAllZero(oldSub2);
}

TEST(Encoding, Has) {
  MallocMessageBuilder builder;

  auto root = builder.initRoot<TestAllTypes>();

  EXPECT_FALSE(root.hasTextField());
  EXPECT_FALSE(root.hasDataField());
  EXPECT_FALSE(root.hasStructField());
  EXPECT_FALSE(root.hasInt32List());

  EXPECT_FALSE(root.asReader().hasTextField());
  EXPECT_FALSE(root.asReader().hasDataField());
  EXPECT_FALSE(root.asReader().hasStructField());
  EXPECT_FALSE(root.asReader().hasInt32List());

  initTestMessage(root);

  EXPECT_TRUE(root.hasTextField());
  EXPECT_TRUE(root.hasDataField());
  EXPECT_TRUE(root.hasStructField());
  EXPECT_TRUE(root.hasInt32List());

  EXPECT_TRUE(root.asReader().hasTextField());
  EXPECT_TRUE(root.asReader().hasDataField());
  EXPECT_TRUE(root.asReader().hasStructField());
  EXPECT_TRUE(root.asReader().hasInt32List());
}

}  // namespace
}  // namespace _ (private)
}  // namespace capnp