diff options
-rw-r--r-- | grpc/tests/grpctest.cpp | 81 | ||||
-rw-r--r-- | grpc/tests/message_builder_test.cpp | 246 | ||||
-rw-r--r-- | include/flatbuffers/flatbuffers.h | 15 | ||||
-rw-r--r-- | include/flatbuffers/grpc.h | 26 | ||||
-rw-r--r-- | tests/test_builder.cpp | 10 | ||||
-rw-r--r-- | tests/test_builder.h | 283 |
6 files changed, 441 insertions, 220 deletions
diff --git a/grpc/tests/grpctest.cpp b/grpc/tests/grpctest.cpp index 50b17cc0..7e5c6e6c 100644 --- a/grpc/tests/grpctest.cpp +++ b/grpc/tests/grpctest.cpp @@ -23,6 +23,9 @@ #include "test_assert.h" using namespace MyGame::Example; +using flatbuffers::grpc::MessageBuilder; +using flatbuffers::FlatBufferBuilder; + void message_builder_tests(); // The callback implementation of our server, that derives from the generated @@ -46,9 +49,9 @@ class ServiceImpl final : public MyGame::Example::MonsterStorage::Service { const flatbuffers::grpc::Message<Stat> *request, ::grpc::ServerWriter<flatbuffers::grpc::Message<Monster>> *writer) override { - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 5; i++) { fbb_.Clear(); - // Create 10 monsters for resposne. + // Create 5 monsters for resposne. auto monster_offset = CreateMonster(fbb_, 0, 0, 0, fbb_.CreateString(request->GetRoot()->id()->str() + @@ -94,6 +97,45 @@ void RunServer() { server_instance->Wait(); } +template <class Builder> +void StoreRPC(MonsterStorage::Stub *stub) { + Builder fbb; + grpc::ClientContext context; + // Build a request with the name set. + auto monster_offset = CreateMonster(fbb, 0, 0, 0, fbb.CreateString("Fred")); + MessageBuilder mb(std::move(fbb)); + mb.Finish(monster_offset); + auto request = mb.ReleaseMessage<Monster>(); + flatbuffers::grpc::Message<Stat> response; + + // The actual RPC. + auto status = stub->Store(&context, request, &response); + + if (status.ok()) { + auto resp = response.GetRoot()->id(); + std::cout << "RPC response: " << resp->str() << std::endl; + } else { + std::cout << "RPC failed" << std::endl; + } +} + +template <class Builder> +void RetrieveRPC(MonsterStorage::Stub *stub) { + Builder fbb; + grpc::ClientContext context; + fbb.Clear(); + auto stat_offset = CreateStat(fbb, fbb.CreateString("Fred")); + fbb.Finish(stat_offset); + auto request = MessageBuilder(std::move(fbb)).ReleaseMessage<Stat>(); + + flatbuffers::grpc::Message<Monster> response; + auto stream = stub->Retrieve(&context, request); + while (stream->Read(&response)) { + auto resp = response.GetRoot()->name(); + std::cout << "RPC Streaming response: " << resp->str() << std::endl; + } +} + int grpc_server_test() { // Launch server. std::thread server_thread(RunServer); @@ -107,39 +149,12 @@ int grpc_server_test() { grpc::InsecureChannelCredentials()); auto stub = MyGame::Example::MonsterStorage::NewStub(channel); - flatbuffers::grpc::MessageBuilder fbb; - { - grpc::ClientContext context; - // Build a request with the name set. - auto monster_offset = CreateMonster(fbb, 0, 0, 0, fbb.CreateString("Fred")); - fbb.Finish(monster_offset); - auto request = fbb.ReleaseMessage<Monster>(); - flatbuffers::grpc::Message<Stat> response; + StoreRPC<MessageBuilder>(stub.get()); + StoreRPC<FlatBufferBuilder>(stub.get()); - // The actual RPC. - auto status = stub->Store(&context, request, &response); + RetrieveRPC<MessageBuilder>(stub.get()); + RetrieveRPC<FlatBufferBuilder>(stub.get()); - if (status.ok()) { - auto resp = response.GetRoot()->id(); - std::cout << "RPC response: " << resp->str() << std::endl; - } else { - std::cout << "RPC failed" << std::endl; - } - } - { - grpc::ClientContext context; - fbb.Clear(); - auto stat_offset = CreateStat(fbb, fbb.CreateString("Fred")); - fbb.Finish(stat_offset); - auto request = fbb.ReleaseMessage<Stat>(); - - flatbuffers::grpc::Message<Monster> response; - auto stream = stub->Retrieve(&context, request); - while (stream->Read(&response)) { - auto resp = response.GetRoot()->name(); - std::cout << "RPC Streaming response: " << resp->str() << std::endl; - } - } #if !FLATBUFFERS_GRPC_DISABLE_AUTO_VERIFICATION { diff --git a/grpc/tests/message_builder_test.cpp b/grpc/tests/message_builder_test.cpp index 25f04da8..d5ed8d97 100644 --- a/grpc/tests/message_builder_test.cpp +++ b/grpc/tests/message_builder_test.cpp @@ -14,19 +14,37 @@ bool release_n_verify(flatbuffers::grpc::MessageBuilder &mbb, const std::string return (monster->name()->str() == expected_name) && (monster->color() == color); } -template <> -struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> { +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 b1; + flatbuffers::grpc::MessageBuilder mb; std::vector<flatbuffers::grpc::Message<Monster>> buffers; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - buffers.push_back(b1.ReleaseMessage<Monster>()); + 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)); } } @@ -36,14 +54,15 @@ struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> { return; } - // FIXME: Populate-Release loop fails assert(GRPC_SLICE_IS_EMPTY(slice_)). + // FIXME: Populate-Release loop fails assert(GRPC_SLICE_IS_EMPTY(slice_)) in SliceAllocator::allocate + // in the second iteration. - flatbuffers::grpc::MessageBuilder b1; + flatbuffers::grpc::MessageBuilder mb; std::vector<flatbuffers::DetachedBuffer> buffers; - for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - buffers.push_back(b1.Release()); + 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)); } } @@ -53,13 +72,13 @@ struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> { return; } - flatbuffers::grpc::MessageBuilder b1; + flatbuffers::grpc::MessageBuilder mb; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); + auto root_offset1 = populate1(mb); + mb.Finish(root_offset1); size_t size, offset; grpc_slice slice; - const uint8_t *buf = b1.ReleaseRaw(size, offset, slice); + const uint8_t *buf = mb.ReleaseRaw(size, offset, slice); TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color)); grpc_slice_unref(slice); } @@ -70,22 +89,23 @@ struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> { return; } - // FIXME: Release-move_assign loop fails assert(p == GRPC_SLICE_START_PTR(slice_)). + // FIXME: Release-move_assign loop fails assert(p == GRPC_SLICE_START_PTR(slice_)) + // in DetachedBuffer destructor after all the iterations - flatbuffers::grpc::MessageBuilder b1; + flatbuffers::grpc::MessageBuilder dst; std::vector<flatbuffers::DetachedBuffer> buffers; - for (int i = 0; i < 1; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - buffers.push_back(b1.Release()); + 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 b1 back to life. - flatbuffers::grpc::MessageBuilder b2; - b1 = std::move(b2); - TEST_EQ_FUNC(b1.GetSize(), 0); - TEST_EQ_FUNC(b2.GetSize(), 0); + // bring dst back to life. + SrcBuilder src; + dst = std::move(src); + TEST_EQ_FUNC(dst.GetSize(), 0); + TEST_EQ_FUNC(src.GetSize(), 0); } } @@ -94,20 +114,20 @@ struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> { return; } - flatbuffers::grpc::MessageBuilder b1; + flatbuffers::grpc::MessageBuilder dst; std::vector<flatbuffers::grpc::Message<Monster>> buffers; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - buffers.push_back(b1.ReleaseMessage<Monster>()); + 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 b1 back to life. - flatbuffers::grpc::MessageBuilder b2; - b1 = std::move(b2); - TEST_EQ_FUNC(b1.GetSize(), 0); - TEST_EQ_FUNC(b2.GetSize(), 0); + // bring dst back to life. + SrcBuilder src; + dst = std::move(src); + TEST_EQ_FUNC(dst.GetSize(), 0); + TEST_EQ_FUNC(src.GetSize(), 0); } } @@ -116,20 +136,20 @@ struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> { return; } - flatbuffers::grpc::MessageBuilder b1; + flatbuffers::grpc::MessageBuilder dst; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); + auto root_offset1 = populate1(dst); + dst.Finish(root_offset1); size_t size, offset; grpc_slice slice = grpc_empty_slice(); - const uint8_t *buf = b1.ReleaseRaw(size, offset, slice); + const uint8_t *buf = dst.ReleaseRaw(size, offset, slice); TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color)); grpc_slice_unref(slice); - flatbuffers::grpc::MessageBuilder b2; - b1 = std::move(b2); - TEST_EQ_FUNC(b1.GetSize(), 0); - TEST_EQ_FUNC(b2.GetSize(), 0); + SrcBuilder src; + dst = std::move(src); + TEST_EQ_FUNC(dst.GetSize(), 0); + TEST_EQ_FUNC(src.GetSize(), 0); } } @@ -153,7 +173,7 @@ void slice_allocator_tests() { buf[0] = 100; buf[size-1] = 200; flatbuffers::grpc::SliceAllocator sa2(std::move(sa1)); - // buf should be deleted after move-construct + // 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 @@ -170,13 +190,140 @@ void slice_allocator_tests() { } } +/// 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 = 4 ; initial_size <= 2048; initial_size *= 2) { + 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); + 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::grpc::MessageBuilder; + using flatbuffers::FlatBufferBuilder; + slice_allocator_tests(); - BuilderTests<flatbuffers::grpc::MessageBuilder>::all_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, // 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, @@ -184,5 +331,6 @@ void message_builder_tests() { REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN }; - BuilderReuseTests<flatbuffers::grpc::MessageBuilder>::run_tests(TestSelector(tests, tests+6)); + BuilderReuseTests<MessageBuilder, MessageBuilder>::run_tests(TestSelector(tests, tests+6)); + BuilderReuseTests<MessageBuilder, FlatBufferBuilder>::run_tests(TestSelector(tests, tests+6)); } diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 5c02e1f1..b6b1b1cb 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -425,6 +425,10 @@ class DefaultAllocator : public Allocator { void deallocate(uint8_t *p, size_t) FLATBUFFERS_OVERRIDE { delete[] p; } + + static void dealloc(void *p, size_t) { + delete[] static_cast<uint8_t *>(p); + } }; // These functions allow for a null allocator to mean use the default allocator, @@ -549,7 +553,7 @@ class DetachedBuffer { #endif // !defined(FLATBUFFERS_CPP98_STL) // clang-format on - protected: +protected: Allocator *allocator_; bool own_allocator_; uint8_t *buf_; @@ -986,7 +990,7 @@ class FlatBufferBuilder { /// `FlatBuffer` starts. /// @return A raw pointer to the start of the memory block containing /// the serialized `FlatBuffer`. - /// @remark If the allocator is owned, it gets deleted during this call. + /// @remark If the allocator is owned, it gets deleted when the destructor is called.. uint8_t *ReleaseRaw(size_t &size, size_t &offset) { Finished(); return buf_.release_raw(size, offset); @@ -1759,7 +1763,12 @@ class FlatBufferBuilder { Finish(root.o, file_identifier, true); } - protected: + void SwapBufAllocator(FlatBufferBuilder &other) { + buf_.swap_allocator(other.buf_); + } + +protected: + // You shouldn't really be copying instances of this class. FlatBufferBuilder(const FlatBufferBuilder &); FlatBufferBuilder &operator=(const FlatBufferBuilder &); diff --git a/include/flatbuffers/grpc.h b/include/flatbuffers/grpc.h index 00553ef6..a75b67c7 100644 --- a/include/flatbuffers/grpc.h +++ b/include/flatbuffers/grpc.h @@ -164,7 +164,7 @@ class MessageBuilder : private detail::SliceAllocatorMember, public FlatBufferBuilder { public: explicit MessageBuilder(uoffset_t initial_size = 1024) - : FlatBufferBuilder(initial_size, &slice_allocator_, false) {} + : FlatBufferBuilder(initial_size, &slice_allocator_, false) {} MessageBuilder(const MessageBuilder &other) = delete; MessageBuilder &operator=(const MessageBuilder &other) = delete; @@ -175,6 +175,30 @@ class MessageBuilder : private detail::SliceAllocatorMember, Swap(other); } + /// Create a MessageBuilder from a FlatBufferBuilder. + explicit MessageBuilder(FlatBufferBuilder &&src, void (*dealloc)(void*, size_t) = &DefaultAllocator::dealloc) + : FlatBufferBuilder(1024, &slice_allocator_, false) { + src.Swap(*this); + src.SwapBufAllocator(*this); + if (buf_.capacity()) { + uint8_t *buf = buf_.scratch_data(); // pointer to memory + size_t capacity = buf_.capacity(); // size of memory + slice_allocator_.slice_ = grpc_slice_new_with_len(buf, capacity, dealloc); + } + else { + slice_allocator_.slice_ = grpc_empty_slice(); + } + } + + /// Move-assign a FlatBufferBuilder to a MessageBuilder. + /// Only FlatBufferBuilder with default allocator (basically, nullptr) is supported. + MessageBuilder &operator=(FlatBufferBuilder &&src) { + // Move construct a temporary and swap + MessageBuilder temp(std::move(src)); + Swap(temp); + return *this; + } + MessageBuilder &operator=(MessageBuilder &&other) { // Move construct a temporary and swap MessageBuilder temp(std::move(other)); diff --git a/tests/test_builder.cpp b/tests/test_builder.cpp index 2c1e823c..8c070c11 100644 --- a/tests/test_builder.cpp +++ b/tests/test_builder.cpp @@ -129,7 +129,9 @@ bool release_n_verify(flatbuffers::FlatBufferBuilder &fbb, const std::string &ex } void FlatBufferBuilderTest() { - BuilderTests<flatbuffers::FlatBufferBuilder>::all_tests(); + using flatbuffers::FlatBufferBuilder; + + BuilderTests<FlatBufferBuilder>::all_tests(); BuilderTests<TestHeapBuilder>::all_tests(); BuilderTests<GrpcLikeMessageBuilder>::all_tests(); @@ -140,7 +142,7 @@ void FlatBufferBuilderTest() { REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN }; - BuilderReuseTests<flatbuffers::FlatBufferBuilder>::run_tests(TestSelector(tests, tests+4)); - BuilderReuseTests<TestHeapBuilder>::run_tests(TestSelector(tests, tests+4)); - BuilderReuseTests<GrpcLikeMessageBuilder>::run_tests(TestSelector(tests, tests+4)); + BuilderReuseTests<FlatBufferBuilder, FlatBufferBuilder>::run_tests(TestSelector(tests, tests+4)); + BuilderReuseTests<TestHeapBuilder, TestHeapBuilder>::run_tests(TestSelector(tests, tests+4)); + BuilderReuseTests<GrpcLikeMessageBuilder, GrpcLikeMessageBuilder>::run_tests(TestSelector(tests, tests+4)); } diff --git a/tests/test_builder.h b/tests/test_builder.h index 76222a72..1ea17b49 100644 --- a/tests/test_builder.h +++ b/tests/test_builder.h @@ -2,6 +2,7 @@ #define TEST_BUILDER_H #include <set> +#include <type_traits> #include "monster_test_generated.h" #include "flatbuffers/flatbuffers.h" #include "test_assert.h" @@ -13,6 +14,16 @@ class MessageBuilder; } } +template <class T, class U> +struct is_same { + static const bool value = false; +}; + +template <class T> +struct is_same<T, T> { + static const bool value = true; +}; + extern const std::string m1_name; extern const Color m1_color; extern const std::string m2_name; @@ -32,145 +43,157 @@ bool verify(const uint8_t *buf, size_t offset, const std::string &expected_name, bool release_n_verify(flatbuffers::FlatBufferBuilder &fbb, const std::string &expected_name, Color color); bool release_n_verify(flatbuffers::grpc::MessageBuilder &mbb, const std::string &expected_name, Color color); +// clang-format off +#if !defined(FLATBUFFERS_CPP98_STL) +// clang-format on template <class Builder> +void builder_move_assign_after_releaseraw_test(Builder b1) { + auto root_offset1 = populate1(b1); + b1.Finish(root_offset1); + size_t size, offset; + b1.ReleaseRaw(size, offset); + Builder src; + auto root_offset2 = populate2(src); + src.Finish(root_offset2); + auto src_size = src.GetSize(); + // Move into a released builder. + b1 = std::move(src); + TEST_EQ_FUNC(b1.GetSize(), src_size); + TEST_ASSERT_FUNC(release_n_verify(b1, m2_name, m2_color)); + TEST_EQ_FUNC(src.GetSize(), 0); +} +// clang-format off +#endif // !defined(FLATBUFFERS_CPP98_STL) +// clang-format on + +void builder_move_assign_after_releaseraw_test(flatbuffers::grpc::MessageBuilder b1); + +template <class DestBuilder, class SrcBuilder = DestBuilder> struct BuilderTests { // clang-format off #if !defined(FLATBUFFERS_CPP98_STL) // clang-format on static void empty_builder_movector_test() { - Builder b1; - size_t b1_size = b1.GetSize(); - Builder b2(std::move(b1)); - size_t b2_size = b2.GetSize(); - TEST_EQ_FUNC(b1_size, 0); - TEST_EQ_FUNC(b1_size, b2_size); + SrcBuilder src; + size_t src_size = src.GetSize(); + DestBuilder dst(std::move(src)); + size_t dst_size = dst.GetSize(); + TEST_EQ_FUNC(src_size, 0); + TEST_EQ_FUNC(src_size, dst_size); } static void nonempty_builder_movector_test() { - Builder b1; - populate1(b1); - size_t b1_size = b1.GetSize(); - Builder b2(std::move(b1)); - TEST_EQ_FUNC(b1_size, b2.GetSize()); - TEST_EQ_FUNC(b1.GetSize(), 0); + SrcBuilder src; + populate1(src); + size_t src_size = src.GetSize(); + DestBuilder dst(std::move(src)); + TEST_EQ_FUNC(src_size, dst.GetSize()); + TEST_EQ_FUNC(src.GetSize(), 0); } static void builder_movector_before_finish_test() { - Builder b1; - auto root_offset1 = populate1(b1); - Builder b2(std::move(b1)); - b2.Finish(root_offset1); - TEST_ASSERT_FUNC(release_n_verify(b2, m1_name, m1_color)); - TEST_EQ_FUNC(b1.GetSize(), 0); + SrcBuilder src; + auto root_offset1 = populate1(src); + DestBuilder dst(std::move(src)); + dst.Finish(root_offset1); + TEST_ASSERT_FUNC(release_n_verify(dst, m1_name, m1_color)); + TEST_EQ_FUNC(src.GetSize(), 0); } static void builder_movector_after_finish_test() { - Builder b1; - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - auto b1_size = b1.GetSize(); - Builder b2(std::move(b1)); - TEST_EQ_FUNC(b2.GetSize(), b1_size); - TEST_ASSERT_FUNC(release_n_verify(b2, m1_name, m1_color)); - TEST_EQ_FUNC(b1.GetSize(), 0); + SrcBuilder src; + auto root_offset1 = populate1(src); + src.Finish(root_offset1); + auto src_size = src.GetSize(); + DestBuilder dst(std::move(src)); + TEST_EQ_FUNC(dst.GetSize(), src_size); + TEST_ASSERT_FUNC(release_n_verify(dst, m1_name, m1_color)); + TEST_EQ_FUNC(src.GetSize(), 0); } static void builder_move_assign_before_finish_test() { - Builder b1; - auto root_offset1 = populate1(b1); - Builder b2; - populate2(b2); - b2 = std::move(b1); - b2.Finish(root_offset1); - TEST_ASSERT_FUNC(release_n_verify(b2, m1_name, m1_color)); - TEST_EQ_FUNC(b1.GetSize(), 0); + SrcBuilder src; + auto root_offset1 = populate1(src); + DestBuilder dst; + populate2(dst); + dst = std::move(src); + dst.Finish(root_offset1); + TEST_ASSERT_FUNC(release_n_verify(dst, m1_name, m1_color)); + TEST_EQ_FUNC(src.GetSize(), 0); } static void builder_move_assign_after_finish_test() { - Builder b1; - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - auto b1_size = b1.GetSize(); - Builder b2; - auto root_offset2 = populate2(b2); - b2.Finish(root_offset2); - b2 = std::move(b1); - TEST_EQ_FUNC(b2.GetSize(), b1_size); - TEST_ASSERT_FUNC(release_n_verify(b2, m1_name, m1_color)); - TEST_EQ_FUNC(b1.GetSize(), 0); + SrcBuilder src; + auto root_offset1 = populate1(src); + src.Finish(root_offset1); + auto src_size = src.GetSize(); + DestBuilder dst; + auto root_offset2 = populate2(dst); + dst.Finish(root_offset2); + dst = std::move(src); + TEST_EQ_FUNC(dst.GetSize(), src_size); + TEST_ASSERT_FUNC(release_n_verify(dst, m1_name, m1_color)); + TEST_EQ_FUNC(src.GetSize(), 0); } static void builder_move_assign_after_release_test() { - Builder b1; - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); + DestBuilder dst; + auto root_offset1 = populate1(dst); + dst.Finish(root_offset1); { - flatbuffers::DetachedBuffer b1_detached = b1.Release(); + flatbuffers::DetachedBuffer dst_detached = dst.Release(); // detached buffer is deleted } - Builder b2; - auto root_offset2 = populate2(b2); - b2.Finish(root_offset2); - auto b2_size = b2.GetSize(); + SrcBuilder src; + auto root_offset2 = populate2(src); + src.Finish(root_offset2); + auto src_size = src.GetSize(); // Move into a released builder. - b1 = std::move(b2); - TEST_EQ_FUNC(b1.GetSize(), b2_size); - TEST_ASSERT_FUNC(release_n_verify(b1, m2_name, m2_color)); - TEST_EQ_FUNC(b2.GetSize(), 0); - } - - static void builder_move_assign_after_releaseraw_test() { - Builder b1; - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - size_t size, offset; - uint8_t *buf = release_raw_base(b1, size, offset); - TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color)); - free_raw(b1, buf); - Builder b2; - auto root_offset2 = populate2(b2); - b2.Finish(root_offset2); - auto b2_size = b2.GetSize(); - // Move into a released builder. - b1 = std::move(b2); - TEST_EQ_FUNC(b1.GetSize(), b2_size); - TEST_ASSERT_FUNC(release_n_verify(b1, m2_name, m2_color)); - TEST_EQ_FUNC(b2.GetSize(), 0); + dst = std::move(src); + TEST_EQ_FUNC(dst.GetSize(), src_size); + TEST_ASSERT_FUNC(release_n_verify(dst, m2_name, m2_color)); + TEST_EQ_FUNC(src.GetSize(), 0); } // clang-format off #endif // !defined(FLATBUFFERS_CPP98_STL) // clang-format on - static void builder_swap_before_finish_test() { - Builder b1; - auto root_offset1 = populate1(b1); - auto size1 = b1.GetSize(); - Builder b2; - auto root_offset2 = populate2(b2); - auto size2 = b2.GetSize(); - b1.Swap(b2); - b1.Finish(root_offset2); - b2.Finish(root_offset1); - TEST_EQ_FUNC(b1.GetSize() > size2, true); - TEST_EQ_FUNC(b2.GetSize() > size1, true); - TEST_ASSERT_FUNC(release_n_verify(b1, m2_name, m2_color)); - TEST_ASSERT_FUNC(release_n_verify(b2, m1_name, m1_color)); + static void builder_swap_before_finish_test(bool run = is_same<DestBuilder, SrcBuilder>::value) { + /// Swap is allowed only when lhs and rhs are the same concrete type. + if(run) { + SrcBuilder src; + auto root_offset1 = populate1(src); + auto size1 = src.GetSize(); + DestBuilder dst; + auto root_offset2 = populate2(dst); + auto size2 = dst.GetSize(); + src.Swap(dst); + src.Finish(root_offset2); + dst.Finish(root_offset1); + TEST_EQ_FUNC(src.GetSize() > size2, true); + TEST_EQ_FUNC(dst.GetSize() > size1, true); + TEST_ASSERT_FUNC(release_n_verify(src, m2_name, m2_color)); + TEST_ASSERT_FUNC(release_n_verify(dst, m1_name, m1_color)); + } } - static void builder_swap_after_finish_test() { - Builder b1; - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - auto size1 = b1.GetSize(); - Builder b2; - auto root_offset2 = populate2(b2); - b2.Finish(root_offset2); - auto size2 = b2.GetSize(); - b1.Swap(b2); - TEST_EQ_FUNC(b1.GetSize(), size2); - TEST_EQ_FUNC(b2.GetSize(), size1); - TEST_ASSERT_FUNC(release_n_verify(b1, m2_name, m2_color)); - TEST_ASSERT_FUNC(release_n_verify(b2, m1_name, m1_color)); + static void builder_swap_after_finish_test(bool run = is_same<DestBuilder, SrcBuilder>::value) { + /// Swap is allowed only when lhs and rhs are the same concrete type. + if(run) { + SrcBuilder src; + auto root_offset1 = populate1(src); + src.Finish(root_offset1); + auto size1 = src.GetSize(); + DestBuilder dst; + auto root_offset2 = populate2(dst); + dst.Finish(root_offset2); + auto size2 = dst.GetSize(); + src.Swap(dst); + TEST_EQ_FUNC(src.GetSize(), size2); + TEST_EQ_FUNC(dst.GetSize(), size1); + TEST_ASSERT_FUNC(release_n_verify(src, m2_name, m2_color)); + TEST_ASSERT_FUNC(release_n_verify(dst, m1_name, m1_color)); + } } static void all_tests() { @@ -184,7 +207,7 @@ struct BuilderTests { builder_move_assign_before_finish_test(); builder_move_assign_after_finish_test(); builder_move_assign_after_release_test(); - builder_move_assign_after_releaseraw_test(); + builder_move_assign_after_releaseraw_test(DestBuilder()); // clang-format off #endif // !defined(FLATBUFFERS_CPP98_STL) // clang-format on @@ -204,19 +227,19 @@ enum BuilderReuseTestSelector { typedef std::set<BuilderReuseTestSelector> TestSelector; -template <class Builder> +template <class DestBuilder, class SrcBuilder> struct BuilderReuseTests { static void builder_reusable_after_release_test(TestSelector selector) { if (!selector.count(REUSABLE_AFTER_RELEASE)) { return; } - Builder b1; + DestBuilder fbb; std::vector<flatbuffers::DetachedBuffer> buffers; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - buffers.push_back(b1.Release()); + auto root_offset1 = populate1(fbb); + fbb.Finish(root_offset1); + buffers.push_back(fbb.Release()); TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color)); } } @@ -226,14 +249,14 @@ struct BuilderReuseTests { return; } - Builder b1; + DestBuilder fbb; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); + auto root_offset1 = populate1(fbb); + fbb.Finish(root_offset1); size_t size, offset; - uint8_t *buf = release_raw_base(b1, size, offset); + uint8_t *buf = release_raw_base(fbb, size, offset); TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color)); - free_raw(b1, buf); + free_raw(fbb, buf); } } @@ -245,16 +268,16 @@ struct BuilderReuseTests { return; } - Builder b1; + DestBuilder dst; std::vector<flatbuffers::DetachedBuffer> buffers; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); - buffers.push_back(b1.Release()); + auto root_offset1 = populate1(dst); + dst.Finish(root_offset1); + buffers.push_back(dst.Release()); TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color)); - Builder b2; - b1 = std::move(b2); - TEST_EQ_FUNC(b2.GetSize(), 0); + SrcBuilder src; + dst = std::move(src); + TEST_EQ_FUNC(src.GetSize(), 0); } } @@ -263,17 +286,17 @@ struct BuilderReuseTests { return; } - Builder b1; + DestBuilder dst; for (int i = 0; i < 5; ++i) { - auto root_offset1 = populate1(b1); - b1.Finish(root_offset1); + auto root_offset1 = populate1(dst); + dst.Finish(root_offset1); size_t size, offset; - uint8_t *buf = release_raw_base(b1, size, offset); + uint8_t *buf = release_raw_base(dst, size, offset); TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color)); - free_raw(b1, buf); - Builder b2; - b1 = std::move(b2); - TEST_EQ_FUNC(b2.GetSize(), 0); + free_raw(dst, buf); + SrcBuilder src; + dst = std::move(src); + TEST_EQ_FUNC(src.GetSize(), 0); } } // clang-format off |