blob: 66938f183de468e64bee5cf6f16af0423b669e71 [file] [log] [blame]
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001#include "aos/flatbuffers/static_flatbuffers.h"
2
Stephan Pleines6191f1d2024-05-30 20:44:45 -07003#include <stdint.h>
4#include <string.h>
5
6#include <algorithm>
7#include <iostream>
8#include <iterator>
9#include <memory>
10#include <span>
11#include <type_traits>
12#include <utility>
13#include <vector>
14
James Kuszmaulf5eb4682023-09-22 17:16:59 -070015#include "absl/strings/str_format.h"
16#include "absl/strings/str_join.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070017#include "absl/types/span.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070018#include "external/com_github_google_flatbuffers/src/annotated_binary_text_gen.h"
19#include "external/com_github_google_flatbuffers/src/binary_annotator.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070020#include "flatbuffers/base.h"
21#include "flatbuffers/buffer.h"
22#include "flatbuffers/flatbuffer_builder.h"
23#include "flatbuffers/stl_emulation.h"
24#include "flatbuffers/string.h"
25#include "flatbuffers/vector.h"
26#include "glog/logging.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070027#include "gtest/gtest.h"
28
29#include "aos/flatbuffers.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070030#include "aos/flatbuffers/base.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070031#include "aos/flatbuffers/builder.h"
32#include "aos/flatbuffers/interesting_schemas.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070033#include "aos/flatbuffers/static_vector.h"
34#include "aos/flatbuffers/test_dir/include_generated.h"
James Kuszmaul1e57af92023-12-20 15:34:54 -080035#include "aos/flatbuffers/test_dir/include_reflection_static.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070036#include "aos/flatbuffers/test_dir/include_static.h"
37#include "aos/flatbuffers/test_dir/type_coverage_generated.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070038#include "aos/flatbuffers/test_dir/type_coverage_static.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070039#include "aos/flatbuffers/test_generated.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070040#include "aos/flatbuffers/test_schema.h"
41#include "aos/flatbuffers/test_static.h"
42#include "aos/json_to_flatbuffer.h"
43#include "aos/testing/path.h"
44#include "aos/testing/tmpdir.h"
45#include "aos/util/file.h"
46
47namespace aos::fbs::testing {
48
49namespace {
50// Uses the binary schema to annotate a provided flatbuffer. Returns the
51// annotated flatbuffer.
52std::string AnnotateBinaries(
53 const aos::NonSizePrefixedFlatbuffer<reflection::Schema> &schema,
54 flatbuffers::span<uint8_t> binary_data) {
55 flatbuffers::BinaryAnnotator binary_annotator(
56 schema.span().data(), schema.span().size(), binary_data.data(),
57 binary_data.size());
58
59 auto annotations = binary_annotator.Annotate();
60 const std::string schema_filename =
61 aos::testing::TestTmpDir() + "/schema.bfbs";
62
63 aos::WriteFlatbufferToFile(schema_filename, schema);
64
65 flatbuffers::AnnotatedBinaryTextGenerator text_generator(
66 flatbuffers::AnnotatedBinaryTextGenerator::Options{}, annotations,
67 binary_data.data(), binary_data.size());
68
69 text_generator.Generate(aos::testing::TestTmpDir() + "/foo.bfbs",
70 schema_filename);
71
72 return aos::util::ReadFileToStringOrDie(aos::testing::TestTmpDir() +
73 "/foo.afb");
74}
75const reflection::Object *GetObjectByName(const reflection::Schema *schema,
76 std::string_view name) {
77 for (const reflection::Object *object : *schema->objects()) {
78 if (object->name()->string_view() == name) {
79 return object;
80 }
81 }
82 return nullptr;
83}
James Kuszmaul1c9693f2023-12-08 09:45:26 -080084
85// Accesses all the values in the supplied span. Used to ensure that memory
86// sanitizers can observe uninitialized memory.
87void TestMemory(std::span<uint8_t> memory) {
88 std::stringstream str;
89 internal::DebugBytes(memory, str);
90 EXPECT_LT(0u, str.view().size());
91}
James Kuszmaulf5eb4682023-09-22 17:16:59 -070092} // namespace
93
94class StaticFlatbuffersTest : public ::testing::Test {
95 protected:
96 template <typename T>
97 void VerifyJson(const std::string_view data) {
98 Builder<T> json_builder = aos::JsonToStaticFlatbuffer<T>(data);
99
100 EXPECT_EQ(data, aos::FlatbufferToJson(json_builder.AsFlatbufferSpan(),
101 {.multi_line = true}));
102 }
103 aos::FlatbufferSpan<reflection::Schema> test_schema_{TestTableSchema()};
104 aos::FlatbufferSpan<reflection::Schema> interesting_schemas_{
105 UnsupportedSchema()};
106};
107
108// Test that compiles the same code that is used by an example in
109// //aos/documentation/aos/docs/flatbuffers.md.
110TEST_F(StaticFlatbuffersTest, DocumentationExample) {
111 aos::fbs::VectorAllocator allocator;
112 Builder<TestTableStatic> builder(&allocator);
113 TestTableStatic *object = builder.get();
114 object->set_scalar(123);
115 {
116 auto vector = object->add_vector_of_scalars();
117 CHECK(vector->emplace_back(4));
118 CHECK(vector->emplace_back(5));
119 }
120 {
121 auto string = object->add_string();
122 string->SetString("Hello, World!");
123 }
124 {
125 auto vector_of_strings = object->add_vector_of_strings();
126 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
127 CHECK(sub_string->emplace_back('D'));
128 }
129 { object->set_substruct({971, 254}); }
130 {
131 auto subtable = object->add_subtable();
132 subtable->set_foo(1234);
133 }
134 {
135 auto vector = object->add_vector_of_structs();
136 CHECK(vector->emplace_back({48, 67}));
137 CHECK(vector->emplace_back({118, 148}));
138 CHECK(vector->emplace_back({971, 973}));
139 // Max vector size is three; this should fail.
140 CHECK(!vector->emplace_back({1114, 2056}));
141 }
142 {
143 auto vector = object->add_vector_of_tables();
144 auto subobject = vector->emplace_back();
145 subobject->set_foo(222);
146 }
147 {
148 auto subtable = object->add_included_table();
149 subtable->set_foo(included::TestEnum::B);
150 }
151 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
152 LOG(INFO) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
153 {.multi_line = true});
154 LOG(INFO) << AnnotateBinaries(test_schema_, builder.buffer());
155}
156
157// Test that compiles the same code that is used by an example in
158// //aos/documentation/aos/docs/flatbuffers.md showing how to convert a
159// Populate*() method for adding a subtable to a flatbuffer.
160namespace {
161flatbuffers::Offset<SubTable> PopulateOld(flatbuffers::FlatBufferBuilder *fbb) {
162 SubTable::Builder builder(*fbb);
163 builder.add_foo(1234);
164 return builder.Finish();
165}
166void PopulateStatic(SubTableStatic *subtable) { subtable->set_foo(1234); }
167} // namespace
168TEST_F(StaticFlatbuffersTest, PopulateMethodConversionExample) {
169 // Using a FlatBufferBuilder:
170 flatbuffers::FlatBufferBuilder fbb;
171 // Note: the PopulateOld() *must* be called prior to creating the builder.
172 const flatbuffers::Offset<SubTable> subtable_offset = PopulateOld(&fbb);
173 TestTable::Builder testtable_builder(fbb);
174 testtable_builder.add_subtable(subtable_offset);
175 fbb.Finish(testtable_builder.Finish());
176 aos::FlatbufferDetachedBuffer<TestTable> fbb_finished = fbb.Release();
177
178 // Using the static flatbuffer API.
179 aos::fbs::VectorAllocator allocator;
180 Builder<TestTableStatic> static_builder(&allocator);
181 PopulateStatic(CHECK_NOTNULL(static_builder.get()->add_subtable()));
182
183 // And confirm that they both contain the expected flatbuffer:
184 const std::string expected = R"json({ "subtable": { "foo": 1234 } })json";
185 EXPECT_EQ(expected, aos::FlatbufferToJson(fbb_finished));
186 EXPECT_EQ(expected, aos::FlatbufferToJson(static_builder.AsFlatbufferSpan()));
187}
188
189TEST_F(StaticFlatbuffersTest, UnsupportedSchema) {
190 const reflection::Schema *schema = &interesting_schemas_.message();
191 EXPECT_DEATH(
192 GenerateCodeForObject(
193 schema, GetObjectByName(schema, "aos.fbs.testing.TableWithUnion")),
194 "Union not supported");
195 GenerateCodeForObject(
196 schema, GetObjectByName(schema, "aos.fbs.testing.MissingVectorLength"));
197 EXPECT_DEATH(
198 GenerateCodeForObject(
199 schema,
200 GetObjectByName(schema, "aos.fbs.testing.NonIntegerVectorLength")),
201 "vector_badlength must specify a positive integer for the "
202 "static_length attribute.");
203 EXPECT_DEATH(GenerateCodeForObject(
204 schema, GetObjectByName(
205 schema, "aos.fbs.testing.NegativeVectorLength")),
206 "Field vector_badlength must have a non-negative "
207 "static_length.");
208 GenerateCodeForObject(
209 schema, GetObjectByName(schema, "aos.fbs.testing.ZeroVectorLength"));
210 GenerateCodeForObject(
211 schema, GetObjectByName(schema, "aos.fbs.testing.MissingStringLength"));
212 GenerateCodeForObject(
213 schema,
214 GetObjectByName(schema, "aos.fbs.testing.MissingSubStringLength"));
215}
216
217// Tests that we can go through and manually build up a big flatbuffer and that
218// it stays valid at all points.
219TEST_F(StaticFlatbuffersTest, ManuallyConstructFlatbuffer) {
220 {
221 aos::fbs::VectorAllocator allocator;
222 Builder<SubTableStatic> builder(&allocator);
223 SubTableStatic *object = builder.get();
224 if (!builder.AsFlatbufferSpan().Verify()) {
225 LOG(ERROR) << object->SerializationDebugString() << "\nRoot table offset "
226 << *reinterpret_cast<const uoffset_t *>(
227 builder.buffer().data())
228 << "\nraw bytes\n";
229 aos::fbs::internal::DebugBytes(builder.buffer(), std::cerr);
230 FAIL();
231 return;
232 }
233 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
234 object->set_foo(123);
235 object->set_baz(971);
236 CHECK(builder.AsFlatbufferSpan().Verify());
237 EXPECT_EQ(123, object->AsFlatbuffer().foo());
238 EXPECT_EQ(971, object->AsFlatbuffer().baz());
James Kuszmaul98c798d2024-04-24 15:58:09 -0700239 EXPECT_EQ(R"json({ "foo": 123, "baz": 971 })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700240 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800241 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700242 }
243 {
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -0800244 // aos::FixedAllocator
245 // allocator(TestTableStatic::kUnalignedBufferSize);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700246 aos::fbs::VectorAllocator allocator;
247 Builder<TestTableStatic> builder(&allocator);
248 TestTableStatic *object = builder.get();
249 const aos::fbs::testing::TestTable &fbs = object->AsFlatbuffer();
250 VLOG(1) << object->SerializationDebugString();
251 CHECK(builder.AsFlatbufferSpan().Verify());
252 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
253 {
254 ASSERT_FALSE(object->has_scalar());
255 object->set_scalar(123);
256 EXPECT_TRUE(fbs.has_scalar());
257 EXPECT_EQ(123, fbs.scalar());
258 }
259 EXPECT_EQ(R"json({ "scalar": 123 })json",
260 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
261 {
262 ASSERT_FALSE(object->has_vector_of_scalars());
263 auto vector = object->add_vector_of_scalars();
264 ASSERT_TRUE(vector->emplace_back(4));
265 ASSERT_TRUE(vector->emplace_back(5));
266 ASSERT_TRUE(object->has_vector_of_scalars());
267 ASSERT_TRUE(fbs.has_vector_of_scalars());
268 VLOG(1) << vector->SerializationDebugString();
269 EXPECT_TRUE(fbs.has_vector_of_scalars());
270 EXPECT_EQ(2u, fbs.vector_of_scalars()->size());
271 EXPECT_EQ(4, fbs.vector_of_scalars()->Get(0));
272 EXPECT_EQ(5, fbs.vector_of_scalars()->Get(1));
273 }
274 EXPECT_EQ(R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ] })json",
275 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
276 {
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700277 // Set the string to a value that is as long as its static length. An
278 // extra byte for the null character should be automatically allocated.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700279 EXPECT_FALSE(object->has_string());
280 auto string = object->add_string();
281 EXPECT_TRUE(object->has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700282 string->SetString("This is twenty chars");
283 EXPECT_EQ(20u, object->string()->size());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700284 ASSERT_TRUE(fbs.has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700285 ASSERT_EQ(20u, fbs.string()->size());
286 EXPECT_EQ("This is twenty chars", fbs.string()->string_view());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700287 // Check that we null-terminated correctly.
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700288 EXPECT_EQ(20u, strnlen(fbs.string()->c_str(), 21));
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700289 }
290 EXPECT_EQ(
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700291 R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ], "string": "This is twenty chars" })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700292 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
293 {
294 EXPECT_FALSE(object->has_vector_of_strings());
295 auto vector_of_strings = object->add_vector_of_strings();
296 EXPECT_TRUE(object->has_vector_of_strings());
297 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
298 ASSERT_TRUE(sub_string->emplace_back('D'));
299 EXPECT_TRUE(fbs.has_vector_of_strings());
300 ASSERT_EQ(1u, fbs.vector_of_strings()->size());
301 ASSERT_EQ(1u, fbs.vector_of_strings()->Get(0)->size());
302 EXPECT_EQ('D', fbs.vector_of_strings()->Get(0)->Get(0));
303 }
304 EXPECT_EQ(
305 R"json({
306 "scalar": 123,
307 "vector_of_scalars": [
308 4,
309 5
310 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700311 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700312 "vector_of_strings": [
313 "D"
314 ]
315})json",
316 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
317 {.multi_line = true}));
318 {
319 EXPECT_FALSE(object->has_substruct());
320 object->set_substruct({971, 254});
321 EXPECT_TRUE(object->has_substruct());
322 EXPECT_TRUE(fbs.has_substruct());
323 EXPECT_EQ(971, fbs.substruct()->x());
324 EXPECT_EQ(254, fbs.substruct()->y());
325 }
326 EXPECT_EQ(
327 R"json({
328 "scalar": 123,
329 "vector_of_scalars": [
330 4,
331 5
332 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700333 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700334 "vector_of_strings": [
335 "D"
336 ],
337 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700338 "x": 971,
339 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700340 }
341})json",
342 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
343 {.multi_line = true}));
344 {
345 auto subtable = object->add_subtable();
346 subtable->set_foo(1234);
347 EXPECT_TRUE(fbs.has_subtable());
348 EXPECT_EQ(1234, fbs.subtable()->foo());
349 EXPECT_FALSE(fbs.subtable()->has_baz());
350 }
351 EXPECT_EQ(
352 R"json({
353 "scalar": 123,
354 "vector_of_scalars": [
355 4,
356 5
357 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700358 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700359 "vector_of_strings": [
360 "D"
361 ],
362 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700363 "x": 971,
364 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700365 },
366 "subtable": {
367 "foo": 1234
368 }
369})json",
370 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
371 {.multi_line = true}));
372 {
373 auto vector = object->add_vector_of_structs();
374 ASSERT_TRUE(vector->emplace_back({48, 67}));
375 ASSERT_TRUE(vector->emplace_back({118, 148}));
376 ASSERT_TRUE(vector->emplace_back({971, 973}));
377 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
378 EXPECT_TRUE(fbs.has_vector_of_structs());
379 EXPECT_EQ(3u, fbs.vector_of_structs()->size());
380 EXPECT_EQ(48, fbs.vector_of_structs()->Get(0)->x());
381 EXPECT_EQ(67, fbs.vector_of_structs()->Get(0)->y());
382 EXPECT_EQ(118, fbs.vector_of_structs()->Get(1)->x());
383 EXPECT_EQ(object->vector_of_structs()->at(1).x(),
384 fbs.vector_of_structs()->Get(1)->x());
385 EXPECT_EQ((*object->vector_of_structs())[1].x(),
386 fbs.vector_of_structs()->Get(1)->x());
387 EXPECT_EQ(148, fbs.vector_of_structs()->Get(1)->y());
388 EXPECT_EQ(971, fbs.vector_of_structs()->Get(2)->x());
389 EXPECT_EQ(973, fbs.vector_of_structs()->Get(2)->y());
390 }
391 EXPECT_EQ(
392 R"json({
393 "scalar": 123,
394 "vector_of_scalars": [
395 4,
396 5
397 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700398 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700399 "vector_of_strings": [
400 "D"
401 ],
402 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700403 "x": 971,
404 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700405 },
406 "subtable": {
407 "foo": 1234
408 },
409 "vector_of_structs": [
410 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700411 "x": 48,
412 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700413 },
414 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700415 "x": 118,
416 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700417 },
418 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700419 "x": 971,
420 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700421 }
422 ]
423})json",
424 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
425 {.multi_line = true}));
426 {
427 EXPECT_FALSE(object->has_vector_of_tables());
428 auto vector = object->add_vector_of_tables();
429 EXPECT_TRUE(object->has_vector_of_tables());
430 auto subobject = vector->emplace_back();
431 subobject->set_foo(222);
432 EXPECT_TRUE(fbs.has_vector_of_tables());
433 EXPECT_EQ(1u, fbs.vector_of_tables()->size());
434 EXPECT_EQ(222, fbs.vector_of_tables()->Get(0)->foo());
435 EXPECT_EQ(object->vector_of_tables()->at(0).foo(),
436 fbs.vector_of_tables()->Get(0)->foo());
437 }
438 EXPECT_EQ(
439 R"json({
440 "scalar": 123,
441 "vector_of_scalars": [
442 4,
443 5
444 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700445 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700446 "vector_of_strings": [
447 "D"
448 ],
449 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700450 "x": 971,
451 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700452 },
453 "subtable": {
454 "foo": 1234
455 },
456 "vector_of_structs": [
457 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700458 "x": 48,
459 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700460 },
461 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700462 "x": 118,
463 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700464 },
465 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700466 "x": 971,
467 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700468 }
469 ],
470 "vector_of_tables": [
471 {
472 "foo": 222
473 }
474 ]
475})json",
476 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
477 {.multi_line = true}));
478 {
479 EXPECT_FALSE(object->has_included_table());
480 auto subtable = object->add_included_table();
481 EXPECT_TRUE(object->has_included_table());
482 subtable->set_foo(included::TestEnum::B);
483 ASSERT_TRUE(fbs.has_included_table());
484 ASSERT_TRUE(fbs.included_table()->has_foo());
485 EXPECT_EQ(included::TestEnum::B, fbs.included_table()->foo());
486 }
487 EXPECT_EQ(
488 R"json({
489 "scalar": 123,
490 "vector_of_scalars": [
491 4,
492 5
493 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700494 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700495 "vector_of_strings": [
496 "D"
497 ],
498 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700499 "x": 971,
500 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700501 },
502 "subtable": {
503 "foo": 1234
504 },
505 "vector_of_structs": [
506 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700507 "x": 48,
508 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700509 },
510 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700511 "x": 118,
512 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700513 },
514 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700515 "x": 971,
516 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700517 }
518 ],
519 "vector_of_tables": [
520 {
521 "foo": 222
522 }
523 ],
524 "included_table": {
525 "foo": "B"
526 }
527})json",
528 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
529 {.multi_line = true}));
530 {
531 auto aligned_vector = object->add_vector_aligned();
532 ASSERT_EQ(64,
533 std::remove_reference<decltype(*aligned_vector)>::type::kAlign);
534 ASSERT_EQ(64, TestTableStatic::kAlign);
535 ASSERT_TRUE(aligned_vector->emplace_back(444));
536 EXPECT_TRUE(fbs.has_vector_aligned());
537 EXPECT_EQ(1u, fbs.vector_aligned()->size());
538 EXPECT_EQ(0u,
539 reinterpret_cast<size_t>(fbs.vector_aligned()->data()) % 64);
540 EXPECT_EQ(444, fbs.vector_aligned()->Get(0));
541 }
542 VLOG(1) << object->SerializationDebugString();
543 CHECK(builder.AsFlatbufferSpan().Verify());
544 const std::string expected_contents =
545 R"json({
546 "scalar": 123,
547 "vector_of_scalars": [
548 4,
549 5
550 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700551 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700552 "vector_of_strings": [
553 "D"
554 ],
555 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700556 "x": 971,
557 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700558 },
559 "subtable": {
560 "foo": 1234
561 },
562 "vector_aligned": [
563 444
564 ],
565 "vector_of_structs": [
566 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700567 "x": 48,
568 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700569 },
570 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700571 "x": 118,
572 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700573 },
574 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700575 "x": 971,
576 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700577 }
578 ],
579 "vector_of_tables": [
580 {
581 "foo": 222
582 }
583 ],
584 "included_table": {
585 "foo": "B"
586 }
587})json";
588 EXPECT_EQ(expected_contents,
589 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
590 {.multi_line = true}));
591 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
592 VerifyJson<TestTableStatic>(expected_contents);
593 {
594 auto aligned_vector = object->mutable_vector_aligned();
595 ASSERT_TRUE(aligned_vector->reserve(100));
596 EXPECT_EQ(100, aligned_vector->capacity());
597 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify())
598 << aligned_vector->SerializationDebugString();
599 EXPECT_EQ(expected_contents,
600 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
601 {.multi_line = true}));
602 std::vector<int> scalars;
603 scalars.push_back(aligned_vector->at(0));
604 while (aligned_vector->size() < 100u) {
605 scalars.push_back(aligned_vector->size());
606 CHECK(aligned_vector->emplace_back(aligned_vector->size()));
607 }
608 VLOG(1) << aligned_vector->SerializationDebugString();
609 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
610 EXPECT_EQ(absl::StrFormat(
611 R"json({
612 "scalar": 123,
613 "vector_of_scalars": [
614 4,
615 5
616 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700617 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700618 "vector_of_strings": [
619 "D"
620 ],
621 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700622 "x": 971,
623 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700624 },
625 "subtable": {
626 "foo": 1234
627 },
628 "vector_aligned": [
629 %s
630 ],
631 "vector_of_structs": [
632 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700633 "x": 48,
634 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700635 },
636 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700637 "x": 118,
638 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700639 },
640 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700641 "x": 971,
642 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700643 }
644 ],
645 "vector_of_tables": [
646 {
647 "foo": 222
648 }
649 ],
650 "included_table": {
651 "foo": "B"
652 }
653})json",
654 absl::StrJoin(scalars, ",\n ")),
655 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
656 {.multi_line = true}));
657 }
658
659 {
660 auto unspecified_vector = object->add_unspecified_length_vector();
661 ASSERT_NE(nullptr, unspecified_vector);
662 ASSERT_EQ(0, unspecified_vector->capacity());
663 ASSERT_FALSE(unspecified_vector->emplace_back(0));
664 ASSERT_TRUE(unspecified_vector->reserve(2));
665 ASSERT_TRUE(unspecified_vector->emplace_back(1));
666 ASSERT_TRUE(unspecified_vector->emplace_back(2));
667 ASSERT_FALSE(unspecified_vector->emplace_back(3));
668 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
669 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800670 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700671 }
672}
673
674// Tests that field clearing (and subsequent resetting) works properly.
675TEST_F(StaticFlatbuffersTest, ClearFields) {
676 aos::fbs::VectorAllocator allocator;
677 Builder<TestTableStatic> builder(&allocator);
678 TestTableStatic *object = builder.get();
679 // For each field, we will confirm the following:
680 // * Clearing a non-existent field causes no issues.
681 // * We can set a field, clear it, and have it not be present.
682 // * We can set the field again afterwards.
683 {
684 object->clear_scalar();
685 ASSERT_TRUE(builder.Verify());
686 object->set_scalar(123);
687 EXPECT_EQ(123, object->AsFlatbuffer().scalar());
688 object->clear_scalar();
689 ASSERT_TRUE(builder.Verify());
690 EXPECT_FALSE(object->has_scalar());
691 object->set_scalar(456);
692 EXPECT_EQ(456, object->AsFlatbuffer().scalar());
693 }
694 {
695 object->clear_vector_of_scalars();
696 ASSERT_TRUE(builder.Verify());
697 EXPECT_FALSE(object->has_vector_of_scalars());
698 auto vector = object->add_vector_of_scalars();
699 ASSERT_TRUE(vector->emplace_back(4));
700 ASSERT_TRUE(vector->emplace_back(5));
701 ASSERT_TRUE(vector->emplace_back(6));
702 // Deliberately force a resize of the vector to ensure that we can exercise
703 // what happens if we clear a non-standard size field.
704 ASSERT_FALSE(vector->emplace_back(7));
705 ASSERT_TRUE(vector->reserve(4));
706 ASSERT_TRUE(vector->emplace_back(7));
707 EXPECT_EQ(
708 R"json({
709 "scalar": 456,
710 "vector_of_scalars": [
711 4,
712 5,
713 6,
714 7
715 ]
716})json",
717 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
718 {.multi_line = true}));
719 ASSERT_TRUE(builder.Verify());
720 object->clear_vector_of_scalars();
721 ASSERT_TRUE(builder.Verify());
722 ASSERT_FALSE(object->has_vector_of_scalars())
723 << aos::FlatbufferToJson(builder.AsFlatbufferSpan());
724 vector = CHECK_NOTNULL(object->add_vector_of_scalars());
725 ASSERT_TRUE(builder.Verify());
726 EXPECT_EQ(0u, object->AsFlatbuffer().vector_of_scalars()->size());
727 ASSERT_TRUE(vector->emplace_back(9));
728 ASSERT_TRUE(vector->emplace_back(7));
729 ASSERT_TRUE(vector->emplace_back(1));
730 // This vector has no knowledge of the past resizing; it should fail to add
731 // an extra number.
732 ASSERT_FALSE(vector->emplace_back(7));
733 }
734 {
735 object->clear_substruct();
736 ASSERT_TRUE(builder.Verify());
737 EXPECT_FALSE(object->has_substruct());
738 object->set_substruct(SubStruct{2, 3});
739 EXPECT_EQ(
740 R"json({
741 "scalar": 456,
742 "vector_of_scalars": [
743 9,
744 7,
745 1
746 ],
747 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700748 "x": 2,
749 "y": 3
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700750 }
751})json",
752 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
753 {.multi_line = true}));
754 object->clear_substruct();
755 ASSERT_TRUE(builder.Verify());
756 EXPECT_FALSE(object->has_substruct());
757 object->set_substruct(SubStruct{4, 5});
758 EXPECT_EQ(
759 R"json({
760 "scalar": 456,
761 "vector_of_scalars": [
762 9,
763 7,
764 1
765 ],
766 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700767 "x": 4,
768 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700769 }
770})json",
771 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
772 {.multi_line = true}));
773 }
774 {
775 object->clear_subtable();
776 ASSERT_TRUE(builder.Verify());
777 EXPECT_FALSE(object->has_subtable());
778 auto subtable = CHECK_NOTNULL(object->add_subtable());
779 subtable->set_baz(9.71);
780 EXPECT_EQ(
781 R"json({
782 "scalar": 456,
783 "vector_of_scalars": [
784 9,
785 7,
786 1
787 ],
788 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700789 "x": 4,
790 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700791 },
792 "subtable": {
793 "baz": 9.71
794 }
795})json",
796 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
797 {.multi_line = true}));
798 object->clear_subtable();
799 ASSERT_TRUE(builder.Verify());
800 EXPECT_FALSE(object->has_subtable());
801 subtable = CHECK_NOTNULL(object->add_subtable());
802 subtable->set_baz(16.78);
803 EXPECT_EQ(
804 R"json({
805 "scalar": 456,
806 "vector_of_scalars": [
807 9,
808 7,
809 1
810 ],
811 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700812 "x": 4,
813 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700814 },
815 "subtable": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700816 "baz": 16.78
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700817 }
818})json",
819 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
820 {.multi_line = true}));
821 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800822 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700823}
824
825// Try to cover ~all supported scalar/flatbuffer types using JSON convenience
826// functions.
827TEST_F(StaticFlatbuffersTest, FlatbufferTypeCoverage) {
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800828 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700829 std::string populated_config =
830 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
831 "aos/flatbuffers/test_dir/type_coverage.json"));
832 // Get rid of a pesky new line.
833 populated_config = populated_config.substr(0, populated_config.size() - 1);
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800834 VerifyJson<aos::testing::ConfigurationStatic>(populated_config);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700835
836 // And now play around with mutating the buffer.
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800837 Builder<aos::testing::ConfigurationStatic> builder =
838 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700839 populated_config);
840 ASSERT_TRUE(builder.Verify());
841 builder.get()->clear_foo_float();
842 ASSERT_TRUE(builder.Verify());
843 ASSERT_FALSE(builder.get()->AsFlatbuffer().has_foo_float());
844 builder.get()->set_foo_float(1.111);
845 ASSERT_TRUE(builder.Verify());
846 ASSERT_FLOAT_EQ(1.111, builder.get()->AsFlatbuffer().foo_float());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800847 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700848}
849
James Kuszmaula75cd7c2023-12-07 15:52:51 -0800850TEST_F(StaticFlatbuffersTest, MinimallyAlignedTable) {
851 VerifyJson<MinimallyAlignedTableStatic>("{\n \"field\": 123\n}");
852 static_assert(4u == alignof(uoffset_t),
853 "The alignment of a uoffset_t is expected to be 4.");
854 ASSERT_EQ(alignof(uoffset_t), MinimallyAlignedTableStatic::kAlign)
855 << "No table should have an alignment of less than the alignment of the "
856 "table's root offset.";
857}
858
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700859// Confirm that we can use the SpanAllocator with a span that provides exactly
860// the required buffer size.
861TEST_F(StaticFlatbuffersTest, ExactSizeSpanAllocator) {
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800862 uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
863 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700864 Builder<TestTableStatic> builder(&allocator);
865 TestTableStatic *object = builder.get();
866 object->set_scalar(123);
867 {
868 auto vector = object->add_vector_of_scalars();
869 ASSERT_TRUE(vector->emplace_back(4));
870 ASSERT_TRUE(vector->emplace_back(5));
871 }
872 {
873 auto string = object->add_string();
874 string->SetString("Hello, World!");
875 }
876 {
877 auto vector_of_strings = object->add_vector_of_strings();
878 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
879 ASSERT_TRUE(sub_string->emplace_back('D'));
880 }
881 { object->set_substruct({971, 254}); }
882 {
883 auto subtable = object->add_subtable();
884 subtable->set_foo(1234);
885 }
886 {
887 auto vector = object->add_vector_of_structs();
888 ASSERT_TRUE(vector->emplace_back({48, 67}));
889 ASSERT_TRUE(vector->emplace_back({118, 148}));
890 ASSERT_TRUE(vector->emplace_back({971, 973}));
891 // Max vector size is three; this should fail.
892 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
893 // We don't have any extra space available.
894 ASSERT_FALSE(vector->reserve(4));
895 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
896 }
897 {
898 auto vector = object->add_vector_of_tables();
899 auto subobject = vector->emplace_back();
900 subobject->set_foo(222);
901 }
902 {
903 auto subtable = object->add_included_table();
904 subtable->set_foo(included::TestEnum::B);
905 }
906 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
907 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
908 {.multi_line = true});
909 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800910 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700911}
912
913// Test that when we provide too small of a span to the Builder that it
914// correctly fails.
915TEST_F(StaticFlatbuffersTest, TooSmallSpanAllocator) {
916 std::vector<uint8_t> buffer;
917 buffer.resize(10, 0);
918 aos::fbs::SpanAllocator allocator({buffer.data(), buffer.size()});
919 EXPECT_DEATH(Builder<TestTableStatic>{&allocator}, "Failed to allocate");
920}
921
922// Verify that if we create a span with extra headroom that that lets us
923// dynamically alter the size of vectors in the flatbuffers.
924TEST_F(StaticFlatbuffersTest, ExtraLargeSpanAllocator) {
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800925 uint8_t buffer[Builder<TestTableStatic>::kBufferSize + 10000];
926 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700927 Builder<TestTableStatic> builder(&allocator);
928 TestTableStatic *object = builder.get();
929 {
930 auto vector = object->add_unspecified_length_vector();
931 // Confirm that the vector does indeed start out at zero length.
932 ASSERT_FALSE(vector->emplace_back(4));
933 ASSERT_TRUE(vector->reserve(9000));
934 vector->resize(256);
935 for (size_t index = 0; index < 256; ++index) {
936 vector->at(index) = static_cast<uint8_t>(index);
937 }
938 }
939 ASSERT_EQ(256, object->AsFlatbuffer().unspecified_length_vector()->size());
940 size_t expected = 0;
941 for (const uint8_t value :
942 *object->AsFlatbuffer().unspecified_length_vector()) {
943 EXPECT_EQ(expected++, value);
944 }
James Kuszmaul22448052023-12-14 15:55:14 -0800945 expected = 0;
946 for (const uint8_t value : *object->unspecified_length_vector()) {
947 EXPECT_EQ(expected++, value);
948 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800949 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700950}
James Kuszmaul22448052023-12-14 15:55:14 -0800951
952// Tests that the iterators on the Vector type work.
953TEST_F(StaticFlatbuffersTest, IteratorTest) {
954 Builder<TestTableStatic> builder(std::make_unique<VectorAllocator>());
955 {
956 auto vector = builder->add_unspecified_length_vector();
957 ASSERT_TRUE(vector->reserve(9000));
958 vector->resize(256);
959 uint8_t set_value = 0;
960 for (uint8_t &destination : *vector) {
961 destination = set_value;
962 ++set_value;
963 }
964 uint8_t expected = 0;
965 for (const uint8_t value : *builder->unspecified_length_vector()) {
966 EXPECT_EQ(expected, value);
967 ++expected;
968 }
969 // Exercise some of the random access iterator functionality to ensure that
970 // we have it implemented.
971 auto begin_it = vector->begin();
972 EXPECT_EQ(begin_it + 256, vector->end());
973 EXPECT_EQ(7, *(begin_it + 7));
974 EXPECT_EQ(255, *(vector->end() - 1));
975 EXPECT_EQ(256, vector->end() - vector->begin());
976 EXPECT_EQ(-256, vector->begin() - vector->end());
977 static_assert(std::random_access_iterator<decltype(vector->begin())>,
978 "The vector iterator does not meet the requirements of a "
979 "random access iterator.");
980 }
981 {
982 auto vector = builder->add_vector_of_structs();
983 vector->resize(3);
984 double set_value = 0;
985 for (SubStruct &destination : *vector) {
986 destination.mutate_x(set_value);
987 destination.mutate_y(-set_value);
988 set_value += 1.0;
989 }
990 double expected = 0;
991 for (const SubStruct &value : *builder->vector_of_structs()) {
992 EXPECT_EQ(expected, value.x());
993 EXPECT_EQ(-expected, value.y());
994 expected += 1.0;
995 }
996 static_assert(std::random_access_iterator<decltype(vector->begin())>,
997 "The vector iterator does not meet the requirements of a "
998 "random access iterator.");
999 }
1000 {
1001 auto vector = builder->add_vector_of_tables();
1002 vector->resize(3);
1003 int set_value = 0;
1004 for (SubTableStatic &destination : *vector) {
1005 destination.set_foo(set_value);
1006 set_value += 1;
1007 }
1008 int expected = 0;
1009 for (const SubTableStatic &value : *builder->vector_of_tables()) {
1010 EXPECT_EQ(expected, value.foo());
1011 EXPECT_FALSE(value.has_baz());
1012 expected += 1;
1013 }
1014 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1015 "The vector iterator does not meet the requirements of a "
1016 "random access iterator.");
1017 }
1018}
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001019
1020// Confirm that we can use the FixedStackAllocator
1021TEST_F(StaticFlatbuffersTest, FixedStackAllocator) {
1022 aos::fbs::FixedStackAllocator<Builder<TestTableStatic>::kBufferSize>
1023 allocator;
1024 Builder<TestTableStatic> builder(&allocator);
1025 TestTableStatic *object = builder.get();
1026 object->set_scalar(123);
1027 {
1028 auto vector = object->add_vector_of_scalars();
1029 ASSERT_TRUE(vector->emplace_back(4));
1030 ASSERT_TRUE(vector->emplace_back(5));
1031 }
1032 {
1033 auto string = object->add_string();
1034 string->SetString("Hello, World!");
1035 }
1036 {
1037 auto vector_of_strings = object->add_vector_of_strings();
1038 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
1039 ASSERT_TRUE(sub_string->emplace_back('D'));
1040 }
1041 { object->set_substruct({971, 254}); }
1042 {
1043 auto subtable = object->add_subtable();
1044 subtable->set_foo(1234);
1045 }
1046 {
1047 auto vector = object->add_vector_of_structs();
1048 ASSERT_TRUE(vector->emplace_back({48, 67}));
1049 ASSERT_TRUE(vector->emplace_back({118, 148}));
1050 ASSERT_TRUE(vector->emplace_back({971, 973}));
1051 // Max vector size is three; this should fail.
1052 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1053 // We don't have any extra space available.
1054 ASSERT_FALSE(vector->reserve(4));
1055 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1056 }
1057 {
1058 auto vector = object->add_vector_of_tables();
1059 auto subobject = vector->emplace_back();
1060 subobject->set_foo(222);
1061 }
1062 {
1063 auto subtable = object->add_included_table();
1064 subtable->set_foo(included::TestEnum::B);
1065 }
1066 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1067 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1068 {.multi_line = true});
1069 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1070 TestMemory(builder.buffer());
1071}
1072
James Kuszmaul6be41022023-12-20 11:55:28 -08001073// Uses a small example to manually verify that we can copy from the flatbuffer
1074// object API.
1075TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
1076 aos::fbs::testing::TestTableT object_t;
1077 object_t.scalar = 971;
1078 object_t.vector_of_strings.push_back("971");
1079 object_t.vector_of_structs.push_back({1, 2});
1080 object_t.subtable = std::make_unique<SubTableT>();
1081 aos::fbs::VectorAllocator allocator;
1082 Builder<TestTableStatic> builder(&allocator);
1083 ASSERT_TRUE(builder->FromFlatbuffer(object_t));
1084 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1085 // Note that vectors and strings get set to zero-length, but present, values.
1086 EXPECT_EQ(
1087 "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
1088 "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
James Kuszmaul98c798d2024-04-24 15:58:09 -07001089 "\"baz\": 0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
1090 "\"x\": 1, \"y\": 2 } ], \"vector_of_tables\": [ ], "
James Kuszmaul6be41022023-12-20 11:55:28 -08001091 "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
1092 "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
1093 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
1094}
1095
1096// More completely covers our object API copying by comparing the flatbuffer
1097// Pack() methods to our FromFlatbuffer() methods.
1098TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
1099 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
1100 std::string populated_config =
1101 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
1102 "aos/flatbuffers/test_dir/type_coverage.json"));
1103 Builder<aos::testing::ConfigurationStatic> json_builder =
1104 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
1105 populated_config);
1106 aos::testing::ConfigurationT object_t;
1107 json_builder->AsFlatbuffer().UnPackTo(&object_t);
1108
1109 Builder<aos::testing::ConfigurationStatic> from_object_static;
1110 ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
1111 flatbuffers::FlatBufferBuilder fbb;
1112 fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
1113 aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
1114 fbb.Release();
1115 EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
1116 aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
1117}
1118
James Kuszmaul1e57af92023-12-20 15:34:54 -08001119// Tests that we can build code that uses the reflection types.
1120TEST_F(StaticFlatbuffersTest, IncludeReflectionTypes) {
1121 VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
1122}
1123
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001124// Tests that we can use the move constructor on a Builder.
1125TEST_F(StaticFlatbuffersTest, BuilderMoveConstructor) {
1126 uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
1127 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
1128 Builder<TestTableStatic> builder_from(&allocator);
1129 Builder<TestTableStatic> builder(std::move(builder_from));
1130 TestTableStatic *object = builder.get();
1131 object->set_scalar(123);
1132 {
1133 auto vector = object->add_vector_of_scalars();
1134 ASSERT_TRUE(vector->emplace_back(4));
1135 ASSERT_TRUE(vector->emplace_back(5));
1136 }
1137 {
1138 auto string = object->add_string();
1139 string->SetString("Hello, World!");
1140 }
1141 {
1142 auto vector_of_strings = object->add_vector_of_strings();
1143 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
1144 ASSERT_TRUE(sub_string->emplace_back('D'));
1145 }
1146 { object->set_substruct({971, 254}); }
1147 {
1148 auto subtable = object->add_subtable();
1149 subtable->set_foo(1234);
1150 }
1151 {
1152 auto vector = object->add_vector_of_structs();
1153 ASSERT_TRUE(vector->emplace_back({48, 67}));
1154 ASSERT_TRUE(vector->emplace_back({118, 148}));
1155 ASSERT_TRUE(vector->emplace_back({971, 973}));
1156 // Max vector size is three; this should fail.
1157 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1158 // We don't have any extra space available.
1159 ASSERT_FALSE(vector->reserve(4));
1160 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1161 }
1162 {
1163 auto vector = object->add_vector_of_tables();
1164 auto subobject = vector->emplace_back();
1165 subobject->set_foo(222);
1166 }
1167 {
1168 auto subtable = object->add_included_table();
1169 subtable->set_foo(included::TestEnum::B);
1170 }
1171 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1172 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1173 {.multi_line = true});
1174 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1175 TestMemory(builder.buffer());
1176}
1177
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001178} // namespace aos::fbs::testing