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