Commit 49fed8c4 authored by Sumant Tambe's avatar Sumant Tambe Committed by Wouter van Oortmerssen

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
parent b1a925df
...@@ -122,6 +122,10 @@ cc_test( ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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 @@ ...@@ -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() { ...@@ -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;
}
} }
This diff is collapsed.
...@@ -581,10 +581,10 @@ class vector_downward { ...@@ -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 { ...@@ -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 { ...@@ -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, ...@@ -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 @@ ...@@ -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 @@ ...@@ -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() { ...@@ -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*/ []) { ...@@ -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;
......
#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); }
}
#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
#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));
}
#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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment