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