| #include "flatbuffers/grpc.h" |
| #include "monster_test_generated.h" |
| #include "test_assert.h" |
| #include "test_builder.h" |
| |
| using MyGame::Example::Any_NONE; |
| using MyGame::Example::CreateStat; |
| using MyGame::Example::Vec3; |
| |
| bool verify(flatbuffers::grpc::Message<Monster> &msg, |
| const std::string &expected_name, Color expected_color) { |
| const Monster *monster = msg.GetRoot(); |
| const auto name = monster->name()->str(); |
| const auto color = monster->color(); |
| TEST_EQ(name, expected_name); |
| TEST_EQ(color, expected_color); |
| return (name == expected_name) && (color == expected_color); |
| } |
| |
| bool release_n_verify(flatbuffers::grpc::MessageBuilder &mbb, |
| const std::string &expected_name, Color expected_color) { |
| flatbuffers::grpc::Message<Monster> msg = mbb.ReleaseMessage<Monster>(); |
| return verify(msg, expected_name, expected_color); |
| } |
| |
| void builder_move_assign_after_releaseraw_test( |
| flatbuffers::grpc::MessageBuilder dst) { |
| auto root_offset1 = populate1(dst); |
| dst.Finish(root_offset1); |
| size_t size, offset; |
| grpc_slice slice; |
| dst.ReleaseRaw(size, offset, slice); |
| flatbuffers::FlatBufferBuilder src; |
| auto root_offset2 = populate2(src); |
| src.Finish(root_offset2); |
| auto src_size = src.GetSize(); |
| // Move into a released builder. |
| dst = std::move(src); |
| TEST_EQ(dst.GetSize(), src_size); |
| TEST_ASSERT(release_n_verify(dst, m2_name(), m2_color())); |
| TEST_EQ(src.GetSize(), 0); |
| grpc_slice_unref(slice); |
| } |
| |
| template<class SrcBuilder> |
| struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder, SrcBuilder> { |
| static void builder_reusable_after_release_message_test( |
| TestSelector selector) { |
| if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE)) { return; } |
| |
| flatbuffers::grpc::MessageBuilder mb; |
| std::vector<flatbuffers::grpc::Message<Monster>> buffers; |
| for (int i = 0; i < 5; ++i) { |
| auto root_offset1 = populate1(mb); |
| mb.Finish(root_offset1); |
| buffers.push_back(mb.ReleaseMessage<Monster>()); |
| TEST_ASSERT_FUNC(verify(buffers[i], m1_name(), m1_color())); |
| } |
| } |
| |
| static void builder_reusable_after_release_test(TestSelector selector) { |
| if (!selector.count(REUSABLE_AFTER_RELEASE)) { return; } |
| |
| // FIXME: Populate-Release loop fails assert(GRPC_SLICE_IS_EMPTY(slice_)) in |
| // SliceAllocator::allocate in the second iteration. |
| |
| flatbuffers::grpc::MessageBuilder mb; |
| std::vector<flatbuffers::DetachedBuffer> buffers; |
| for (int i = 0; i < 2; ++i) { |
| auto root_offset1 = populate1(mb); |
| mb.Finish(root_offset1); |
| buffers.push_back(mb.Release()); |
| TEST_ASSERT_FUNC(verify(buffers[i], m1_name(), m1_color())); |
| } |
| } |
| |
| static void builder_reusable_after_releaseraw_test(TestSelector selector) { |
| if (!selector.count(REUSABLE_AFTER_RELEASE_RAW)) { return; } |
| |
| flatbuffers::grpc::MessageBuilder mb; |
| for (int i = 0; i < 5; ++i) { |
| auto root_offset1 = populate1(mb); |
| mb.Finish(root_offset1); |
| size_t size, offset; |
| grpc_slice slice; |
| const uint8_t *buf = mb.ReleaseRaw(size, offset, slice); |
| TEST_ASSERT_FUNC(verify(buf, offset, m1_name(), m1_color())); |
| grpc_slice_unref(slice); |
| } |
| } |
| |
| static void builder_reusable_after_release_and_move_assign_test( |
| TestSelector selector) { |
| if (!selector.count(REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN)) { return; } |
| |
| // FIXME: Release-move_assign loop fails assert(p == |
| // GRPC_SLICE_START_PTR(slice_)) in DetachedBuffer destructor after all the |
| // iterations |
| |
| flatbuffers::grpc::MessageBuilder dst; |
| std::vector<flatbuffers::DetachedBuffer> buffers; |
| |
| for (int i = 0; i < 2; ++i) { |
| auto root_offset1 = populate1(dst); |
| dst.Finish(root_offset1); |
| buffers.push_back(dst.Release()); |
| TEST_ASSERT_FUNC(verify(buffers[i], m1_name(), m1_color())); |
| |
| // bring dst back to life. |
| SrcBuilder src; |
| dst = std::move(src); |
| TEST_EQ_FUNC(dst.GetSize(), 0); |
| TEST_EQ_FUNC(src.GetSize(), 0); |
| } |
| } |
| |
| static void builder_reusable_after_release_message_and_move_assign_test( |
| TestSelector selector) { |
| if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN)) { |
| return; |
| } |
| |
| flatbuffers::grpc::MessageBuilder dst; |
| std::vector<flatbuffers::grpc::Message<Monster>> buffers; |
| |
| for (int i = 0; i < 5; ++i) { |
| auto root_offset1 = populate1(dst); |
| dst.Finish(root_offset1); |
| buffers.push_back(dst.ReleaseMessage<Monster>()); |
| TEST_ASSERT_FUNC(verify(buffers[i], m1_name(), m1_color())); |
| |
| // bring dst back to life. |
| SrcBuilder src; |
| dst = std::move(src); |
| TEST_EQ_FUNC(dst.GetSize(), 0); |
| TEST_EQ_FUNC(src.GetSize(), 0); |
| } |
| } |
| |
| static void builder_reusable_after_releaseraw_and_move_assign_test( |
| TestSelector selector) { |
| if (!selector.count(REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN)) { return; } |
| |
| flatbuffers::grpc::MessageBuilder dst; |
| for (int i = 0; i < 5; ++i) { |
| auto root_offset1 = populate1(dst); |
| dst.Finish(root_offset1); |
| size_t size, offset; |
| grpc_slice slice = grpc_empty_slice(); |
| const uint8_t *buf = dst.ReleaseRaw(size, offset, slice); |
| TEST_ASSERT_FUNC(verify(buf, offset, m1_name(), m1_color())); |
| grpc_slice_unref(slice); |
| |
| SrcBuilder src; |
| dst = std::move(src); |
| TEST_EQ_FUNC(dst.GetSize(), 0); |
| TEST_EQ_FUNC(src.GetSize(), 0); |
| } |
| } |
| |
| static void run_tests(TestSelector selector) { |
| builder_reusable_after_release_test(selector); |
| builder_reusable_after_release_message_test(selector); |
| builder_reusable_after_releaseraw_test(selector); |
| builder_reusable_after_release_and_move_assign_test(selector); |
| builder_reusable_after_releaseraw_and_move_assign_test(selector); |
| builder_reusable_after_release_message_and_move_assign_test(selector); |
| } |
| }; |
| |
| void slice_allocator_tests() { |
| // move-construct no-delete test |
| { |
| size_t size = 2048; |
| flatbuffers::grpc::SliceAllocator sa1; |
| uint8_t *buf = sa1.allocate(size); |
| TEST_ASSERT_FUNC(buf != 0); |
| buf[0] = 100; |
| buf[size - 1] = 200; |
| flatbuffers::grpc::SliceAllocator sa2(std::move(sa1)); |
| // buf should not be deleted after move-construct |
| TEST_EQ_FUNC(buf[0], 100); |
| TEST_EQ_FUNC(buf[size - 1], 200); |
| // buf is freed here |
| } |
| |
| // move-assign test |
| { |
| flatbuffers::grpc::SliceAllocator sa1, sa2; |
| uint8_t *buf = sa1.allocate(2048); |
| sa1 = std::move(sa2); |
| // sa1 deletes previously allocated memory in move-assign. |
| // So buf is no longer usable here. |
| TEST_ASSERT_FUNC(buf != 0); |
| } |
| } |
| |
| /// This function does not populate exactly the first half of the table. But it |
| /// could. |
| void populate_first_half(MyGame::Example::MonsterBuilder &wrapper, |
| flatbuffers::Offset<flatbuffers::String> name_offset) { |
| wrapper.add_name(name_offset); |
| wrapper.add_color(m1_color()); |
| } |
| |
| /// This function does not populate exactly the second half of the table. But it |
| /// could. |
| void populate_second_half(MyGame::Example::MonsterBuilder &wrapper) { |
| wrapper.add_hp(77); |
| wrapper.add_mana(88); |
| Vec3 vec3; |
| wrapper.add_pos(&vec3); |
| } |
| |
| /// This function is a hack to update the FlatBufferBuilder reference (fbb_) in |
| /// the MonsterBuilder object. This function will break if fbb_ is not the first |
| /// member in MonsterBuilder. In that case, some offset must be added. This |
| /// function is used exclusively for testing correctness of move operations |
| /// between FlatBufferBuilders. If MonsterBuilder had a fbb_ pointer, this hack |
| /// would be unnecessary. That involves a code-generator change though. |
| void test_only_hack_update_fbb_reference( |
| MyGame::Example::MonsterBuilder &monsterBuilder, |
| flatbuffers::grpc::MessageBuilder &mb) { |
| *reinterpret_cast<flatbuffers::FlatBufferBuilder **>(&monsterBuilder) = &mb; |
| } |
| |
| /// This test validates correctness of move conversion of FlatBufferBuilder to a |
| /// MessageBuilder DURING a table construction. Half of the table is constructed |
| /// using FlatBufferBuilder and the other half of the table is constructed using |
| /// a MessageBuilder. |
| void builder_move_ctor_conversion_before_finish_half_n_half_table_test() { |
| for (size_t initial_size = 4; initial_size <= 2048; initial_size *= 2) { |
| flatbuffers::FlatBufferBuilder fbb(initial_size); |
| auto name_offset = fbb.CreateString(m1_name()); |
| MyGame::Example::MonsterBuilder monsterBuilder( |
| fbb); // starts a table in FlatBufferBuilder |
| populate_first_half(monsterBuilder, name_offset); |
| flatbuffers::grpc::MessageBuilder mb(std::move(fbb)); |
| test_only_hack_update_fbb_reference(monsterBuilder, mb); // hack |
| populate_second_half(monsterBuilder); |
| mb.Finish(monsterBuilder.Finish()); // ends the table in MessageBuilder |
| TEST_ASSERT_FUNC(release_n_verify(mb, m1_name(), m1_color())); |
| TEST_EQ_FUNC(fbb.GetSize(), 0); |
| } |
| } |
| |
| /// This test populates a COMPLETE inner table before move conversion and later |
| /// populates more members in the outer table. |
| void builder_move_ctor_conversion_before_finish_test() { |
| for (size_t initial_size = 1; initial_size <= 2048; initial_size += 1) { |
| flatbuffers::FlatBufferBuilder fbb(initial_size); |
| auto stat_offset = CreateStat(fbb, fbb.CreateString("SomeId"), 0, 0); |
| flatbuffers::grpc::MessageBuilder mb(std::move(fbb)); |
| auto monster_offset = |
| CreateMonster(mb, 0, 150, 100, mb.CreateString(m1_name()), 0, |
| m1_color(), Any_NONE, 0, 0, 0, 0, 0, 0, stat_offset); |
| mb.Finish(monster_offset); |
| { |
| auto mon = flatbuffers::GetRoot<Monster>(mb.GetBufferPointer()); |
| TEST_NOTNULL(mon); |
| TEST_NOTNULL(mon->name()); |
| TEST_EQ_STR(mon->name()->c_str(), m1_name().c_str()); |
| TEST_EQ(mon->color(), m1_color()); |
| } |
| TEST_EQ(1, MyGame::Example::Color_Red); |
| TEST_EQ(1, m1_color()); |
| TEST_ASSERT_FUNC(release_n_verify(mb, m1_name(), m1_color())); |
| TEST_EQ_FUNC(fbb.GetSize(), 0); |
| } |
| } |
| |
| /// This test validates correctness of move conversion of FlatBufferBuilder to a |
| /// MessageBuilder DURING a table construction. Half of the table is constructed |
| /// using FlatBufferBuilder and the other half of the table is constructed using |
| /// a MessageBuilder. |
| void builder_move_assign_conversion_before_finish_half_n_half_table_test() { |
| flatbuffers::FlatBufferBuilder fbb; |
| flatbuffers::grpc::MessageBuilder mb; |
| |
| for (int i = 0; i < 5; ++i) { |
| flatbuffers::FlatBufferBuilder fbb; |
| auto name_offset = fbb.CreateString(m1_name()); |
| MyGame::Example::MonsterBuilder monsterBuilder( |
| fbb); // starts a table in FlatBufferBuilder |
| populate_first_half(monsterBuilder, name_offset); |
| mb = std::move(fbb); |
| test_only_hack_update_fbb_reference(monsterBuilder, mb); // hack |
| populate_second_half(monsterBuilder); |
| mb.Finish(monsterBuilder.Finish()); // ends the table in MessageBuilder |
| TEST_ASSERT_FUNC(release_n_verify(mb, m1_name(), m1_color())); |
| TEST_EQ_FUNC(fbb.GetSize(), 0); |
| } |
| } |
| |
| /// This test populates a COMPLETE inner table before move conversion and later |
| /// populates more members in the outer table. |
| void builder_move_assign_conversion_before_finish_test() { |
| flatbuffers::FlatBufferBuilder fbb; |
| flatbuffers::grpc::MessageBuilder mb; |
| |
| for (int i = 0; i < 5; ++i) { |
| auto stat_offset = CreateStat(fbb, fbb.CreateString("SomeId"), 0, 0); |
| mb = std::move(fbb); |
| auto monster_offset = |
| CreateMonster(mb, 0, 150, 100, mb.CreateString(m1_name()), 0, |
| m1_color(), Any_NONE, 0, 0, 0, 0, 0, 0, stat_offset); |
| mb.Finish(monster_offset); |
| TEST_ASSERT_FUNC(release_n_verify(mb, m1_name(), m1_color())); |
| TEST_EQ_FUNC(fbb.GetSize(), 0); |
| } |
| } |
| |
| /// This test populates data, finishes the buffer, and does move conversion |
| /// after. |
| void builder_move_ctor_conversion_after_finish_test() { |
| flatbuffers::FlatBufferBuilder fbb; |
| fbb.Finish(populate1(fbb)); |
| flatbuffers::grpc::MessageBuilder mb(std::move(fbb)); |
| TEST_ASSERT_FUNC(release_n_verify(mb, m1_name(), m1_color())); |
| TEST_EQ_FUNC(fbb.GetSize(), 0); |
| } |
| |
| /// This test populates data, finishes the buffer, and does move conversion |
| /// after. |
| void builder_move_assign_conversion_after_finish_test() { |
| flatbuffers::FlatBufferBuilder fbb; |
| flatbuffers::grpc::MessageBuilder mb; |
| |
| for (int i = 0; i < 5; ++i) { |
| fbb.Finish(populate1(fbb)); |
| mb = std::move(fbb); |
| TEST_ASSERT_FUNC(release_n_verify(mb, m1_name(), m1_color())); |
| TEST_EQ_FUNC(fbb.GetSize(), 0); |
| } |
| } |
| |
| void message_builder_tests() { |
| using flatbuffers::FlatBufferBuilder; |
| using flatbuffers::grpc::MessageBuilder; |
| |
| slice_allocator_tests(); |
| |
| #ifndef __APPLE__ |
| builder_move_ctor_conversion_before_finish_half_n_half_table_test(); |
| builder_move_assign_conversion_before_finish_half_n_half_table_test(); |
| #endif // __APPLE__ |
| builder_move_ctor_conversion_before_finish_test(); |
| builder_move_assign_conversion_before_finish_test(); |
| |
| builder_move_ctor_conversion_after_finish_test(); |
| builder_move_assign_conversion_after_finish_test(); |
| |
| BuilderTests<MessageBuilder, MessageBuilder>::all_tests(); |
| BuilderTests<MessageBuilder, FlatBufferBuilder>::all_tests(); |
| |
| BuilderReuseTestSelector tests[6] = { |
| // REUSABLE_AFTER_RELEASE, // Assertion failed: |
| // (GRPC_SLICE_IS_EMPTY(slice_)) |
| // REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN, // Assertion failed: (p == |
| // GRPC_SLICE_START_PTR(slice_) |
| |
| REUSABLE_AFTER_RELEASE_RAW, REUSABLE_AFTER_RELEASE_MESSAGE, |
| REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN, |
| REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN |
| }; |
| |
| BuilderReuseTests<MessageBuilder, MessageBuilder>::run_tests( |
| TestSelector(tests, tests + 6)); |
| BuilderReuseTests<MessageBuilder, FlatBufferBuilder>::run_tests( |
| TestSelector(tests, tests + 6)); |
| } |