1 #include "flatbuffers/grpc.h"
2 #include "monster_test_generated.h"
3 #include "test_assert.h"
4 #include "test_builder.h"
5
6 using MyGame::Example::Vec3;
7 using MyGame::Example::CreateStat;
8 using MyGame::Example::Any_NONE;
9
verify(flatbuffers::grpc::Message<Monster> & msg,const std::string & expected_name,Color color)10 bool verify(flatbuffers::grpc::Message<Monster> &msg, const std::string &expected_name, Color color) {
11 const Monster *monster = msg.GetRoot();
12 return (monster->name()->str() == expected_name) && (monster->color() == color);
13 }
14
release_n_verify(flatbuffers::grpc::MessageBuilder & mbb,const std::string & expected_name,Color color)15 bool release_n_verify(flatbuffers::grpc::MessageBuilder &mbb, const std::string &expected_name, Color color) {
16 flatbuffers::grpc::Message<Monster> msg = mbb.ReleaseMessage<Monster>();
17 const Monster *monster = msg.GetRoot();
18 return (monster->name()->str() == expected_name) && (monster->color() == color);
19 }
20
builder_move_assign_after_releaseraw_test(flatbuffers::grpc::MessageBuilder dst)21 void builder_move_assign_after_releaseraw_test(flatbuffers::grpc::MessageBuilder dst) {
22 auto root_offset1 = populate1(dst);
23 dst.Finish(root_offset1);
24 size_t size, offset;
25 grpc_slice slice;
26 dst.ReleaseRaw(size, offset, slice);
27 flatbuffers::FlatBufferBuilder src;
28 auto root_offset2 = populate2(src);
29 src.Finish(root_offset2);
30 auto src_size = src.GetSize();
31 // Move into a released builder.
32 dst = std::move(src);
33 TEST_EQ(dst.GetSize(), src_size);
34 TEST_ASSERT(release_n_verify(dst, m2_name, m2_color));
35 TEST_EQ(src.GetSize(), 0);
36 grpc_slice_unref(slice);
37 }
38
39 template <class SrcBuilder>
40 struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder, SrcBuilder> {
builder_reusable_after_release_message_testBuilderReuseTests41 static void builder_reusable_after_release_message_test(TestSelector selector) {
42 if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE)) {
43 return;
44 }
45
46 flatbuffers::grpc::MessageBuilder mb;
47 std::vector<flatbuffers::grpc::Message<Monster>> buffers;
48 for (int i = 0; i < 5; ++i) {
49 auto root_offset1 = populate1(mb);
50 mb.Finish(root_offset1);
51 buffers.push_back(mb.ReleaseMessage<Monster>());
52 TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
53 }
54 }
55
builder_reusable_after_release_testBuilderReuseTests56 static void builder_reusable_after_release_test(TestSelector selector) {
57 if (!selector.count(REUSABLE_AFTER_RELEASE)) {
58 return;
59 }
60
61 // FIXME: Populate-Release loop fails assert(GRPC_SLICE_IS_EMPTY(slice_)) in SliceAllocator::allocate
62 // in the second iteration.
63
64 flatbuffers::grpc::MessageBuilder mb;
65 std::vector<flatbuffers::DetachedBuffer> buffers;
66 for (int i = 0; i < 2; ++i) {
67 auto root_offset1 = populate1(mb);
68 mb.Finish(root_offset1);
69 buffers.push_back(mb.Release());
70 TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
71 }
72 }
73
builder_reusable_after_releaseraw_testBuilderReuseTests74 static void builder_reusable_after_releaseraw_test(TestSelector selector) {
75 if (!selector.count(REUSABLE_AFTER_RELEASE_RAW)) {
76 return;
77 }
78
79 flatbuffers::grpc::MessageBuilder mb;
80 for (int i = 0; i < 5; ++i) {
81 auto root_offset1 = populate1(mb);
82 mb.Finish(root_offset1);
83 size_t size, offset;
84 grpc_slice slice;
85 const uint8_t *buf = mb.ReleaseRaw(size, offset, slice);
86 TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color));
87 grpc_slice_unref(slice);
88 }
89 }
90
builder_reusable_after_release_and_move_assign_testBuilderReuseTests91 static void builder_reusable_after_release_and_move_assign_test(TestSelector selector) {
92 if (!selector.count(REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN)) {
93 return;
94 }
95
96 // FIXME: Release-move_assign loop fails assert(p == GRPC_SLICE_START_PTR(slice_))
97 // in DetachedBuffer destructor after all the iterations
98
99 flatbuffers::grpc::MessageBuilder dst;
100 std::vector<flatbuffers::DetachedBuffer> buffers;
101
102 for (int i = 0; i < 2; ++i) {
103 auto root_offset1 = populate1(dst);
104 dst.Finish(root_offset1);
105 buffers.push_back(dst.Release());
106 TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
107
108 // bring dst back to life.
109 SrcBuilder src;
110 dst = std::move(src);
111 TEST_EQ_FUNC(dst.GetSize(), 0);
112 TEST_EQ_FUNC(src.GetSize(), 0);
113 }
114 }
115
builder_reusable_after_release_message_and_move_assign_testBuilderReuseTests116 static void builder_reusable_after_release_message_and_move_assign_test(TestSelector selector) {
117 if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN)) {
118 return;
119 }
120
121 flatbuffers::grpc::MessageBuilder dst;
122 std::vector<flatbuffers::grpc::Message<Monster>> buffers;
123
124 for (int i = 0; i < 5; ++i) {
125 auto root_offset1 = populate1(dst);
126 dst.Finish(root_offset1);
127 buffers.push_back(dst.ReleaseMessage<Monster>());
128 TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
129
130 // bring dst back to life.
131 SrcBuilder src;
132 dst = std::move(src);
133 TEST_EQ_FUNC(dst.GetSize(), 0);
134 TEST_EQ_FUNC(src.GetSize(), 0);
135 }
136 }
137
builder_reusable_after_releaseraw_and_move_assign_testBuilderReuseTests138 static void builder_reusable_after_releaseraw_and_move_assign_test(TestSelector selector) {
139 if (!selector.count(REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN)) {
140 return;
141 }
142
143 flatbuffers::grpc::MessageBuilder dst;
144 for (int i = 0; i < 5; ++i) {
145 auto root_offset1 = populate1(dst);
146 dst.Finish(root_offset1);
147 size_t size, offset;
148 grpc_slice slice = grpc_empty_slice();
149 const uint8_t *buf = dst.ReleaseRaw(size, offset, slice);
150 TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color));
151 grpc_slice_unref(slice);
152
153 SrcBuilder src;
154 dst = std::move(src);
155 TEST_EQ_FUNC(dst.GetSize(), 0);
156 TEST_EQ_FUNC(src.GetSize(), 0);
157 }
158 }
159
run_testsBuilderReuseTests160 static void run_tests(TestSelector selector) {
161 builder_reusable_after_release_test(selector);
162 builder_reusable_after_release_message_test(selector);
163 builder_reusable_after_releaseraw_test(selector);
164 builder_reusable_after_release_and_move_assign_test(selector);
165 builder_reusable_after_releaseraw_and_move_assign_test(selector);
166 builder_reusable_after_release_message_and_move_assign_test(selector);
167 }
168 };
169
slice_allocator_tests()170 void slice_allocator_tests() {
171 // move-construct no-delete test
172 {
173 size_t size = 2048;
174 flatbuffers::grpc::SliceAllocator sa1;
175 uint8_t *buf = sa1.allocate(size);
176 TEST_ASSERT_FUNC(buf != 0);
177 buf[0] = 100;
178 buf[size-1] = 200;
179 flatbuffers::grpc::SliceAllocator sa2(std::move(sa1));
180 // buf should not be deleted after move-construct
181 TEST_EQ_FUNC(buf[0], 100);
182 TEST_EQ_FUNC(buf[size-1], 200);
183 // buf is freed here
184 }
185
186 // move-assign test
187 {
188 flatbuffers::grpc::SliceAllocator sa1, sa2;
189 uint8_t *buf = sa1.allocate(2048);
190 sa1 = std::move(sa2);
191 // sa1 deletes previously allocated memory in move-assign.
192 // So buf is no longer usable here.
193 TEST_ASSERT_FUNC(buf != 0);
194 }
195 }
196
197 /// This function does not populate exactly the first half of the table. But it could.
populate_first_half(MyGame::Example::MonsterBuilder & wrapper,flatbuffers::Offset<flatbuffers::String> name_offset)198 void populate_first_half(MyGame::Example::MonsterBuilder &wrapper, flatbuffers::Offset<flatbuffers::String> name_offset) {
199 wrapper.add_name(name_offset);
200 wrapper.add_color(m1_color);
201 }
202
203 /// This function does not populate exactly the second half of the table. But it could.
populate_second_half(MyGame::Example::MonsterBuilder & wrapper)204 void populate_second_half(MyGame::Example::MonsterBuilder &wrapper) {
205 wrapper.add_hp(77);
206 wrapper.add_mana(88);
207 Vec3 vec3;
208 wrapper.add_pos(&vec3);
209 }
210
211 /// This function is a hack to update the FlatBufferBuilder reference (fbb_) in the MonsterBuilder object.
212 /// This function will break if fbb_ is not the first member in MonsterBuilder. In that case, some offset must be added.
213 /// This function is used exclusively for testing correctness of move operations between FlatBufferBuilders.
214 /// If MonsterBuilder had a fbb_ pointer, this hack would be unnecessary. That involves a code-generator change though.
test_only_hack_update_fbb_reference(MyGame::Example::MonsterBuilder & monsterBuilder,flatbuffers::grpc::MessageBuilder & mb)215 void test_only_hack_update_fbb_reference(MyGame::Example::MonsterBuilder &monsterBuilder,
216 flatbuffers::grpc::MessageBuilder &mb) {
217 *reinterpret_cast<flatbuffers::FlatBufferBuilder **>(&monsterBuilder) = &mb;
218 }
219
220 /// This test validates correctness of move conversion of FlatBufferBuilder to a MessageBuilder DURING
221 /// a table construction. Half of the table is constructed using FlatBufferBuilder and the other half
222 /// of the table is constructed using a MessageBuilder.
builder_move_ctor_conversion_before_finish_half_n_half_table_test()223 void builder_move_ctor_conversion_before_finish_half_n_half_table_test() {
224 for (size_t initial_size = 4 ; initial_size <= 2048; initial_size *= 2) {
225 flatbuffers::FlatBufferBuilder fbb(initial_size);
226 auto name_offset = fbb.CreateString(m1_name);
227 MyGame::Example::MonsterBuilder monsterBuilder(fbb); // starts a table in FlatBufferBuilder
228 populate_first_half(monsterBuilder, name_offset);
229 flatbuffers::grpc::MessageBuilder mb(std::move(fbb));
230 test_only_hack_update_fbb_reference(monsterBuilder, mb); // hack
231 populate_second_half(monsterBuilder);
232 mb.Finish(monsterBuilder.Finish()); // ends the table in MessageBuilder
233 TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
234 TEST_EQ_FUNC(fbb.GetSize(), 0);
235 }
236 }
237
238 /// This test populates a COMPLETE inner table before move conversion and later populates more members in the outer table.
builder_move_ctor_conversion_before_finish_test()239 void builder_move_ctor_conversion_before_finish_test() {
240 for (size_t initial_size = 4 ; initial_size <= 2048; initial_size *= 2) {
241 flatbuffers::FlatBufferBuilder fbb(initial_size);
242 auto stat_offset = CreateStat(fbb, fbb.CreateString("SomeId"), 0, 0);
243 flatbuffers::grpc::MessageBuilder mb(std::move(fbb));
244 auto monster_offset = CreateMonster(mb, 0, 150, 100, mb.CreateString(m1_name), 0, m1_color, Any_NONE, 0, 0, 0, 0, 0, 0, stat_offset);
245 mb.Finish(monster_offset);
246 TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
247 TEST_EQ_FUNC(fbb.GetSize(), 0);
248 }
249 }
250
251 /// This test validates correctness of move conversion of FlatBufferBuilder to a MessageBuilder DURING
252 /// a table construction. Half of the table is constructed using FlatBufferBuilder and the other half
253 /// of the table is constructed using a MessageBuilder.
builder_move_assign_conversion_before_finish_half_n_half_table_test()254 void builder_move_assign_conversion_before_finish_half_n_half_table_test() {
255 flatbuffers::FlatBufferBuilder fbb;
256 flatbuffers::grpc::MessageBuilder mb;
257
258 for (int i = 0;i < 5; ++i) {
259 flatbuffers::FlatBufferBuilder fbb;
260 auto name_offset = fbb.CreateString(m1_name);
261 MyGame::Example::MonsterBuilder monsterBuilder(fbb); // starts a table in FlatBufferBuilder
262 populate_first_half(monsterBuilder, name_offset);
263 mb = std::move(fbb);
264 test_only_hack_update_fbb_reference(monsterBuilder, mb); // hack
265 populate_second_half(monsterBuilder);
266 mb.Finish(monsterBuilder.Finish()); // ends the table in MessageBuilder
267 TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
268 TEST_EQ_FUNC(fbb.GetSize(), 0);
269 }
270 }
271
272 /// This test populates a COMPLETE inner table before move conversion and later populates more members in the outer table.
builder_move_assign_conversion_before_finish_test()273 void builder_move_assign_conversion_before_finish_test() {
274 flatbuffers::FlatBufferBuilder fbb;
275 flatbuffers::grpc::MessageBuilder mb;
276
277 for (int i = 0;i < 5; ++i) {
278 auto stat_offset = CreateStat(fbb, fbb.CreateString("SomeId"), 0, 0);
279 mb = std::move(fbb);
280 auto monster_offset = CreateMonster(mb, 0, 150, 100, mb.CreateString(m1_name), 0, m1_color, Any_NONE, 0, 0, 0, 0, 0, 0, stat_offset);
281 mb.Finish(monster_offset);
282 TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
283 TEST_EQ_FUNC(fbb.GetSize(), 0);
284 }
285 }
286
287 /// This test populates data, finishes the buffer, and does move conversion after.
builder_move_ctor_conversion_after_finish_test()288 void builder_move_ctor_conversion_after_finish_test() {
289 flatbuffers::FlatBufferBuilder fbb;
290 fbb.Finish(populate1(fbb));
291 flatbuffers::grpc::MessageBuilder mb(std::move(fbb));
292 TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
293 TEST_EQ_FUNC(fbb.GetSize(), 0);
294 }
295
296 /// This test populates data, finishes the buffer, and does move conversion after.
builder_move_assign_conversion_after_finish_test()297 void builder_move_assign_conversion_after_finish_test() {
298 flatbuffers::FlatBufferBuilder fbb;
299 flatbuffers::grpc::MessageBuilder mb;
300
301 for (int i = 0;i < 5; ++i) {
302 fbb.Finish(populate1(fbb));
303 mb = std::move(fbb);
304 TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
305 TEST_EQ_FUNC(fbb.GetSize(), 0);
306 }
307 }
308
message_builder_tests()309 void message_builder_tests() {
310 using flatbuffers::grpc::MessageBuilder;
311 using flatbuffers::FlatBufferBuilder;
312
313 slice_allocator_tests();
314
315 #ifndef __APPLE__
316 builder_move_ctor_conversion_before_finish_half_n_half_table_test();
317 builder_move_assign_conversion_before_finish_half_n_half_table_test();
318 #endif // __APPLE__
319 builder_move_ctor_conversion_before_finish_test();
320 builder_move_assign_conversion_before_finish_test();
321
322 builder_move_ctor_conversion_after_finish_test();
323 builder_move_assign_conversion_after_finish_test();
324
325 BuilderTests<MessageBuilder, MessageBuilder>::all_tests();
326 BuilderTests<MessageBuilder, FlatBufferBuilder>::all_tests();
327
328 BuilderReuseTestSelector tests[6] = {
329 //REUSABLE_AFTER_RELEASE, // Assertion failed: (GRPC_SLICE_IS_EMPTY(slice_))
330 //REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN, // Assertion failed: (p == GRPC_SLICE_START_PTR(slice_)
331
332 REUSABLE_AFTER_RELEASE_RAW,
333 REUSABLE_AFTER_RELEASE_MESSAGE,
334 REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN,
335 REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN
336 };
337
338 BuilderReuseTests<MessageBuilder, MessageBuilder>::run_tests(TestSelector(tests, tests+6));
339 BuilderReuseTests<MessageBuilder, FlatBufferBuilder>::run_tests(TestSelector(tests, tests+6));
340 }
341