| // 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 "dynamic.h" |
| #include "message.h" |
| #include <kj/debug.h> |
| #include <kj/compat/gtest.h> |
| #include "test-util.h" |
| |
| namespace capnp { |
| namespace _ { // private |
| namespace { |
| |
| template <typename Element, typename T> |
| void checkList(T reader, std::initializer_list<ReaderFor<Element>> expected) { |
| auto list = reader.template as<DynamicList>(); |
| ASSERT_EQ(expected.size(), list.size()); |
| for (uint i = 0; i < expected.size(); i++) { |
| EXPECT_EQ(expected.begin()[i], list[i].template as<Element>()); |
| } |
| |
| auto typed = reader.template as<List<Element>>(); |
| ASSERT_EQ(expected.size(), typed.size()); |
| for (uint i = 0; i < expected.size(); i++) { |
| EXPECT_EQ(expected.begin()[i], typed[i]); |
| } |
| } |
| |
| TEST(DynamicApi, Build) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| initDynamicTestMessage(root); |
| checkTestMessage(root.asReader().as<TestAllTypes>()); |
| |
| checkDynamicTestMessage(root.asReader()); |
| checkDynamicTestMessage(root); |
| } |
| |
| TEST(DynamicApi, Read) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<TestAllTypes>(); |
| |
| initTestMessage(root); |
| |
| checkDynamicTestMessage(toDynamic(root.asReader())); |
| checkDynamicTestMessage(toDynamic(root).asReader()); |
| checkDynamicTestMessage(toDynamic(root)); |
| } |
| |
| TEST(DynamicApi, 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)); |
| auto root = reader.getRoot<DynamicStruct>(Schema::from<TestDefaults>()); |
| checkDynamicTestMessage(root); |
| } |
| |
| TEST(DynamicApi, DefaultsBuilder) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestDefaults>()); |
| |
| checkTestMessage(root.asReader().as<TestDefaults>()); |
| checkDynamicTestMessage(root.asReader()); |
| |
| // This will initialize the whole message, replacing null pointers with copies of defaults. |
| checkDynamicTestMessage(root); |
| |
| // Check again now that the message is initialized. |
| checkTestMessage(root.asReader().as<TestDefaults>()); |
| checkDynamicTestMessage(root.asReader()); |
| checkDynamicTestMessage(root); |
| } |
| |
| TEST(DynamicApi, Zero) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| checkDynamicTestMessageAllZero(root.asReader()); |
| checkTestMessageAllZero(root.asReader().as<TestAllTypes>()); |
| checkDynamicTestMessageAllZero(root); |
| checkTestMessageAllZero(root.asReader().as<TestAllTypes>()); |
| } |
| |
| TEST(DynamicApi, ListListsBuild) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestListDefaults>()); |
| |
| initDynamicTestLists(root); |
| checkTestMessage(root.asReader().as<TestListDefaults>()); |
| |
| checkDynamicTestLists(root.asReader()); |
| checkDynamicTestLists(root); |
| } |
| |
| TEST(DynamicApi, ListListsRead) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<TestListDefaults>(); |
| |
| initTestMessage(root); |
| |
| checkDynamicTestLists(toDynamic(root.asReader())); |
| checkDynamicTestLists(toDynamic(root).asReader()); |
| checkDynamicTestLists(toDynamic(root)); |
| } |
| |
| TEST(DynamicApi, AnyPointers) { |
| MallocMessageBuilder builder; |
| auto root = builder.getRoot<test::TestAnyPointer>(); |
| |
| initDynamicTestMessage( |
| root.getAnyPointerField().initAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| checkTestMessage(root.asReader().getAnyPointerField().getAs<TestAllTypes>()); |
| |
| checkDynamicTestMessage( |
| root.asReader().getAnyPointerField().getAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| checkDynamicTestMessage( |
| root.getAnyPointerField().getAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| |
| { |
| { |
| auto list = root.getAnyPointerField().initAs<DynamicList>(Schema::from<List<uint32_t>>(), 4); |
| list.set(0, 123); |
| list.set(1, 456); |
| list.set(2, 789); |
| list.set(3, 123456789); |
| } |
| |
| { |
| auto list = root.asReader().getAnyPointerField().getAs<List<uint32_t>>(); |
| ASSERT_EQ(4u, list.size()); |
| EXPECT_EQ(123u, list[0]); |
| EXPECT_EQ(456u, list[1]); |
| EXPECT_EQ(789u, list[2]); |
| EXPECT_EQ(123456789u, list[3]); |
| } |
| |
| checkList<uint32_t>(root.asReader().getAnyPointerField().getAs<DynamicList>( |
| Schema::from<List<uint32_t>>()), {123u, 456u, 789u, 123456789u}); |
| checkList<uint32_t>(root.getAnyPointerField().getAs<DynamicList>( |
| Schema::from<List<uint32_t>>()), {123u, 456u, 789u, 123456789u}); |
| } |
| |
| // Setting an AnyPointer to various types should work. |
| toDynamic(root).set("anyPointerField", capnp::Text::Reader("foo")); |
| EXPECT_EQ("foo", root.getAnyPointerField().getAs<Text>()); |
| |
| { |
| auto orphan = builder.getOrphanage().newOrphan<TestAllTypes>(); |
| initTestMessage(orphan.get()); |
| toDynamic(root).set("anyPointerField", orphan.getReader()); |
| checkTestMessage(root.getAnyPointerField().getAs<TestAllTypes>()); |
| |
| toDynamic(root).adopt("anyPointerField", kj::mv(orphan)); |
| checkTestMessage(root.getAnyPointerField().getAs<TestAllTypes>()); |
| } |
| |
| { |
| auto lorphan = builder.getOrphanage().newOrphan<List<uint32_t>>(3); |
| lorphan.get().set(0, 12); |
| lorphan.get().set(1, 34); |
| lorphan.get().set(2, 56); |
| toDynamic(root).set("anyPointerField", lorphan.getReader()); |
| auto l = root.getAnyPointerField().getAs<List<uint32_t>>(); |
| ASSERT_EQ(3, l.size()); |
| EXPECT_EQ(12, l[0]); |
| EXPECT_EQ(34, l[1]); |
| EXPECT_EQ(56, l[2]); |
| } |
| |
| // Just compile this one. |
| toDynamic(root).set("anyPointerField", Capability::Client(nullptr)); |
| root.getAnyPointerField().getAs<Capability>(); |
| } |
| |
| TEST(DynamicApi, DynamicAnyPointers) { |
| MallocMessageBuilder builder; |
| auto root = builder.getRoot<DynamicStruct>(Schema::from<test::TestAnyPointer>()); |
| |
| initDynamicTestMessage( |
| root.get("anyPointerField").as<AnyPointer>() |
| .initAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| checkTestMessage( |
| root.asReader().as<test::TestAnyPointer>().getAnyPointerField().getAs<TestAllTypes>()); |
| |
| checkDynamicTestMessage( |
| root.asReader().get("anyPointerField").as<AnyPointer>() |
| .getAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| checkDynamicTestMessage( |
| root.asReader().get("anyPointerField").as<AnyPointer>() |
| .getAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| checkDynamicTestMessage( |
| root.get("anyPointerField").as<AnyPointer>().asReader() |
| .getAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| checkDynamicTestMessage( |
| root.get("anyPointerField").as<AnyPointer>() |
| .getAs<DynamicStruct>(Schema::from<TestAllTypes>())); |
| |
| { |
| { |
| auto list = root.init("anyPointerField").as<AnyPointer>() |
| .initAs<DynamicList>(Schema::from<List<uint32_t>>(), 4); |
| list.set(0, 123); |
| list.set(1, 456); |
| list.set(2, 789); |
| list.set(3, 123456789); |
| } |
| |
| { |
| auto list = root.asReader().as<test::TestAnyPointer>() |
| .getAnyPointerField().getAs<List<uint32_t>>(); |
| ASSERT_EQ(4u, list.size()); |
| EXPECT_EQ(123u, list[0]); |
| EXPECT_EQ(456u, list[1]); |
| EXPECT_EQ(789u, list[2]); |
| EXPECT_EQ(123456789u, list[3]); |
| } |
| |
| checkList<uint32_t>( |
| root.asReader().get("anyPointerField").as<AnyPointer>() |
| .getAs<DynamicList>(Schema::from<List<uint32_t>>()), |
| {123u, 456u, 789u, 123456789u}); |
| checkList<uint32_t>( |
| root.asReader().get("anyPointerField").as<AnyPointer>() |
| .getAs<DynamicList>(Schema::from<List<uint32_t>>()), |
| {123u, 456u, 789u, 123456789u}); |
| checkList<uint32_t>( |
| root.get("anyPointerField").as<AnyPointer>().asReader() |
| .getAs<DynamicList>(Schema::from<List<uint32_t>>()), |
| {123u, 456u, 789u, 123456789u}); |
| checkList<uint32_t>( |
| root.get("anyPointerField").as<AnyPointer>() |
| .getAs<DynamicList>(Schema::from<List<uint32_t>>()), |
| {123u, 456u, 789u, 123456789u}); |
| } |
| } |
| |
| TEST(DynamicApi, DynamicAnyStructs) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| root.as<AnyStruct>().as<TestAllTypes>().setInt8Field(123); |
| EXPECT_EQ(root.get("int8Field").as<int8_t>(), 123); |
| EXPECT_EQ(root.asReader().as<AnyStruct>().as<TestAllTypes>().getInt8Field(), 123); |
| } |
| |
| #define EXPECT_MAYBE_EQ(name, exp, expected, actual) \ |
| KJ_IF_MAYBE(name, exp) { \ |
| EXPECT_EQ(expected, actual); \ |
| } else { \ |
| KJ_FAIL_EXPECT("Maybe was empty."); \ |
| } |
| |
| TEST(DynamicApi, UnionsRead) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<TestUnion>(); |
| |
| root.getUnion0().setU0f1s32(1234567); |
| root.getUnion1().setU1f1sp("foo"); |
| root.getUnion2().setU2f0s1(true); |
| root.getUnion3().setU3f0s64(1234567890123456789ll); |
| |
| { |
| auto dynamic = toDynamic(root.asReader()); |
| { |
| auto u = dynamic.get("union0").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u0f1s32", w->getProto().getName()); |
| EXPECT_EQ(1234567, u.get("u0f1s32").as<int32_t>()); |
| } |
| { |
| auto u = dynamic.get("union1").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u1f1sp", w->getProto().getName()); |
| EXPECT_EQ("foo", u.get("u1f1sp").as<Text>()); |
| } |
| { |
| auto u = dynamic.get("union2").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u2f0s1", w->getProto().getName()); |
| EXPECT_TRUE(u.get("u2f0s1").as<bool>()); |
| } |
| { |
| auto u = dynamic.get("union3").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u3f0s64", w->getProto().getName()); |
| EXPECT_EQ(1234567890123456789ll, u.get("u3f0s64").as<int64_t>()); |
| } |
| } |
| |
| { |
| // Again as a builder. |
| auto dynamic = toDynamic(root); |
| { |
| auto u = dynamic.get("union0").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u0f1s32", w->getProto().getName()); |
| EXPECT_EQ(1234567, u.get("u0f1s32").as<int32_t>()); |
| } |
| { |
| auto u = dynamic.get("union1").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u1f1sp", w->getProto().getName()); |
| EXPECT_EQ("foo", u.get("u1f1sp").as<Text>()); |
| } |
| { |
| auto u = dynamic.get("union2").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u2f0s1", w->getProto().getName()); |
| EXPECT_TRUE(u.get("u2f0s1").as<bool>()); |
| } |
| { |
| auto u = dynamic.get("union3").as<DynamicStruct>(); |
| EXPECT_MAYBE_EQ(w, u.which(), "u3f0s64", w->getProto().getName()); |
| EXPECT_EQ(1234567890123456789ll, u.get("u3f0s64").as<int64_t>()); |
| } |
| } |
| } |
| |
| TEST(DynamicApi, UnionsWrite) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestUnion>()); |
| |
| root.get("union0").as<DynamicStruct>().set("u0f1s32", 1234567); |
| root.get("union1").as<DynamicStruct>().set("u1f1sp", "foo"); |
| root.get("union2").as<DynamicStruct>().set("u2f0s1", true); |
| root.get("union3").as<DynamicStruct>().set("u3f0s64", 1234567890123456789ll); |
| |
| auto reader = root.asReader().as<TestUnion>(); |
| ASSERT_EQ(TestUnion::Union0::U0F1S32, reader.getUnion0().which()); |
| EXPECT_EQ(1234567, reader.getUnion0().getU0f1s32()); |
| |
| ASSERT_EQ(TestUnion::Union1::U1F1SP, reader.getUnion1().which()); |
| EXPECT_EQ("foo", reader.getUnion1().getU1f1sp()); |
| |
| ASSERT_EQ(TestUnion::Union2::U2F0S1, reader.getUnion2().which()); |
| EXPECT_TRUE(reader.getUnion2().getU2f0s1()); |
| |
| ASSERT_EQ(TestUnion::Union3::U3F0S64, reader.getUnion3().which()); |
| EXPECT_EQ(1234567890123456789ll, reader.getUnion3().getU3f0s64()); |
| |
| // Can't access union members by name from the root. |
| EXPECT_ANY_THROW(root.get("u0f1s32")); |
| EXPECT_ANY_THROW(root.set("u0f1s32", 1234567)); |
| } |
| |
| TEST(DynamicApi, UnnamedUnion) { |
| MallocMessageBuilder builder; |
| StructSchema schema = Schema::from<test::TestUnnamedUnion>(); |
| auto root = builder.initRoot<DynamicStruct>(schema); |
| |
| EXPECT_EQ(schema.getFieldByName("foo"), KJ_ASSERT_NONNULL(root.which())); |
| |
| root.set("bar", 321); |
| EXPECT_EQ(schema.getFieldByName("bar"), KJ_ASSERT_NONNULL(root.which())); |
| EXPECT_EQ(321u, root.get("bar").as<uint>()); |
| EXPECT_EQ(321u, root.asReader().get("bar").as<uint>()); |
| EXPECT_ANY_THROW(root.get("foo")); |
| EXPECT_ANY_THROW(root.asReader().get("foo")); |
| |
| root.set("foo", 123); |
| EXPECT_EQ(schema.getFieldByName("foo"), KJ_ASSERT_NONNULL(root.which())); |
| EXPECT_EQ(123u, root.get("foo").as<uint>()); |
| EXPECT_EQ(123u, root.asReader().get("foo").as<uint>()); |
| EXPECT_ANY_THROW(root.get("bar")); |
| EXPECT_ANY_THROW(root.asReader().get("bar")); |
| |
| root.set("bar", 321); |
| EXPECT_EQ(schema.getFieldByName("bar"), KJ_ASSERT_NONNULL(root.which())); |
| EXPECT_EQ(321u, root.get("bar").as<uint>()); |
| EXPECT_EQ(321u, root.asReader().get("bar").as<uint>()); |
| EXPECT_ANY_THROW(root.get("foo")); |
| EXPECT_ANY_THROW(root.asReader().get("foo")); |
| |
| root.set("foo", 123); |
| EXPECT_EQ(schema.getFieldByName("foo"), KJ_ASSERT_NONNULL(root.which())); |
| EXPECT_EQ(123u, root.get("foo").as<uint>()); |
| EXPECT_EQ(123u, root.asReader().get("foo").as<uint>()); |
| EXPECT_ANY_THROW(root.get("bar")); |
| EXPECT_ANY_THROW(root.asReader().get("bar")); |
| } |
| |
| TEST(DynamicApi, ConversionFailures) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| root.set("int8Field", 123); |
| EXPECT_NONFATAL_FAILURE(root.set("int8Field", 1234)); |
| |
| root.set("uInt32Field", 1); |
| EXPECT_NONFATAL_FAILURE(root.set("uInt32Field", -1)); |
| |
| root.set("int16Field", 5); |
| EXPECT_NONFATAL_FAILURE(root.set("int16Field", 0.5)); |
| |
| root.set("boolField", true); |
| EXPECT_NONFATAL_FAILURE(root.set("boolField", 1)); |
| } |
| |
| TEST(DynamicApi, LateUnion) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<test::TestLateUnion>()); |
| |
| root.get("theUnion").as<DynamicStruct>().set("qux", "hello"); |
| EXPECT_EQ("hello", root.as<test::TestLateUnion>().getTheUnion().getQux()); |
| } |
| |
| TEST(DynamicApi, Has) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestDefaults>()); |
| |
| // Primitive fields are always present even if set to default. |
| EXPECT_TRUE(root.has("int32Field")); |
| EXPECT_FALSE(root.has("int32Field", HasMode::NON_DEFAULT)); |
| root.set("int32Field", 123); |
| EXPECT_TRUE(root.has("int32Field")); |
| EXPECT_TRUE(root.has("int32Field", HasMode::NON_DEFAULT)); |
| root.set("int32Field", -12345678); |
| EXPECT_TRUE(root.has("int32Field")); |
| EXPECT_FALSE(root.has("int32Field", HasMode::NON_DEFAULT)); |
| |
| // Pointers are absent until initialized. |
| EXPECT_FALSE(root.has("structField")); |
| EXPECT_FALSE(root.has("structField", HasMode::NON_DEFAULT)); |
| root.init("structField"); |
| EXPECT_TRUE(root.has("structField")); |
| EXPECT_TRUE(root.has("structField", HasMode::NON_DEFAULT)); |
| } |
| |
| TEST(DynamicApi, HasWhenEmpty) { |
| 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)); |
| auto root = reader.getRoot<DynamicStruct>(Schema::from<TestDefaults>()); |
| |
| EXPECT_TRUE(root.has("voidField")); |
| EXPECT_TRUE(root.has("int32Field")); |
| EXPECT_FALSE(root.has("structField")); |
| EXPECT_FALSE(root.has("int32List")); |
| |
| EXPECT_FALSE(root.has("voidField", HasMode::NON_DEFAULT)); |
| EXPECT_FALSE(root.has("int32Field", HasMode::NON_DEFAULT)); |
| EXPECT_FALSE(root.has("structField", HasMode::NON_DEFAULT)); |
| EXPECT_FALSE(root.has("int32List", HasMode::NON_DEFAULT)); |
| } |
| |
| TEST(DynamicApi, SetEnumFromNative) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| root.set("enumField", TestEnum::BAZ); |
| root.set("enumList", {TestEnum::BAR, TestEnum::FOO}); |
| EXPECT_EQ(TestEnum::BAZ, root.get("enumField").as<TestEnum>()); |
| checkList<TestEnum>(root.get("enumList"), {TestEnum::BAR, TestEnum::FOO}); |
| } |
| |
| TEST(DynamicApi, SetDataFromText) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| root.set("dataField", "foo"); |
| EXPECT_EQ(data("foo"), root.get("dataField").as<Data>()); |
| } |
| |
| TEST(DynamicApi, BuilderAssign) { |
| MallocMessageBuilder builder; |
| auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); |
| |
| // Declare upfront, assign later. |
| // Note that the Python implementation requires defaulted constructors. Do not delete them! |
| DynamicValue::Builder value; |
| DynamicStruct::Builder structValue; |
| DynamicList::Builder listValue; |
| |
| value = root.get("structField"); |
| structValue = value.as<DynamicStruct>(); |
| structValue.set("int32Field", 123); |
| |
| value = root.init("int32List", 1); |
| listValue = value.as<DynamicList>(); |
| listValue.set(0, 123); |
| } |
| |
| } // namespace |
| } // namespace _ (private) |
| } // namespace capnp |