Mercurial > hg > sv-dependency-builds
diff src/capnproto-0.6.0/c++/src/capnp/encoding-test.c++ @ 62:0994c39f1e94
Cap'n Proto v0.6 + build for OSX
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Mon, 22 May 2017 10:01:37 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/capnproto-0.6.0/c++/src/capnp/encoding-test.c++ Mon May 22 10:01:37 2017 +0100 @@ -0,0 +1,1934 @@ +// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +// Licensed under the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include <capnp/test-import.capnp.h> +#include <capnp/test-import2.capnp.h> +#include "message.h" +#include <kj/debug.h> +#include <kj/compat/gtest.h> +#include "test-util.h" +#include "schema-lite.h" +#include "serialize-packed.h" + +namespace capnp { +namespace _ { // private +namespace { + +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>().totalSize().wordCount); +} + +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)); + + checkTestMessage(TestDefaults::Reader()); +} + +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, Unions) { + MallocMessageBuilder builder; + TestUnion::Builder root = builder.getRoot<TestUnion>(); + + EXPECT_EQ(TestUnion::Union0::U0F0S0, root.getUnion0().which()); + EXPECT_EQ(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; + } +}; + +kj::String KJ_STRINGIFY(const UnionState& us) { + return kj::str("UnionState({", kj::strArray(us.discriminants, ", "), "}, ", 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))); + 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))); + 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))); + 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))); + EXPECT_EQ(UnionState({0,14,0,0}, 65), INIT_UNION(getUnion1().setU1f2s1(1))); + EXPECT_EQ(UnionState({0,15,0,0}, 72), INIT_UNION(getUnion1().setU1f2s8(1))); + EXPECT_EQ(UnionState({0,16,0,0}, 80), INIT_UNION(getUnion1().setU1f2s16(1))); + EXPECT_EQ(UnionState({0,17,0,0}, 96), 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, UnnamedUnion) { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestUnnamedUnion>(); + EXPECT_EQ(test::TestUnnamedUnion::FOO, root.which()); + + root.setBar(321); + EXPECT_EQ(test::TestUnnamedUnion::BAR, root.which()); + EXPECT_EQ(test::TestUnnamedUnion::BAR, root.asReader().which()); + EXPECT_EQ(321u, root.getBar()); + EXPECT_EQ(321u, root.asReader().getBar()); + EXPECT_DEBUG_ANY_THROW(root.getFoo()); + EXPECT_DEBUG_ANY_THROW(root.asReader().getFoo()); + + root.setFoo(123); + EXPECT_EQ(test::TestUnnamedUnion::FOO, root.which()); + EXPECT_EQ(test::TestUnnamedUnion::FOO, root.asReader().which()); + EXPECT_EQ(123u, root.getFoo()); + EXPECT_EQ(123u, root.asReader().getFoo()); + EXPECT_DEBUG_ANY_THROW(root.getBar()); + EXPECT_DEBUG_ANY_THROW(root.asReader().getBar()); + +#if !CAPNP_LITE + StructSchema schema = Schema::from<test::TestUnnamedUnion>(); + + // The discriminant is allocated just before allocating "bar". + EXPECT_EQ(2u, schema.getProto().getStruct().getDiscriminantOffset()); + EXPECT_EQ(0u, schema.getFieldByName("foo").getProto().getSlot().getOffset()); + EXPECT_EQ(2u, schema.getFieldByName("bar").getProto().getSlot().getOffset()); +#endif // !CAPNP_LITE +} + +TEST(Encoding, Groups) { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestGroups>(); + + { + auto foo = root.getGroups().initFoo(); + foo.setCorge(12345678); + foo.setGrault(123456789012345ll); + foo.setGarply("foobar"); + + EXPECT_EQ(12345678, foo.getCorge()); + EXPECT_EQ(123456789012345ll, foo.getGrault()); + EXPECT_EQ("foobar", foo.getGarply()); + } + + { + auto bar = root.getGroups().initBar(); + bar.setCorge(23456789); + bar.setGrault("barbaz"); + bar.setGarply(234567890123456ll); + + EXPECT_EQ(23456789, bar.getCorge()); + EXPECT_EQ("barbaz", bar.getGrault()); + EXPECT_EQ(234567890123456ll, bar.getGarply()); + } + + { + auto baz = root.getGroups().initBaz(); + baz.setCorge(34567890); + baz.setGrault("bazqux"); + baz.setGarply("quxquux"); + + EXPECT_EQ(34567890, baz.getCorge()); + EXPECT_EQ("bazqux", baz.getGrault()); + EXPECT_EQ("quxquux", baz.getGarply()); + } +} + +TEST(Encoding, InterleavedGroups) { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestInterleavedGroups>(); + + // Init both groups to different values. + { + auto group = root.getGroup1(); + group.setFoo(12345678u); + group.setBar(123456789012345llu); + auto corge = group.initCorge(); + corge.setGrault(987654321098765llu); + corge.setGarply(12345u); + corge.setPlugh("plugh"); + corge.setXyzzy("xyzzy"); + group.setWaldo("waldo"); + } + + { + auto group = root.getGroup2(); + group.setFoo(23456789u); + group.setBar(234567890123456llu); + auto corge = group.initCorge(); + corge.setGrault(876543210987654llu); + corge.setGarply(23456u); + corge.setPlugh("hgulp"); + corge.setXyzzy("yzzyx"); + group.setWaldo("odlaw"); + } + + // Check group1 is still set correctly. + { + auto group = root.asReader().getGroup1(); + EXPECT_EQ(12345678u, group.getFoo()); + EXPECT_EQ(123456789012345llu, group.getBar()); + auto corge = group.getCorge(); + EXPECT_EQ(987654321098765llu, corge.getGrault()); + EXPECT_EQ(12345u, corge.getGarply()); + EXPECT_EQ("plugh", corge.getPlugh()); + EXPECT_EQ("xyzzy", corge.getXyzzy()); + EXPECT_EQ("waldo", group.getWaldo()); + } + + // Zero out group 1 and see if it is zero'd. + { + auto group = root.initGroup1().asReader(); + EXPECT_EQ(0u, group.getFoo()); + EXPECT_EQ(0u, group.getBar()); + EXPECT_EQ(test::TestInterleavedGroups::Group1::QUX, group.which()); + EXPECT_EQ(0u, group.getQux()); + EXPECT_FALSE(group.hasWaldo()); + } + + // Group 2 should not have been touched. + { + auto group = root.asReader().getGroup2(); + EXPECT_EQ(23456789u, group.getFoo()); + EXPECT_EQ(234567890123456llu, group.getBar()); + auto corge = group.getCorge(); + EXPECT_EQ(876543210987654llu, corge.getGrault()); + EXPECT_EQ(23456u, corge.getGarply()); + EXPECT_EQ("hgulp", corge.getPlugh()); + EXPECT_EQ("yzzyx", corge.getXyzzy()); + EXPECT_EQ("odlaw", group.getWaldo()); + } +} + +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, field.getUnion0().getU0f1s0()); + EXPECT_EQ("foo", field.getUnion1().getU1f0sp()); + EXPECT_EQ(true, field.getUnion2().getU2f0s1()); + EXPECT_EQ(12345678, field.getUnion3().getU3f0s32()); + } + + { + auto field = reader.getUnnamed1(); + EXPECT_EQ(test::TestUnnamedUnion::FOO, field.which()); + EXPECT_EQ(123u, field.getFoo()); + EXPECT_FALSE(field.hasBefore()); + EXPECT_FALSE(field.hasAfter()); + } + + { + auto field = reader.getUnnamed2(); + EXPECT_EQ(test::TestUnnamedUnion::BAR, field.which()); + EXPECT_EQ(321u, field.getBar()); + EXPECT_EQ("foo", field.getBefore()); + EXPECT_EQ("bar", field.getAfter()); + } +} + +// ======================================================================================= + +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); l[1].setF(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 < kj::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, SetListToEmpty) { + // Test initializing list fields from various ways of constructing zero-sized lists. + // At one point this would often fail because the lists would have ElementSize::VOID which is + // incompatible with other list sizes. + +#define ALL_LIST_TYPES(MACRO) \ + MACRO(Void, Void) \ + MACRO(Bool, bool) \ + MACRO(UInt8, uint8_t) \ + MACRO(UInt16, uint16_t) \ + MACRO(UInt32, uint32_t) \ + MACRO(UInt64, uint64_t) \ + MACRO(Int8, int8_t) \ + MACRO(Int16, int16_t) \ + MACRO(Int32, int32_t) \ + MACRO(Int64, int64_t) \ + MACRO(Float32, float) \ + MACRO(Float64, double) \ + MACRO(Text, Text) \ + MACRO(Data, Data) \ + MACRO(Struct, TestAllTypes) + +#define SET_FROM_READER_ACCESSOR(name, type) \ + root.set##name##List(reader.get##name##List()); + +#define SET_FROM_BUILDER_ACCESSOR(name, type) \ + root.set##name##List(root.get##name##List()); + +#define SET_FROM_READER_CONSTRUCTOR(name, type) \ + root.set##name##List(List<type>::Reader()); + +#define SET_FROM_BUILDER_CONSTRUCTOR(name, type) \ + root.set##name##List(List<type>::Builder()); + +#define CHECK_EMPTY_NONNULL(name, type) \ + EXPECT_TRUE(root.has##name##List()); \ + EXPECT_EQ(0, root.get##name##List().size()); + + { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAllTypes>(); + auto reader = root.asReader(); + ALL_LIST_TYPES(SET_FROM_READER_ACCESSOR) + ALL_LIST_TYPES(CHECK_EMPTY_NONNULL) + } + + { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAllTypes>(); + ALL_LIST_TYPES(SET_FROM_BUILDER_ACCESSOR) + ALL_LIST_TYPES(CHECK_EMPTY_NONNULL) + } + + { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAllTypes>(); + ALL_LIST_TYPES(SET_FROM_READER_CONSTRUCTOR) + ALL_LIST_TYPES(CHECK_EMPTY_NONNULL) + } + + { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAllTypes>(); + ALL_LIST_TYPES(SET_FROM_BUILDER_CONSTRUCTOR) + ALL_LIST_TYPES(CHECK_EMPTY_NONNULL) + } + +#undef SET_FROM_READER_ACCESSOR +#undef SET_FROM_BUILDER_ACCESSOR +#undef SET_FROM_READER_CONSTRUCTOR +#undef SET_FROM_BUILDER_CONSTRUCTOR +#undef CHECK_EMPTY_NONNULL +} + +#if CAPNP_EXPENSIVE_TESTS +TEST(Encoding, LongList) { + // This test allocates 512MB of contiguous memory and takes several seconds, so we usually don't + // run it. It is run before release, though. + + MallocMessageBuilder builder; + + auto root = builder.initRoot<TestAllTypes>(); + uint length = 1 << 27; + auto list = root.initUInt64List(length); + for (uint ii = 0; ii < length; ++ii) { + list.set(ii, ii); + } + for (uint ii = 0; ii < length; ++ii) { + ASSERT_EQ(list[ii], ii); + } +} +#endif + +// ======================================================================================= + +TEST(Encoding, ListUpgrade) { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAnyPointer>(); + + root.getAnyPointerField().setAs<List<uint16_t>>({12, 34, 56}); + + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {12, 34, 56}); + + { + auto l = root.getAnyPointerField().getAs<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.getAnyPointerField().getAs<List<uint16_t>>(), {12, 34, 56}); + + auto reader = root.asReader(); + + checkList(reader.getAnyPointerField().getAs<List<uint8_t>>(), {12, 34, 56}); + + { + auto l = reader.getAnyPointerField().getAs<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()); + } + + root.getAnyPointerField().setAs<List<uint16_t>>({12, 34, 56}); + + { + kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() { + reader.getAnyPointerField().getAs<List<uint32_t>>(); +#if !KJ_NO_EXCEPTIONS + ADD_FAILURE() << "Should have thrown an exception."; +#endif + }); + + KJ_EXPECT(e != nullptr, "Should have thrown an exception."); + } + + { + auto l = reader.getAnyPointerField().getAs<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.getAnyPointerField().getAs<List<uint16_t>>(), {12, 34, 56}); +} + +TEST(Encoding, BitListDowngrade) { + // NO LONGER SUPPORTED -- We check for exceptions thrown. + + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAnyPointer>(); + + root.getAnyPointerField().setAs<List<uint16_t>>({0x1201u, 0x3400u, 0x5601u, 0x7801u}); + + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + + { + auto l = root.getAnyPointerField().getAs<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.getAnyPointerField().getAs<List<uint16_t>>(), + {0x1201u, 0x3400u, 0x5601u, 0x7801u}); + + auto reader = root.asReader(); + + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + + { + auto l = reader.getAnyPointerField().getAs<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.getAnyPointerField().getAs<List<uint16_t>>(), + {0x1201u, 0x3400u, 0x5601u, 0x7801u}); +} + +TEST(Encoding, BitListDowngradeFromStruct) { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAnyPointer>(); + + { + auto list = root.getAnyPointerField().initAs<List<test::TestLists::Struct1c>>(4); + list[0].setF(true); + list[1].setF(false); + list[2].setF(true); + list[3].setF(true); + } + + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + + { + auto l = root.getAnyPointerField().getAs<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(); + + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + + { + auto l = reader.getAnyPointerField().getAs<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()); + } +} + +TEST(Encoding, BitListUpgrade) { + // No longer supported! + + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAnyPointer>(); + + root.getAnyPointerField().setAs<List<bool>>({true, false, true, true}); + + { + kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() { + root.getAnyPointerField().getAs<List<test::TestLists::Struct1>>(); +#if !KJ_NO_EXCEPTIONS + ADD_FAILURE() << "Should have thrown an exception."; +#endif + }); + + KJ_EXPECT(e != nullptr, "Should have thrown an exception."); + } + + auto reader = root.asReader(); + + { + kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() { + reader.getAnyPointerField().getAs<List<test::TestLists::Struct1>>(); +#if !KJ_NO_EXCEPTIONS + ADD_FAILURE() << "Should have thrown an exception."; +#endif + }); + + KJ_EXPECT(e != nullptr, "Should have thrown an exception."); + } +} + +TEST(Encoding, UpgradeStructInBuilder) { + MallocMessageBuilder builder; + auto root = builder.initRoot<test::TestAnyPointer>(); + + test::TestOldVersion::Reader oldReader; + + { + auto oldVersion = root.getAnyPointerField().initAs<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.getAnyPointerField().getAs<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.getAnyPointerField().getAs<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.getAnyPointerField().getAs<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 actually not such a complicated case. + + MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE); + auto root = builder.initRoot<test::TestAnyPointer>(); + + // 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.getAnyPointerField().initAs<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.getAnyPointerField().getAs<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.getAnyPointerField().getAs<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.getAnyPointerField().getAs<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::TestAnyPointer>(); + + root.getAnyPointerField().initAs<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.getAnyPointerField().getAs<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::TestAnyPointer>(); + + root.getAnyPointerField().initAs<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.getAnyPointerField().getAs<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 checkUpgradedList(test::TestAnyPointer::Builder root, + std::initializer_list<int64_t> expectedData, + std::initializer_list<Text::Reader> expectedPointers) { + { + auto builder = root.getAnyPointerField().getAs<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.getAnyPointerField().getAs<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.getAnyPointerField().getAs<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::TestAnyPointer>(); + + // ----------------------------------------------------------------- + + root.getAnyPointerField().setAs<List<Void>>({VOID, VOID, VOID, VOID}); + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID, VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint8_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint16_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Text>>()); + checkUpgradedList(root, {0, 0, 0, 0}, {"", "", "", ""}); + + // ----------------------------------------------------------------- + + { + root.getAnyPointerField().setAs<List<bool>>({true, false, true, true}); + auto orig = root.asReader().getAnyPointerField().getAs<List<bool>>(); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Void>>()); + checkList(root.getAnyPointerField().getAs<List<bool>>(), {true, false, true, true}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint8_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint16_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Text>>()); + + checkList(orig, {true, false, true, true}); + + // Can't upgrade bit lists. (This used to be supported.) + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<test::TestNewVersion>>()); + } + + // ----------------------------------------------------------------- + + { + root.getAnyPointerField().setAs<List<uint8_t>>({0x12, 0x23, 0x33, 0x44}); + auto orig = root.asReader().getAnyPointerField().getAs<List<uint8_t>>(); + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID, VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint16_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<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.getAnyPointerField().setAs<List<uint16_t>>({0x5612, 0x7823, 0xab33, 0xcd44}); + auto orig = root.asReader().getAnyPointerField().getAs<List<uint16_t>>(); + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID, VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44}); + checkList(root.getAnyPointerField().getAs<List<uint16_t>>(), {0x5612, 0x7823, 0xab33, 0xcd44}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<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.getAnyPointerField().setAs<List<uint32_t>>({0x17595612, 0x29347823, 0x5923ab32, 0x1a39cd45}); + auto orig = root.asReader().getAnyPointerField().getAs<List<uint32_t>>(); + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID, VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {0x12, 0x23, 0x32, 0x45}); + checkList(root.getAnyPointerField().getAs<List<uint16_t>>(), {0x5612, 0x7823, 0xab32, 0xcd45}); + checkList(root.getAnyPointerField().getAs<List<uint32_t>>(), {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<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.getAnyPointerField().setAs<List<uint64_t>>({0x1234abcd8735fe21, 0x7173bc0e1923af36}); + auto orig = root.asReader().getAnyPointerField().getAs<List<uint64_t>>(); + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {0x21, 0x36}); + checkList(root.getAnyPointerField().getAs<List<uint16_t>>(), {0xfe21, 0xaf36}); + checkList(root.getAnyPointerField().getAs<List<uint32_t>>(), {0x8735fe21u, 0x1923af36u}); + checkList(root.getAnyPointerField().getAs<List<uint64_t>>(), {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Text>>()); + + checkList(orig, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}); + checkUpgradedList(root, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}, {"", ""}); + checkList(orig, {0u, 0u}); // old location zero'd during upgrade + } + + // ----------------------------------------------------------------- + + { + root.getAnyPointerField().setAs<List<Text>>({"foo", "bar", "baz"}); + auto orig = root.asReader().getAnyPointerField().getAs<List<Text>>(); + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint8_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint16_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + checkList(root.getAnyPointerField().getAs<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.getAnyPointerField().initAs<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().getAnyPointerField().getAs<List<test::TestOldVersion>>(); + + checkList(root.getAnyPointerField().getAs<List<Void>>(), {VOID, VOID, VOID}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {0xefu, 0xf1u, 0x12u}); + checkList(root.getAnyPointerField().getAs<List<uint16_t>>(), {0xcdefu, 0xdef1u, 0xef12u}); + checkList(root.getAnyPointerField().getAs<List<uint32_t>>(), {0x90abcdefu, 0x0abcdef1u, 0xabcdef12u}); + checkList(root.getAnyPointerField().getAs<List<uint64_t>>(), + {0x1234567890abcdefull, 0x234567890abcdef1ull, 0x34567890abcdef12ull}); + checkList(root.getAnyPointerField().getAs<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 multi-byte, sub-word data. + root.getAnyPointerField().setAs<List<uint16_t>>({12u, 34u, 56u, 78u}); + { + auto orig = root.asReader().getAnyPointerField().getAs<List<uint16_t>>(); + checkList(orig, {12u, 34u, 56u, 78u}); + auto l = root.getAnyPointerField().getAs<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); + } + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint8_t>>(), {0x35u, 0x79u, 0x82u, 0x48u}); + checkList(root.getAnyPointerField().getAs<List<uint16_t>>(), {0x1235u, 0x2879u, 0x3082u, 0x8948u}); + checkList(root.getAnyPointerField().getAs<List<uint32_t>>(), + {0x65ac1235u, 0x13f12879u, 0x33423082u, 0x12988948u}); + checkList(root.getAnyPointerField().getAs<List<uint64_t>>(), + {0x65ac1235u, 0x13f12879u, 0x33423082u, 0x12988948u}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Text>>()); + + // Upgrade from void -> data struct + root.getAnyPointerField().setAs<List<Void>>({VOID, VOID, VOID, VOID}); + { + auto l = root.getAnyPointerField().getAs<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); + } + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<bool>>()); + checkList(root.getAnyPointerField().getAs<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u}); + checkList(root.getAnyPointerField().getAs<List<uint32_t>>(), {12573u, 3251u, 9238u, 5832u}); + checkList(root.getAnyPointerField().getAs<List<uint64_t>>(), {12573u, 3251u, 9238u, 5832u}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Text>>()); + + // Upgrade from void -> pointer struct + root.getAnyPointerField().setAs<List<Void>>({VOID, VOID, VOID, VOID}); + { + auto l = root.getAnyPointerField().getAs<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.getAnyPointerField().getAs<List<bool>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint16_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint64_t>>()); + checkList(root.getAnyPointerField().getAs<List<Text>>(), {"foo", "bar", "baz", "qux"}); + + // Verify that we cannot "side-grade" a pointer list to a data list, or a data list to + // a pointer struct list. + root.getAnyPointerField().setAs<List<Text>>({"foo", "bar", "baz", "qux"}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<uint32_t>>()); + root.getAnyPointerField().setAs<List<uint32_t>>({12, 34, 56, 78}); + EXPECT_NONFATAL_FAILURE(root.getAnyPointerField().getAs<List<Text>>()); +} + +TEST(Encoding, UpgradeUnion) { + // This tests for a specific case that was broken originally. + MallocMessageBuilder builder; + + { + auto root = builder.getRoot<test::TestOldUnionVersion>(); + root.setB(123); + } + + { + auto root = builder.getRoot<test::TestNewUnionVersion>(); + ASSERT_TRUE(root.isB()) + EXPECT_EQ(123, root.getB()); + } +} + +// ======================================================================================= +// 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()); + } + + { + MallocMessageBuilder builder; + TestImport2::Builder root = builder.getRoot<TestImport2>(); + initTestMessage(root.initFoo()); + checkTestMessage(root.asReader().getFoo()); + root.setBar(schemaProto<TestAllTypes>()); + initTestMessage(root.initBaz().initField()); + checkTestMessage(root.asReader().getBaz().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::TestAnyPointer>(); + root2.getAnyPointerField().setAs<test::TestAllTypes>(root); + checkTestMessage(root2.getAnyPointerField().getAs<test::TestAllTypes>()); + } +} + +TEST(Encoding, OneBitStructSetters) { + // Test case of setting a 1-bit struct. + + MallocMessageBuilder builder; + auto root = builder.getRoot<test::TestLists>(); + auto list = root.initList1(8); + list[0].setF(true); + list[1].setF(true); + list[2].setF(false); + list[3].setF(true); + list[4].setF(true); + list[5].setF(false); + list[6].setF(true); + list[7].setF(false); + + MallocMessageBuilder builder2; + builder2.setRoot(list.asReader()[2]); + EXPECT_FALSE(builder2.getRoot<test::TestLists::Struct1>().getF()); + builder2.setRoot(list.asReader()[6]); + EXPECT_TRUE(builder2.getRoot<test::TestLists::Struct1>().getF()); +} + +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()); +} + +TEST(Encoding, VoidListAmplification) { + MallocMessageBuilder builder; + builder.initRoot<test::TestAnyPointer>().getAnyPointerField().initAs<List<Void>>(1u << 28); + + auto segments = builder.getSegmentsForOutput(); + EXPECT_EQ(1, segments.size()); + EXPECT_LT(segments[0].size(), 16); // quite small for such a big list! + + SegmentArrayMessageReader reader(builder.getSegmentsForOutput()); + auto root = reader.getRoot<test::TestAnyPointer>().getAnyPointerField(); + EXPECT_NONFATAL_FAILURE(root.getAs<List<TestAllTypes>>()); + + MallocMessageBuilder copy; + EXPECT_NONFATAL_FAILURE(copy.setRoot(reader.getRoot<AnyPointer>())); +} + +TEST(Encoding, EmptyStructListAmplification) { + MallocMessageBuilder builder(1024); + auto listList = builder.initRoot<test::TestAnyPointer>().getAnyPointerField() + .initAs<List<List<test::TestEmptyStruct>>>(500); + + for (uint i = 0; i < listList.size(); i++) { + listList.init(i, 1u << 28); + } + + auto segments = builder.getSegmentsForOutput(); + ASSERT_EQ(1, segments.size()); + + SegmentArrayMessageReader reader(builder.getSegmentsForOutput()); + auto root = reader.getRoot<test::TestAnyPointer>(); + auto listListReader = root.getAnyPointerField().getAs<List<List<TestAllTypes>>>(); + EXPECT_NONFATAL_FAILURE(listListReader[0]); + EXPECT_NONFATAL_FAILURE(listListReader[10]); + + EXPECT_EQ(segments[0].size() - 1, root.totalSize().wordCount); +} + +TEST(Encoding, Constants) { + EXPECT_EQ(VOID, test::TestConstants::VOID_CONST); + EXPECT_EQ(true, test::TestConstants::BOOL_CONST); + EXPECT_EQ(-123, test::TestConstants::INT8_CONST); + EXPECT_EQ(-12345, test::TestConstants::INT16_CONST); + EXPECT_EQ(-12345678, test::TestConstants::INT32_CONST); + EXPECT_EQ(-123456789012345ll, test::TestConstants::INT64_CONST); + EXPECT_EQ(234u, test::TestConstants::UINT8_CONST); + EXPECT_EQ(45678u, test::TestConstants::UINT16_CONST); + EXPECT_EQ(3456789012u, test::TestConstants::UINT32_CONST); + EXPECT_EQ(12345678901234567890ull, test::TestConstants::UINT64_CONST); + EXPECT_FLOAT_EQ(1234.5f, test::TestConstants::FLOAT32_CONST); + EXPECT_DOUBLE_EQ(-123e45, test::TestConstants::FLOAT64_CONST); + EXPECT_EQ("foo", *test::TestConstants::TEXT_CONST); + EXPECT_EQ(data("bar"), test::TestConstants::DATA_CONST); + { + TestAllTypes::Reader subReader = test::TestConstants::STRUCT_CONST; + EXPECT_EQ(VOID, subReader.getVoidField()); + EXPECT_EQ(true, subReader.getBoolField()); + EXPECT_EQ(-12, subReader.getInt8Field()); + EXPECT_EQ(3456, subReader.getInt16Field()); + EXPECT_EQ(-78901234, subReader.getInt32Field()); + EXPECT_EQ(56789012345678ll, subReader.getInt64Field()); + EXPECT_EQ(90u, subReader.getUInt8Field()); + EXPECT_EQ(1234u, subReader.getUInt16Field()); + EXPECT_EQ(56789012u, subReader.getUInt32Field()); + EXPECT_EQ(345678901234567890ull, subReader.getUInt64Field()); + EXPECT_FLOAT_EQ(-1.25e-10f, subReader.getFloat32Field()); + EXPECT_DOUBLE_EQ(345, subReader.getFloat64Field()); + EXPECT_EQ("baz", subReader.getTextField()); + EXPECT_EQ(data("qux"), subReader.getDataField()); + { + auto subSubReader = subReader.getStructField(); + EXPECT_EQ("nested", subSubReader.getTextField()); + EXPECT_EQ("really nested", subSubReader.getStructField().getTextField()); + } + EXPECT_EQ(TestEnum::BAZ, subReader.getEnumField()); + + checkList(subReader.getVoidList(), {VOID, VOID, VOID}); + checkList(subReader.getBoolList(), {false, true, false, true, true}); + checkList(subReader.getInt8List(), {12, -34, -0x80, 0x7f}); + checkList(subReader.getInt16List(), {1234, -5678, -0x8000, 0x7fff}); + // gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1. + checkList(subReader.getInt32List(), {12345678, -90123456, -0x7fffffff - 1, 0x7fffffff}); + checkList(subReader.getInt64List(), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll}); + checkList(subReader.getUInt8List(), {12u, 34u, 0u, 0xffu}); + checkList(subReader.getUInt16List(), {1234u, 5678u, 0u, 0xffffu}); + checkList(subReader.getUInt32List(), {12345678u, 90123456u, 0u, 0xffffffffu}); + checkList(subReader.getUInt64List(), {123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull}); + checkList(subReader.getFloat32List(), {0.0f, 1234567.0f, 1e37f, -1e37f, 1e-37f, -1e-37f}); + checkList(subReader.getFloat64List(), {0.0, 123456789012345.0, 1e306, -1e306, 1e-306, -1e-306}); + checkList(subReader.getTextList(), {"quux", "corge", "grault"}); + checkList(subReader.getDataList(), {data("garply"), data("waldo"), data("fred")}); + { + auto listReader = subReader.getStructList(); + ASSERT_EQ(3u, listReader.size()); + EXPECT_EQ("x structlist 1", listReader[0].getTextField()); + EXPECT_EQ("x structlist 2", listReader[1].getTextField()); + EXPECT_EQ("x structlist 3", listReader[2].getTextField()); + } + checkList(subReader.getEnumList(), {TestEnum::QUX, TestEnum::BAR, TestEnum::GRAULT}); + } + EXPECT_EQ(TestEnum::CORGE, test::TestConstants::ENUM_CONST); + + EXPECT_EQ(6u, test::TestConstants::VOID_LIST_CONST->size()); + checkList(*test::TestConstants::BOOL_LIST_CONST, {true, false, false, true}); + checkList(*test::TestConstants::INT8_LIST_CONST, {111, -111}); + checkList(*test::TestConstants::INT16_LIST_CONST, {11111, -11111}); + checkList(*test::TestConstants::INT32_LIST_CONST, {111111111, -111111111}); + checkList(*test::TestConstants::INT64_LIST_CONST, {1111111111111111111ll, -1111111111111111111ll}); + checkList(*test::TestConstants::UINT8_LIST_CONST, {111u, 222u}); + checkList(*test::TestConstants::UINT16_LIST_CONST, {33333u, 44444u}); + checkList(*test::TestConstants::UINT32_LIST_CONST, {3333333333u}); + checkList(*test::TestConstants::UINT64_LIST_CONST, {11111111111111111111ull}); + { + List<float>::Reader listReader = test::TestConstants::FLOAT32_LIST_CONST; + ASSERT_EQ(4u, listReader.size()); + EXPECT_EQ(5555.5f, listReader[0]); + EXPECT_EQ(kj::inf(), listReader[1]); + EXPECT_EQ(-kj::inf(), listReader[2]); + EXPECT_TRUE(listReader[3] != listReader[3]); + } + { + List<double>::Reader listReader = test::TestConstants::FLOAT64_LIST_CONST; + ASSERT_EQ(4u, listReader.size()); + EXPECT_EQ(7777.75, listReader[0]); + EXPECT_EQ(kj::inf(), listReader[1]); + EXPECT_EQ(-kj::inf(), listReader[2]); + EXPECT_TRUE(listReader[3] != listReader[3]); + } + checkList(*test::TestConstants::TEXT_LIST_CONST, {"plugh", "xyzzy", "thud"}); + checkList(*test::TestConstants::DATA_LIST_CONST, {data("oops"), data("exhausted"), data("rfc3092")}); + { + List<TestAllTypes>::Reader listReader = test::TestConstants::STRUCT_LIST_CONST; + ASSERT_EQ(3u, listReader.size()); + EXPECT_EQ("structlist 1", listReader[0].getTextField()); + EXPECT_EQ("structlist 2", listReader[1].getTextField()); + EXPECT_EQ("structlist 3", listReader[2].getTextField()); + } + checkList(*test::TestConstants::ENUM_LIST_CONST, {TestEnum::FOO, TestEnum::GARPLY}); +} + +TEST(Encoding, AnyPointerConstants) { + auto reader = test::ANY_POINTER_CONSTANTS.get(); + + EXPECT_EQ("baz", reader.getAnyKindAsStruct().getAs<TestAllTypes>().getTextField()); + EXPECT_EQ("baz", reader.getAnyStructAsStruct().as<TestAllTypes>().getTextField()); + + EXPECT_EQ(111111111, reader.getAnyKindAsList().getAs<List<int32_t>>()[0]); + EXPECT_EQ(111111111, reader.getAnyListAsList().as<List<int32_t>>()[0]); +} + +TEST(Encoding, GlobalConstants) { + EXPECT_EQ(12345u, test::GLOBAL_INT); + EXPECT_EQ("foobar", test::GLOBAL_TEXT.get()); + EXPECT_EQ(54321, test::GLOBAL_STRUCT->getInt32Field()); + + TestAllTypes::Reader reader = test::DERIVED_CONSTANT; + + EXPECT_EQ(12345, reader.getUInt32Field()); + EXPECT_EQ("foo", reader.getTextField()); + checkList(reader.getStructField().getTextList(), {"quux", "corge", "grault"}); + checkList(reader.getInt16List(), {11111, -11111}); + { + List<TestAllTypes>::Reader listReader = reader.getStructList(); + ASSERT_EQ(3u, listReader.size()); + EXPECT_EQ("structlist 1", listReader[0].getTextField()); + EXPECT_EQ("structlist 2", listReader[1].getTextField()); + EXPECT_EQ("structlist 3", listReader[2].getTextField()); + } +} + +TEST(Encoding, Embeds) { + { + kj::ArrayInputStream input(test::EMBEDDED_DATA); + PackedMessageReader reader(input); + checkTestMessage(reader.getRoot<TestAllTypes>()); + } + +#if !CAPNP_LITE + + { + MallocMessageBuilder builder; + auto root = builder.getRoot<TestAllTypes>(); + initTestMessage(root); + kj::StringPtr text = test::EMBEDDED_TEXT; + EXPECT_EQ(kj::str(root, text.endsWith("\r\n") ? "\r\n" : "\n"), text); + } + +#endif // CAPNP_LITE + + { + checkTestMessage(test::EMBEDDED_STRUCT); + } +} + +TEST(Encoding, HasEmptyStruct) { + MallocMessageBuilder message; + auto root = message.initRoot<test::TestAnyPointer>(); + + EXPECT_EQ(1, root.totalSize().wordCount); + + EXPECT_FALSE(root.asReader().hasAnyPointerField()); + EXPECT_FALSE(root.hasAnyPointerField()); + root.getAnyPointerField().initAs<test::TestEmptyStruct>(); + EXPECT_TRUE(root.asReader().hasAnyPointerField()); + EXPECT_TRUE(root.hasAnyPointerField()); + + EXPECT_EQ(1, root.totalSize().wordCount); +} + +TEST(Encoding, HasEmptyList) { + MallocMessageBuilder message; + auto root = message.initRoot<test::TestAnyPointer>(); + + EXPECT_EQ(1, root.totalSize().wordCount); + + EXPECT_FALSE(root.asReader().hasAnyPointerField()); + EXPECT_FALSE(root.hasAnyPointerField()); + root.getAnyPointerField().initAs<List<int32_t>>(0); + EXPECT_TRUE(root.asReader().hasAnyPointerField()); + EXPECT_TRUE(root.hasAnyPointerField()); + + EXPECT_EQ(1, root.totalSize().wordCount); +} + +TEST(Encoding, HasEmptyStructList) { + MallocMessageBuilder message; + auto root = message.initRoot<test::TestAnyPointer>(); + + EXPECT_EQ(1, root.totalSize().wordCount); + + EXPECT_FALSE(root.asReader().hasAnyPointerField()); + EXPECT_FALSE(root.hasAnyPointerField()); + root.getAnyPointerField().initAs<List<TestAllTypes>>(0); + EXPECT_TRUE(root.asReader().hasAnyPointerField()); + EXPECT_TRUE(root.hasAnyPointerField()); + + EXPECT_EQ(2, root.totalSize().wordCount); +} + +TEST(Encoding, NameAnnotation) { + EXPECT_EQ(2, static_cast<uint16_t>(test::RenamedStruct::RenamedEnum::QUX)); + EXPECT_EQ(2, static_cast<uint16_t>(test::RenamedStruct::RenamedNestedStruct::RenamedDeeplyNestedEnum::GARPLY)); + + MallocMessageBuilder message; + auto root = message.initRoot<test::RenamedStruct>(); + + root.setGoodFieldName(true); + EXPECT_EQ(true, root.getGoodFieldName()); + EXPECT_TRUE(root.isGoodFieldName()); + + root.setBar(0xff); + EXPECT_FALSE(root.isGoodFieldName()); + + root.setAnotherGoodFieldName(test::RenamedStruct::RenamedEnum::QUX); + EXPECT_EQ(test::RenamedStruct::RenamedEnum::QUX, root.getAnotherGoodFieldName()); + + EXPECT_FALSE(root.getRenamedUnion().isQux()); + auto quxBuilder = root.getRenamedUnion().initQux(); + EXPECT_TRUE(root.getRenamedUnion().isQux()); + EXPECT_FALSE(root.getRenamedUnion().getQux().hasAnotherGoodNestedFieldName()); + + quxBuilder.setGoodNestedFieldName(true); + EXPECT_EQ(true, quxBuilder.getGoodNestedFieldName()); + + EXPECT_FALSE(quxBuilder.hasAnotherGoodNestedFieldName()); + auto nestedFieldBuilder = quxBuilder.initAnotherGoodNestedFieldName(); + EXPECT_TRUE(quxBuilder.hasAnotherGoodNestedFieldName()); + + nestedFieldBuilder.setGoodNestedFieldName(true); + EXPECT_EQ(true, nestedFieldBuilder.getGoodNestedFieldName()); + EXPECT_FALSE(nestedFieldBuilder.hasAnotherGoodNestedFieldName()); + + EXPECT_FALSE(root.getRenamedUnion().isRenamedGroup()); + auto renamedGroupBuilder KJ_UNUSED = root.getRenamedUnion().initRenamedGroup(); + EXPECT_TRUE(root.getRenamedUnion().isRenamedGroup()); + + test::RenamedInterface::RenamedMethodParams::Reader renamedInterfaceParams; + renamedInterfaceParams.getRenamedParam(); +} + +TEST(Encoding, DefaultFloatPlusNan) { + MallocMessageBuilder message; + auto root = message.initRoot<TestDefaults>(); + + root.setFloat32Field(kj::nan()); + root.setFloat64Field(kj::nan()); + + float f = root.getFloat32Field(); + EXPECT_TRUE(f != f); + + double d = root.getFloat64Field(); + EXPECT_TRUE(d != d); +} + +TEST(Encoding, WholeFloatDefault) { + MallocMessageBuilder message; + auto root = message.initRoot<test::TestWholeFloatDefault>(); + + EXPECT_EQ(123.0f, root.getField()); + EXPECT_EQ(2e30f, root.getBigField()); + EXPECT_EQ(456.0f, test::TestWholeFloatDefault::CONSTANT); + EXPECT_EQ(4e30f, test::TestWholeFloatDefault::BIG_CONSTANT); +} + +TEST(Encoding, Generics) { + MallocMessageBuilder message; + auto root = message.initRoot<test::TestUseGenerics>(); + auto reader = root.asReader(); + + initTestMessage(root.initBasic().initFoo()); + checkTestMessage(reader.getBasic().getFoo()); + + { + auto typed = root.getBasic(); + test::TestGenerics<>::Reader generic = typed.asGeneric<>(); + checkTestMessage(generic.getFoo().getAs<TestAllTypes>()); + test::TestGenerics<TestAllTypes>::Reader halfGeneric = typed.asGeneric<TestAllTypes>(); + checkTestMessage(halfGeneric.getFoo()); + } + + { + auto typed = root.getBasic().asReader(); + test::TestGenerics<>::Reader generic = typed.asGeneric<>(); + checkTestMessage(generic.getFoo().getAs<TestAllTypes>()); + test::TestGenerics<TestAllTypes>::Reader halfGeneric = typed.asGeneric<TestAllTypes>(); + checkTestMessage(halfGeneric.getFoo()); + } + + initTestMessage(root.initInner().initFoo()); + checkTestMessage(reader.getInner().getFoo()); + + { + auto typed = root.getInner(); + test::TestGenerics<>::Inner::Reader generic = typed.asTestGenericsGeneric<>(); + checkTestMessage(generic.getFoo().getAs<TestAllTypes>()); + test::TestGenerics<TestAllTypes>::Inner::Reader halfGeneric = typed.asTestGenericsGeneric<TestAllTypes>(); + checkTestMessage(halfGeneric.getFoo()); + } + + { + auto typed = root.getInner().asReader(); + test::TestGenerics<>::Inner::Reader generic = typed.asTestGenericsGeneric<>(); + checkTestMessage(generic.getFoo().getAs<TestAllTypes>()); + test::TestGenerics<TestAllTypes>::Inner::Reader halfGeneric = typed.asTestGenericsGeneric<TestAllTypes>(); + checkTestMessage(halfGeneric.getFoo()); + } + + root.initInner2().setBaz("foo"); + EXPECT_EQ("foo", reader.getInner2().getBaz()); + + initTestMessage(root.getInner2().initInnerBound().initFoo()); + checkTestMessage(reader.getInner2().getInnerBound().getFoo()); + + initTestMessage(root.getInner2().initInnerUnbound().getFoo().initAs<TestAllTypes>()); + checkTestMessage(reader.getInner2().getInnerUnbound().getFoo().getAs<TestAllTypes>()); + + initTestMessage(root.initUnspecified().getFoo().initAs<TestAllTypes>()); + checkTestMessage(reader.getUnspecified().getFoo().getAs<TestAllTypes>()); + + initTestMessage(root.initWrapper().initValue().initFoo()); + checkTestMessage(reader.getWrapper().getValue().getFoo()); +} + +TEST(Encoding, GenericDefaults) { + test::TestUseGenerics::Reader reader; + + EXPECT_EQ(123, reader.getDefault().getFoo().getInt16Field()); + EXPECT_EQ(123, reader.getDefaultInner().getFoo().getInt16Field()); + EXPECT_EQ("text", reader.getDefaultInner().getBar()); + EXPECT_EQ(123, reader.getDefaultUser().getBasic().getFoo().getInt16Field()); + EXPECT_EQ("text", reader.getDefaultWrapper().getValue().getFoo()); + EXPECT_EQ(321, reader.getDefaultWrapper().getValue().getRev().getFoo().getInt16Field()); + EXPECT_EQ("text", reader.getDefaultWrapper2().getValue().getValue().getFoo()); + EXPECT_EQ(321, reader.getDefaultWrapper2().getValue() + .getValue().getRev().getFoo().getInt16Field()); +} + +TEST(Encoding, UnionInGenerics) { + MallocMessageBuilder message; + auto builder = message.initRoot<test::TestGenerics<>>(); + auto reader = builder.asReader(); + + //just call the methods to verify that generated code compiles + reader.which(); + builder.which(); + + reader.isUv(); + builder.isUv(); + reader.getUv(); + builder.getUv(); + builder.setUv(); + + builder.initUg(); + + reader.isUg(); + builder.isUg(); + reader.getUg(); + builder.getUg(); + builder.initUg(); +} + +TEST(Encoding, DefaultListBuilder) { + // At one point, this wouldn't compile. + + List<int>::Builder(nullptr); + List<TestAllTypes>::Builder(nullptr); + List<List<int>>::Builder(nullptr); + List<Text>::Builder(nullptr); +} + +} // namespace +} // namespace _ (private) +} // namespace capnp