• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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