mirror of
https://github.com/google/flatbuffers.git
synced 2026-07-05 17:46:56 +00:00
Add FlatBufferBuilder move semantics tests to the main test suite (#4902)
* Add FlatBufferBuilder move semantics tests to main Do not eagerly delete/reset allocators in release and release_raw functions Update android, vs2010 build files New tests for various types of FlatBufferBuilders and move semantics * Improve test failure output with function names
This commit is contained in:
committed by
Wouter van Oortmerssen
parent
b1a925dfc2
commit
49fed8c4f6
4
BUILD
4
BUILD
@@ -122,6 +122,10 @@ cc_test(
|
|||||||
"tests/namespace_test/namespace_test1_generated.h",
|
"tests/namespace_test/namespace_test1_generated.h",
|
||||||
"tests/namespace_test/namespace_test2_generated.h",
|
"tests/namespace_test/namespace_test2_generated.h",
|
||||||
"tests/test.cpp",
|
"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",
|
"tests/union_vector/union_vector_generated.h",
|
||||||
":public_headers",
|
":public_headers",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ set(FlatBuffers_Tests_SRCS
|
|||||||
${FlatBuffers_Library_SRCS}
|
${FlatBuffers_Library_SRCS}
|
||||||
src/idl_gen_fbs.cpp
|
src/idl_gen_fbs.cpp
|
||||||
tests/test.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
|
# file generate by running compiler on tests/monster_test.fbs
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
|
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
|
||||||
)
|
)
|
||||||
@@ -100,7 +104,11 @@ set(FlatBuffers_GRPCTest_SRCS
|
|||||||
include/flatbuffers/flatbuffers.h
|
include/flatbuffers/flatbuffers.h
|
||||||
include/flatbuffers/grpc.h
|
include/flatbuffers/grpc.h
|
||||||
tests/monster_test.grpc.fb.h
|
tests/monster_test.grpc.fb.h
|
||||||
|
tests/test_assert.h
|
||||||
|
tests/test_builder.h
|
||||||
tests/monster_test.grpc.fb.cc
|
tests/monster_test.grpc.fb.cc
|
||||||
|
tests/test_assert.cpp
|
||||||
|
tests/test_builder.cpp
|
||||||
grpc/tests/grpctest.cpp
|
grpc/tests/grpctest.cpp
|
||||||
grpc/tests/message_builder_test.cpp
|
grpc/tests/message_builder_test.cpp
|
||||||
# file generated by running compiler on samples/monster.fbs
|
# file generated by running compiler on samples/monster.fbs
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_MODULE := FlatBufferTest
|
LOCAL_MODULE := FlatBufferTest
|
||||||
LOCAL_SRC_FILES := android/jni/main.cpp \
|
LOCAL_SRC_FILES := android/jni/main.cpp \
|
||||||
tests/test.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_fbs.cpp \
|
||||||
src/idl_gen_general.cpp
|
src/idl_gen_general.cpp
|
||||||
LOCAL_LDLIBS := -llog -landroid -latomic
|
LOCAL_LDLIBS := -llog -landroid -latomic
|
||||||
|
|||||||
@@ -20,9 +20,10 @@
|
|||||||
|
|
||||||
#include "monster_test.grpc.fb.h"
|
#include "monster_test.grpc.fb.h"
|
||||||
#include "monster_test_generated.h"
|
#include "monster_test_generated.h"
|
||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
using namespace MyGame::Example;
|
using namespace MyGame::Example;
|
||||||
int builder_tests();
|
void message_builder_tests();
|
||||||
|
|
||||||
// The callback implementation of our server, that derives from the generated
|
// The callback implementation of our server, that derives from the generated
|
||||||
// code. It implements all rpcs specified in the FlatBuffers schema.
|
// code. It implements all rpcs specified in the FlatBuffers schema.
|
||||||
@@ -166,6 +167,15 @@ int grpc_server_test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int /*argc*/, const char * /*argv*/ []) {
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,10 @@
|
|||||||
#include "flatbuffers/grpc.h"
|
#include "flatbuffers/grpc.h"
|
||||||
#include "monster_test_generated.h"
|
#include "monster_test_generated.h"
|
||||||
|
#include "test_assert.h"
|
||||||
|
#include "test_builder.h"
|
||||||
|
|
||||||
static int builder_test_error = 0;
|
bool verify(flatbuffers::grpc::Message<Monster> &msg, const std::string &expected_name, Color color) {
|
||||||
|
const Monster *monster = msg.GetRoot();
|
||||||
#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());
|
|
||||||
return (monster->name()->str() == expected_name) && (monster->color() == color);
|
return (monster->name()->str() == expected_name) && (monster->color() == color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,121 +14,175 @@ bool release_n_verify(flatbuffers::grpc::MessageBuilder &mbb, const std::string
|
|||||||
return (monster->name()->str() == expected_name) && (monster->color() == color);
|
return (monster->name()->str() == expected_name) && (monster->color() == color);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OwnedAllocator : public flatbuffers::DefaultAllocator {};
|
template <>
|
||||||
|
struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder> {
|
||||||
|
static void builder_reusable_after_release_message_test(TestSelector selector) {
|
||||||
|
if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct TestHeapMessageBuilder : public flatbuffers::FlatBufferBuilder {
|
flatbuffers::grpc::MessageBuilder b1;
|
||||||
TestHeapMessageBuilder()
|
std::vector<flatbuffers::grpc::Message<Monster>> buffers;
|
||||||
: flatbuffers::FlatBufferBuilder(2048, new OwnedAllocator(), true) {}
|
for (int i = 0; i < 5; ++i) {
|
||||||
};
|
auto root_offset1 = populate1(b1);
|
||||||
|
b1.Finish(root_offset1);
|
||||||
template <class Builder>
|
buffers.push_back(b1.ReleaseMessage<Monster>());
|
||||||
struct BuilderTests {
|
TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nonempty_builder_movector_test() {
|
static void builder_reusable_after_release_test(TestSelector selector) {
|
||||||
Builder b1;
|
if (!selector.count(REUSABLE_AFTER_RELEASE)) {
|
||||||
populate1(b1);
|
return;
|
||||||
size_t b1_size = b1.GetSize();
|
}
|
||||||
Builder b2(std::move(b1));
|
|
||||||
test_assert(b1_size == b2.GetSize());
|
// FIXME: Populate-Release loop fails assert(GRPC_SLICE_IS_EMPTY(slice_)).
|
||||||
test_assert(0 == b1.GetSize());
|
|
||||||
|
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() {
|
static void builder_reusable_after_releaseraw_test(TestSelector selector) {
|
||||||
Builder b1;
|
if (!selector.count(REUSABLE_AFTER_RELEASE_RAW)) {
|
||||||
auto root_offset1 = populate1(b1);
|
return;
|
||||||
Builder b2(std::move(b1));
|
}
|
||||||
b2.Finish(root_offset1);
|
|
||||||
test_assert(release_n_verify(b2, m1_name, m1_color));
|
flatbuffers::grpc::MessageBuilder b1;
|
||||||
test_assert(0 == b1.GetSize());
|
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() {
|
static void builder_reusable_after_release_and_move_assign_test(TestSelector selector) {
|
||||||
Builder b1;
|
if (!selector.count(REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN)) {
|
||||||
auto root_offset1 = populate1(b1);
|
return;
|
||||||
b1.Finish(root_offset1);
|
}
|
||||||
Builder b2(std::move(b1));
|
|
||||||
test_assert(release_n_verify(b2, m1_name, m1_color));
|
// FIXME: Release-move_assign loop fails assert(p == GRPC_SLICE_START_PTR(slice_)).
|
||||||
test_assert(0 == b1.GetSize());
|
|
||||||
|
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() {
|
static void builder_reusable_after_release_message_and_move_assign_test(TestSelector selector) {
|
||||||
Builder b1;
|
if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN)) {
|
||||||
auto root_offset1 = populate1(b1);
|
return;
|
||||||
Builder b2;
|
}
|
||||||
populate2(b2);
|
|
||||||
b2 = std::move(b1);
|
flatbuffers::grpc::MessageBuilder b1;
|
||||||
b2.Finish(root_offset1);
|
std::vector<flatbuffers::grpc::Message<Monster>> buffers;
|
||||||
test_assert(release_n_verify(b2, m1_name, m1_color));
|
|
||||||
test_assert(0 == b1.GetSize());
|
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() {
|
static void builder_reusable_after_releaseraw_and_move_assign_test(TestSelector selector) {
|
||||||
Builder b1;
|
if (!selector.count(REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN)) {
|
||||||
auto root_offset1 = populate1(b1);
|
return;
|
||||||
b1.Finish(root_offset1);
|
}
|
||||||
Builder b2;
|
|
||||||
auto root_offset2 = populate2(b2);
|
flatbuffers::grpc::MessageBuilder b1;
|
||||||
b2.Finish(root_offset2);
|
for (int i = 0; i < 5; ++i) {
|
||||||
b2 = std::move(b1);
|
auto root_offset1 = populate1(b1);
|
||||||
test_assert(release_n_verify(b2, m1_name, m1_color));
|
b1.Finish(root_offset1);
|
||||||
test_assert(0 == b1.GetSize());
|
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() {
|
static void run_tests(TestSelector selector) {
|
||||||
Builder b1;
|
builder_reusable_after_release_test(selector);
|
||||||
auto root_offset1 = populate1(b1);
|
builder_reusable_after_release_message_test(selector);
|
||||||
auto size1 = b1.GetSize();
|
builder_reusable_after_releaseraw_test(selector);
|
||||||
Builder b2;
|
builder_reusable_after_release_and_move_assign_test(selector);
|
||||||
auto root_offset2 = populate2(b2);
|
builder_reusable_after_releaseraw_and_move_assign_test(selector);
|
||||||
auto size2 = b2.GetSize();
|
builder_reusable_after_release_message_and_move_assign_test(selector);
|
||||||
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 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int builder_tests() {
|
void slice_allocator_tests() {
|
||||||
BuilderTests<flatbuffers::grpc::MessageBuilder>::all_tests();
|
// move-construct no-delete test
|
||||||
BuilderTests<flatbuffers::FlatBufferBuilder>::all_tests();
|
{
|
||||||
BuilderTests<TestHeapMessageBuilder>::all_tests();
|
size_t size = 2048;
|
||||||
return builder_test_error;
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void message_builder_tests() {
|
||||||
|
slice_allocator_tests();
|
||||||
|
BuilderTests<flatbuffers::grpc::MessageBuilder>::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<flatbuffers::grpc::MessageBuilder>::run_tests(TestSelector(tests, tests+6));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -581,10 +581,10 @@ class vector_downward {
|
|||||||
buf_(other.buf_),
|
buf_(other.buf_),
|
||||||
cur_(other.cur_),
|
cur_(other.cur_),
|
||||||
scratch_(other.scratch_) {
|
scratch_(other.scratch_) {
|
||||||
other.allocator_ = nullptr;
|
// No change in other.allocator_
|
||||||
other.own_allocator_ = false;
|
|
||||||
// No change in other.initial_size_
|
// No change in other.initial_size_
|
||||||
// No change in other.buffer_minalign_
|
// No change in other.buffer_minalign_
|
||||||
|
other.own_allocator_ = false;
|
||||||
other.reserved_ = 0;
|
other.reserved_ = 0;
|
||||||
other.buf_ = nullptr;
|
other.buf_ = nullptr;
|
||||||
other.cur_ = nullptr;
|
other.cur_ = nullptr;
|
||||||
@@ -639,18 +639,22 @@ class vector_downward {
|
|||||||
allocated_bytes = reserved_;
|
allocated_bytes = reserved_;
|
||||||
offset = static_cast<size_t>(cur_ - buf_);
|
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;
|
buf_ = nullptr;
|
||||||
clear_allocator();
|
|
||||||
clear();
|
clear();
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relinquish the pointer to the caller.
|
// Relinquish the pointer to the caller.
|
||||||
DetachedBuffer release() {
|
DetachedBuffer release() {
|
||||||
|
// allocator ownership (if any) is transferred to DetachedBuffer.
|
||||||
DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_,
|
DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_,
|
||||||
size());
|
size());
|
||||||
allocator_ = nullptr;
|
if (own_allocator_) {
|
||||||
own_allocator_ = false;
|
allocator_ = nullptr;
|
||||||
|
own_allocator_ = false;
|
||||||
|
}
|
||||||
buf_ = nullptr;
|
buf_ = nullptr;
|
||||||
clear();
|
clear();
|
||||||
return fb;
|
return fb;
|
||||||
|
|||||||
@@ -89,13 +89,15 @@ class SliceAllocator : public Allocator {
|
|||||||
SliceAllocator &operator=(const SliceAllocator &other) = delete;
|
SliceAllocator &operator=(const SliceAllocator &other) = delete;
|
||||||
|
|
||||||
SliceAllocator(SliceAllocator &&other)
|
SliceAllocator(SliceAllocator &&other)
|
||||||
: slice_(other.slice_) {
|
: slice_(grpc_empty_slice()) {
|
||||||
other.slice_ = grpc_empty_slice();
|
// default-construct and swap idiom
|
||||||
|
swap(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
SliceAllocator &operator=(SliceAllocator &&other) {
|
SliceAllocator &operator=(SliceAllocator &&other) {
|
||||||
slice_ = other.slice_;
|
// move-construct and swap idiom
|
||||||
other.slice_ = grpc_empty_slice();
|
SliceAllocator temp(std::move(other));
|
||||||
|
swap(temp);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +192,16 @@ class MessageBuilder : private detail::SliceAllocatorMember,
|
|||||||
buf_.swap_allocator(other.buf_);
|
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() {}
|
~MessageBuilder() {}
|
||||||
|
|
||||||
// GetMessage extracts the subslice of the buffer corresponding to the
|
// GetMessage extracts the subslice of the buffer corresponding to the
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "namespace_test/namespace_test1_generated.h"
|
#include "namespace_test/namespace_test1_generated.h"
|
||||||
#include "namespace_test/namespace_test2_generated.h"
|
#include "namespace_test/namespace_test2_generated.h"
|
||||||
#include "union_vector/union_vector_generated.h"
|
#include "union_vector/union_vector_generated.h"
|
||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#ifndef FLATBUFFERS_CPP98_STL
|
#ifndef FLATBUFFERS_CPP98_STL
|
||||||
@@ -43,44 +44,7 @@
|
|||||||
|
|
||||||
using namespace MyGame::Example;
|
using namespace MyGame::Example;
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
void FlatBufferBuilderTest();
|
||||||
#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__)
|
|
||||||
|
|
||||||
// Include simple random number generator to ensure results will be the
|
// Include simple random number generator to ensure results will be the
|
||||||
// same cross platform.
|
// same cross platform.
|
||||||
@@ -2041,7 +2005,7 @@ void LoadVerifyBinaryTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int /*argc*/, const char * /*argv*/ []) {
|
int FlatBufferTests() {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \
|
#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \
|
||||||
defined(_MSC_VER) && defined(_DEBUG)
|
defined(_MSC_VER) && defined(_DEBUG)
|
||||||
@@ -2115,6 +2079,14 @@ int main(int /*argc*/, const char * /*argv*/ []) {
|
|||||||
UninitializedVectorTest();
|
UninitializedVectorTest();
|
||||||
EqualOperatorTest();
|
EqualOperatorTest();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int /*argc*/, const char * /*argv*/ []) {
|
||||||
|
|
||||||
|
FlatBufferTests();
|
||||||
|
FlatBufferBuilderTest();
|
||||||
|
|
||||||
if (!testing_fails) {
|
if (!testing_fails) {
|
||||||
TEST_OUTPUT_LINE("ALL TESTS PASSED");
|
TEST_OUTPUT_LINE("ALL TESTS PASSED");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
17
tests/test_assert.cpp
Normal file
17
tests/test_assert.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#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); }
|
||||||
|
}
|
||||||
|
|
||||||
46
tests/test_assert.h
Normal file
46
tests/test_assert.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#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
|
||||||
128
tests/test_builder.cpp
Normal file
128
tests/test_builder.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#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));
|
||||||
|
}
|
||||||
273
tests/test_builder.h
Normal file
273
tests/test_builder.h
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
#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
|
||||||
Reference in New Issue
Block a user