"tests/namespace_test/namespace_test1_generated.h",
"tests/namespace_test/namespace_test2_generated.h",
"tests/test.cpp",
+ "tests/test_builder.h",
+ "tests/test_assert.h",
+ "tests/test_builder.cpp",
+ "tests/test_assert.cpp",
"tests/union_vector/union_vector_generated.h",
":public_headers",
],
${FlatBuffers_Library_SRCS}
src/idl_gen_fbs.cpp
tests/test.cpp
+ tests/test_assert.h
+ tests/test_assert.cpp
+ tests/test_builder.h
+ tests/test_builder.cpp
# file generate by running compiler on tests/monster_test.fbs
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
)
include/flatbuffers/flatbuffers.h
include/flatbuffers/grpc.h
tests/monster_test.grpc.fb.h
+ tests/test_assert.h
+ tests/test_builder.h
tests/monster_test.grpc.fb.cc
+ tests/test_assert.cpp
+ tests/test_builder.cpp
grpc/tests/grpctest.cpp
grpc/tests/message_builder_test.cpp
# file generated by running compiler on samples/monster.fbs
LOCAL_MODULE := FlatBufferTest
LOCAL_SRC_FILES := android/jni/main.cpp \
tests/test.cpp \
+ tests/test_assert.h \
+ tests/test_builder.h \
+ tests/test_assert.cpp \
+ tests/test_builder.cpp \
src/idl_gen_fbs.cpp \
src/idl_gen_general.cpp
LOCAL_LDLIBS := -llog -landroid -latomic
#include "monster_test.grpc.fb.h"
#include "monster_test_generated.h"
+#include "test_assert.h"
using namespace MyGame::Example;
-int builder_tests();
+void message_builder_tests();
// The callback implementation of our server, that derives from the generated
// code. It implements all rpcs specified in the FlatBuffers schema.
}
int main(int /*argc*/, const char * /*argv*/ []) {
- return builder_tests() + grpc_server_test();
+ message_builder_tests();
+ grpc_server_test();
+
+ if (!testing_fails) {
+ TEST_OUTPUT_LINE("ALL TESTS PASSED");
+ return 0;
+ } else {
+ TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
+ return 1;
+ }
}
#include "flatbuffers/grpc.h"
#include "monster_test_generated.h"
+#include "test_assert.h"
+#include "test_builder.h"
-static int builder_test_error = 0;
-
-#define test_assert(condition) do { \
- if(!(condition)) { \
- fprintf(stderr, "%s:%d: %s failed.\n", __FILE__, __LINE__, #condition);\
- builder_test_error = 1;\
- } \
-} while(0)
-
-using namespace MyGame::Example;
-
-const std::string m1_name = "Cyberdemon";
-const Color m1_color = Color_Red;
-const std::string m2_name = "Imp";
-const Color m2_color = Color_Green;
-
-flatbuffers::Offset<Monster> populate1(flatbuffers::FlatBufferBuilder &builder) {
- auto name_offset = builder.CreateString(m1_name);
- return CreateMonster(builder, nullptr, 0, 0, name_offset, 0, m1_color);
-}
-
-flatbuffers::Offset<Monster> populate2(flatbuffers::FlatBufferBuilder &builder) {
- auto name_offset = builder.CreateString(m2_name);
- return CreateMonster(builder, nullptr, 0, 0, name_offset, 0, m2_color);
-}
-
-bool release_n_verify(flatbuffers::FlatBufferBuilder &fbb, const std::string &expected_name, Color color) {
- flatbuffers::DetachedBuffer buf = fbb.Release();
- const Monster *monster = flatbuffers::GetRoot<Monster>(buf.data());
+bool verify(flatbuffers::grpc::Message<Monster> &msg, const std::string &expected_name, Color color) {
+ const Monster *monster = msg.GetRoot();
return (monster->name()->str() == expected_name) && (monster->color() == color);
}
return (monster->name()->str() == expected_name) && (monster->color() == color);
}
-struct OwnedAllocator : public flatbuffers::DefaultAllocator {};
-
-struct TestHeapMessageBuilder : public flatbuffers::FlatBufferBuilder {
- TestHeapMessageBuilder()
- : flatbuffers::FlatBufferBuilder(2048, new OwnedAllocator(), true) {}
-};
-
-template <class Builder>
-struct BuilderTests {
- 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_assert(b1_size == 0);
- test_assert(b1_size == b2_size);
+template <>
+struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> {
+ static void builder_reusable_after_release_message_test(TestSelector selector) {
+ if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE)) {
+ return;
+ }
+
+ flatbuffers::grpc::MessageBuilder b1;
+ 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>());
+ TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
+ }
}
- static void nonempty_builder_movector_test() {
- Builder b1;
- populate1(b1);
- size_t b1_size = b1.GetSize();
- Builder b2(std::move(b1));
- test_assert(b1_size == b2.GetSize());
- test_assert(0 == b1.GetSize());
+ 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_)).
+
+ flatbuffers::grpc::MessageBuilder b1;
+ 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());
+ TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
+ }
}
- 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(release_n_verify(b2, m1_name, m1_color));
- test_assert(0 == b1.GetSize());
+ static void builder_reusable_after_releaseraw_test(TestSelector selector) {
+ if (!selector.count(REUSABLE_AFTER_RELEASE_RAW)) {
+ return;
+ }
+
+ flatbuffers::grpc::MessageBuilder b1;
+ for (int i = 0; i < 5; ++i) {
+ auto root_offset1 = populate1(b1);
+ b1.Finish(root_offset1);
+ size_t size, offset;
+ grpc_slice slice;
+ const uint8_t *buf = b1.ReleaseRaw(size, offset, slice);
+ TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color));
+ grpc_slice_unref(slice);
+ }
}
- static void builder_movector_after_finish_test() {
- Builder b1;
- auto root_offset1 = populate1(b1);
- b1.Finish(root_offset1);
- Builder b2(std::move(b1));
- test_assert(release_n_verify(b2, m1_name, m1_color));
- test_assert(0 == b1.GetSize());
+ 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_)).
+
+ flatbuffers::grpc::MessageBuilder b1;
+ 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());
+ 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);
+ }
}
- 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(release_n_verify(b2, m1_name, m1_color));
- test_assert(0 == b1.GetSize());
+ 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 b1;
+ 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>());
+ 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);
+ }
}
- static void builder_move_assign_after_finish_test() {
- Builder b1;
- auto root_offset1 = populate1(b1);
- b1.Finish(root_offset1);
- Builder b2;
- auto root_offset2 = populate2(b2);
- b2.Finish(root_offset2);
- b2 = std::move(b1);
- test_assert(release_n_verify(b2, m1_name, m1_color));
- test_assert(0 == b1.GetSize());
+ 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 b1;
+ for (int i = 0; i < 5; ++i) {
+ auto root_offset1 = populate1(b1);
+ b1.Finish(root_offset1);
+ size_t size, offset;
+ grpc_slice slice = grpc_empty_slice();
+ const uint8_t *buf = b1.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);
+ }
}
- 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_assert(b1.GetSize() > size2);
- test_assert(b2.GetSize() > size1);
- test_assert(release_n_verify(b1, m2_name, m2_color));
- test_assert(release_n_verify(b2, m1_name, m1_color));
+ 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);
}
+};
- 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_assert(b1.GetSize() == size2);
- test_assert(b2.GetSize() == size1);
- test_assert(release_n_verify(b1, m2_name, m2_color));
- test_assert(release_n_verify(b2, m1_name, m1_color));
+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 be deleted after move-construct
+ TEST_EQ_FUNC(buf[0], 100);
+ TEST_EQ_FUNC(buf[size-1], 200);
+ // buf is freed here
}
- static void all_tests() {
- empty_builder_movector_test();
- nonempty_builder_movector_test();
- builder_movector_before_finish_test();
- builder_movector_after_finish_test();
- builder_move_assign_before_finish_test();
- builder_move_assign_after_finish_test();
- builder_swap_before_finish_test();
- builder_swap_after_finish_test();
+ // 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);
}
-};
+}
-int builder_tests() {
+void message_builder_tests() {
+ slice_allocator_tests();
BuilderTests<flatbuffers::grpc::MessageBuilder>::all_tests();
- BuilderTests<flatbuffers::FlatBufferBuilder>::all_tests();
- BuilderTests<TestHeapMessageBuilder>::all_tests();
- return builder_test_error;
+
+ 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<flatbuffers::grpc::MessageBuilder>::run_tests(TestSelector(tests, tests+6));
}
buf_(other.buf_),
cur_(other.cur_),
scratch_(other.scratch_) {
- other.allocator_ = nullptr;
- other.own_allocator_ = false;
+ // No change in other.allocator_
// No change in other.initial_size_
// No change in other.buffer_minalign_
+ other.own_allocator_ = false;
other.reserved_ = 0;
other.buf_ = nullptr;
other.cur_ = nullptr;
allocated_bytes = reserved_;
offset = static_cast<size_t>(cur_ - buf_);
+ // release_raw only relinquishes the buffer ownership.
+ // Does not deallocate or reset the allocator. Destructor will do that.
buf_ = nullptr;
- clear_allocator();
clear();
return buf;
}
// Relinquish the pointer to the caller.
DetachedBuffer release() {
+ // allocator ownership (if any) is transferred to DetachedBuffer.
DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_,
size());
- allocator_ = nullptr;
- own_allocator_ = false;
+ if (own_allocator_) {
+ allocator_ = nullptr;
+ own_allocator_ = false;
+ }
buf_ = nullptr;
clear();
return fb;
SliceAllocator &operator=(const SliceAllocator &other) = delete;
SliceAllocator(SliceAllocator &&other)
- : slice_(other.slice_) {
- other.slice_ = grpc_empty_slice();
+ : slice_(grpc_empty_slice()) {
+ // default-construct and swap idiom
+ swap(other);
}
SliceAllocator &operator=(SliceAllocator &&other) {
- slice_ = other.slice_;
- other.slice_ = grpc_empty_slice();
+ // move-construct and swap idiom
+ SliceAllocator temp(std::move(other));
+ swap(temp);
return *this;
}
buf_.swap_allocator(other.buf_);
}
+ // Releases the ownership of the buffer pointer.
+ // Returns the size, offset, and the original grpc_slice that
+ // allocated the buffer. Also see grpc_slice_unref().
+ uint8_t *ReleaseRaw(size_t &size, size_t &offset, grpc_slice &slice) {
+ uint8_t *buf = FlatBufferBuilder::ReleaseRaw(size, offset);
+ slice = slice_allocator_.slice_;
+ slice_allocator_.slice_ = grpc_empty_slice();
+ return buf;
+ }
+
~MessageBuilder() {}
// GetMessage extracts the subslice of the buffer corresponding to the
#include "namespace_test/namespace_test1_generated.h"
#include "namespace_test/namespace_test2_generated.h"
#include "union_vector/union_vector_generated.h"
+#include "test_assert.h"
// clang-format off
#ifndef FLATBUFFERS_CPP98_STL
using namespace MyGame::Example;
-#ifdef __ANDROID__
- #include <android/log.h>
- #define TEST_OUTPUT_LINE(...) \
- __android_log_print(ANDROID_LOG_INFO, "FlatBuffers", __VA_ARGS__)
- #define FLATBUFFERS_NO_FILE_TESTS
-#else
- #define TEST_OUTPUT_LINE(...) \
- { printf(__VA_ARGS__); printf("\n"); }
-#endif
-// clang-format on
-
-int testing_fails = 0;
-
-void TestFail(const char *expval, const char *val, const char *exp,
- const char *file, int line) {
- TEST_OUTPUT_LINE("VALUE: \"%s\"", expval);
- TEST_OUTPUT_LINE("EXPECTED: \"%s\"", val);
- TEST_OUTPUT_LINE("TEST FAILED: %s:%d, %s", file, line, exp);
- assert(0);
- testing_fails++;
-}
-
-void TestEqStr(const char *expval, const char *val, const char *exp,
- const char *file, int line) {
- if (strcmp(expval, val) != 0) { TestFail(expval, val, exp, file, line); }
-}
-
-template<typename T, typename U>
-void TestEq(T expval, U val, const char *exp, const char *file, int line) {
- if (U(expval) != val) {
- TestFail(flatbuffers::NumToString(expval).c_str(),
- flatbuffers::NumToString(val).c_str(), exp, file, line);
- }
-}
-
-#define TEST_EQ(exp, val) TestEq(exp, val, #exp, __FILE__, __LINE__)
-#define TEST_NOTNULL(exp) TestEq(exp == NULL, false, #exp, __FILE__, __LINE__)
-#define TEST_EQ_STR(exp, val) TestEqStr(exp, val, #exp, __FILE__, __LINE__)
+void FlatBufferBuilderTest();
// Include simple random number generator to ensure results will be the
// same cross platform.
}
}
-int main(int /*argc*/, const char * /*argv*/ []) {
+int FlatBufferTests() {
// clang-format off
#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \
defined(_MSC_VER) && defined(_DEBUG)
UninitializedVectorTest();
EqualOperatorTest();
+ return 0;
+}
+
+int main(int /*argc*/, const char * /*argv*/ []) {
+
+ FlatBufferTests();
+ FlatBufferBuilderTest();
+
if (!testing_fails) {
TEST_OUTPUT_LINE("ALL TESTS PASSED");
return 0;
--- /dev/null
+#include "test_assert.h"
+
+int testing_fails = 0;
+
+void TestFail(const char *expval, const char *val, const char *exp,
+ const char *file, int line, const char *func) {
+ TEST_OUTPUT_LINE("VALUE: \"%s\"", expval);
+ TEST_OUTPUT_LINE("EXPECTED: \"%s\"", val);
+ TEST_OUTPUT_LINE("TEST FAILED: %s:%d, %s in %s", file, line, exp, func? func : "");
+ testing_fails++;
+}
+
+void TestEqStr(const char *expval, const char *val, const char *exp,
+ const char *file, int line) {
+ if (strcmp(expval, val) != 0) { TestFail(expval, val, exp, file, line); }
+}
+
--- /dev/null
+#ifndef TEST_ASSERT_H
+#define TEST_ASSERT_H
+
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/util.h"
+
+#ifdef __ANDROID__
+#include <android/log.h>
+ #define TEST_OUTPUT_LINE(...) \
+ __android_log_print(ANDROID_LOG_INFO, "FlatBuffers", __VA_ARGS__)
+ #define FLATBUFFERS_NO_FILE_TESTS
+#else
+#define TEST_OUTPUT_LINE(...) \
+ { printf(__VA_ARGS__); printf("\n"); }
+#endif
+// clang-format on
+
+extern int testing_fails;
+
+void TestFail(const char *expval, const char *val, const char *exp,
+ const char *file, int line, const char *func = 0);
+
+void TestEqStr(const char *expval, const char *val, const char *exp,
+ const char *file, int line);
+
+template<typename T, typename U>
+void TestEq(T expval, U val, const char *exp, const char *file, int line, const char *func = 0) {
+ if (U(expval) != val) {
+ TestFail(flatbuffers::NumToString(expval).c_str(),
+ flatbuffers::NumToString(val).c_str(), exp, file, line, func);
+ }
+}
+
+#define TEST_EQ(exp, val) TestEq(exp, val, #exp, __FILE__, __LINE__)
+#define TEST_ASSERT(exp) TestEq(exp, true, #exp, __FILE__, __LINE__)
+#ifdef WIN32
+ #define TEST_ASSERT_FUNC(exp) TestEq(exp, true, #exp, __FILE__, __LINE__, __FUNCTION__)
+ #define TEST_EQ_FUNC(exp, val) TestEq(exp, val, #exp, __FILE__, __LINE__, __FUNCTION__)
+#else
+ #define TEST_ASSERT_FUNC(exp) TestEq(exp, true, #exp, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+ #define TEST_EQ_FUNC(exp, val) TestEq(exp, val, #exp, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#endif
+#define TEST_NOTNULL(exp) TestEq(exp == NULL, false, #exp, __FILE__, __LINE__)
+#define TEST_EQ_STR(exp, val) TestEqStr(exp, val, #exp, __FILE__, __LINE__)
+
+#endif // TEST_ASSERT_H
--- /dev/null
+#include "monster_test_generated.h"
+#include "test_builder.h"
+
+using namespace MyGame::Example;
+
+const std::string m1_name = "Cyberdemon";
+const Color m1_color = Color_Red;
+const std::string m2_name = "Imp";
+const Color m2_color = Color_Green;
+
+struct OwnedAllocator : public flatbuffers::DefaultAllocator {};
+
+class TestHeapBuilder : public flatbuffers::FlatBufferBuilder {
+private:
+ TestHeapBuilder(const TestHeapBuilder &);
+ TestHeapBuilder &operator=(const TestHeapBuilder &);
+
+public:
+ TestHeapBuilder()
+ : flatbuffers::FlatBufferBuilder(2048, new OwnedAllocator(), true) {}
+
+ TestHeapBuilder(TestHeapBuilder &&other)
+ : FlatBufferBuilder(std::move(other)) { }
+
+ TestHeapBuilder &operator=(TestHeapBuilder &&other) {
+ FlatBufferBuilder::operator=(std::move(other));
+ return *this;
+ }
+};
+
+// This class simulates flatbuffers::grpc::detail::SliceAllocatorMember
+struct AllocatorMember {
+ flatbuffers::DefaultAllocator member_allocator_;
+};
+
+struct GrpcLikeMessageBuilder : private AllocatorMember,
+ public flatbuffers::FlatBufferBuilder {
+private:
+ GrpcLikeMessageBuilder(const GrpcLikeMessageBuilder &);
+ GrpcLikeMessageBuilder &operator=(const GrpcLikeMessageBuilder &);
+
+public:
+ GrpcLikeMessageBuilder()
+ : flatbuffers::FlatBufferBuilder(1024, &member_allocator_, false) {}
+
+ GrpcLikeMessageBuilder(GrpcLikeMessageBuilder &&other)
+ : FlatBufferBuilder(1024, &member_allocator_, false) {
+ // Default construct and swap idiom.
+ Swap(other);
+ }
+
+ GrpcLikeMessageBuilder &operator=(GrpcLikeMessageBuilder &&other) {
+ // Construct temporary and swap idiom
+ GrpcLikeMessageBuilder temp(std::move(other));
+ Swap(temp);
+ return *this;
+ }
+
+ void Swap(GrpcLikeMessageBuilder &other) {
+ // No need to swap member_allocator_ because it's stateless.
+ FlatBufferBuilder::Swap(other);
+ // After swapping the FlatBufferBuilder, we swap back the allocator, which restores
+ // the original allocator back in place. This is necessary because MessageBuilder's
+ // allocator is its own member (SliceAllocatorMember). The allocator passed to
+ // FlatBufferBuilder::vector_downward must point to this member.
+ buf_.swap_allocator(other.buf_);
+ }
+};
+
+flatbuffers::Offset<Monster> populate1(flatbuffers::FlatBufferBuilder &builder) {
+ auto name_offset = builder.CreateString(m1_name);
+ return CreateMonster(builder, nullptr, 0, 0, name_offset, 0, m1_color);
+}
+
+flatbuffers::Offset<Monster> populate2(flatbuffers::FlatBufferBuilder &builder) {
+ auto name_offset = builder.CreateString(m2_name);
+ return CreateMonster(builder, nullptr, 0, 0, name_offset, 0, m2_color);
+}
+
+uint8_t *release_raw_base(flatbuffers::FlatBufferBuilder &fbb, size_t &size, size_t &offset) {
+ return fbb.ReleaseRaw(size, offset);
+}
+
+void free_raw(flatbuffers::grpc::MessageBuilder &, uint8_t *) {
+ // release_raw_base calls FlatBufferBuilder::ReleaseRaw on the argument MessageBuilder.
+ // It's semantically wrong as MessageBuilder has its own ReleaseRaw member function that
+ // takes three arguments. In such cases though, ~MessageBuilder() invokes
+ // ~SliceAllocator() that takes care of deleting memory as it calls grpc_slice_unref.
+ // Obviously, this behavior is very surprising as the pointer returned by
+ // FlatBufferBuilder::ReleaseRaw is not valid as soon as MessageBuilder goes out of scope.
+ // This problem does not occur with FlatBufferBuilder.
+}
+
+void free_raw(flatbuffers::FlatBufferBuilder &, uint8_t *buf) {
+ flatbuffers::DefaultAllocator().deallocate(buf, 0);
+}
+
+bool verify(const flatbuffers::DetachedBuffer &buf, const std::string &expected_name, Color color) {
+ const Monster *monster = flatbuffers::GetRoot<Monster>(buf.data());
+ return (monster->name()->str() == expected_name) && (monster->color() == color);
+}
+
+bool verify(const uint8_t *buf, size_t offset, const std::string &expected_name, Color color) {
+ const Monster *monster = flatbuffers::GetRoot<Monster>(buf+offset);
+ return (monster->name()->str() == expected_name) && (monster->color() == color);
+}
+
+bool release_n_verify(flatbuffers::FlatBufferBuilder &fbb, const std::string &expected_name, Color color) {
+ flatbuffers::DetachedBuffer buf = fbb.Release();
+ return verify(buf, expected_name, color);
+}
+
+void FlatBufferBuilderTest() {
+ BuilderTests<flatbuffers::FlatBufferBuilder>::all_tests();
+ BuilderTests<TestHeapBuilder>::all_tests();
+ BuilderTests<GrpcLikeMessageBuilder>::all_tests();
+
+ BuilderReuseTestSelector tests[4] = {
+ REUSABLE_AFTER_RELEASE,
+ REUSABLE_AFTER_RELEASE_RAW,
+ REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN,
+ 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));
+}
--- /dev/null
+#ifndef TEST_BUILDER_H
+#define TEST_BUILDER_H
+
+#include <set>
+#include "monster_test_generated.h"
+#include "flatbuffers/flatbuffers.h"
+#include "test_assert.h"
+
+using namespace MyGame::Example;
+namespace flatbuffers {
+namespace grpc {
+class MessageBuilder;
+}
+}
+
+extern const std::string m1_name;
+extern const Color m1_color;
+extern const std::string m2_name;
+extern const Color m2_color;
+
+flatbuffers::Offset<Monster> populate1(flatbuffers::FlatBufferBuilder &builder);
+flatbuffers::Offset<Monster> populate2(flatbuffers::FlatBufferBuilder &builder);
+
+uint8_t *release_raw_base(flatbuffers::FlatBufferBuilder &fbb, size_t &size, size_t &offset);
+
+void free_raw(flatbuffers::grpc::MessageBuilder &mbb, uint8_t *buf);
+void free_raw(flatbuffers::FlatBufferBuilder &fbb, uint8_t *buf);
+
+bool verify(const flatbuffers::DetachedBuffer &buf, const std::string &expected_name, Color color);
+bool verify(const uint8_t *buf, size_t offset, const std::string &expected_name, Color color);
+
+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);
+
+template <class Builder>
+struct BuilderTests {
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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_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_move_assign_after_release_test() {
+ Builder b1;
+ auto root_offset1 = populate1(b1);
+ b1.Finish(root_offset1);
+ {
+ flatbuffers::DetachedBuffer b1_detached = b1.Release();
+ // detached buffer is deleted
+ }
+ 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);
+ }
+
+ 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);
+ }
+
+ static void all_tests() {
+ empty_builder_movector_test();
+ nonempty_builder_movector_test();
+ builder_movector_before_finish_test();
+ builder_movector_after_finish_test();
+ builder_move_assign_before_finish_test();
+ builder_move_assign_after_finish_test();
+ builder_swap_before_finish_test();
+ builder_swap_after_finish_test();
+ builder_move_assign_after_release_test();
+ builder_move_assign_after_releaseraw_test();
+ }
+};
+
+enum BuilderReuseTestSelector {
+ REUSABLE_AFTER_RELEASE = 1,
+ REUSABLE_AFTER_RELEASE_RAW = 2,
+ REUSABLE_AFTER_RELEASE_MESSAGE = 3,
+ REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN = 4,
+ REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN = 5,
+ REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN = 6
+};
+
+typedef std::set<BuilderReuseTestSelector> TestSelector;
+
+template <class Builder>
+struct BuilderReuseTests {
+ static void builder_reusable_after_release_test(TestSelector selector) {
+ if (!selector.count(REUSABLE_AFTER_RELEASE)) {
+ return;
+ }
+
+ Builder b1;
+ 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());
+ 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;
+ }
+
+ Builder b1;
+ for (int i = 0; i < 5; ++i) {
+ 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);
+ }
+ }
+
+ static void builder_reusable_after_release_and_move_assign_test(TestSelector selector) {
+ if (!selector.count(REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN)) {
+ return;
+ }
+
+ Builder b1;
+ 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());
+ TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
+ Builder b2;
+ b1 = std::move(b2);
+ TEST_EQ_FUNC(b2.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;
+ }
+
+ Builder b1;
+ for (int i = 0; i < 5; ++i) {
+ 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;
+ b1 = std::move(b2);
+ TEST_EQ_FUNC(b2.GetSize(), 0);
+ }
+ }
+
+ static void run_tests(TestSelector selector) {
+ builder_reusable_after_release_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);
+ }
+};
+
+#endif // TEST_BUILDER_H