blob: 4a8b9e9117fd8b9f21c561584ccd6d375524bb3f [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) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700111 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700112 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.
Austin Schuh02e0d772024-05-30 16:41:06 -0700179 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700180 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 {
Austin Schuh02e0d772024-05-30 16:41:06 -0700221 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700222 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 {
Austin Schuh02e0d772024-05-30 16:41:06 -0700244 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700245 Builder<TestTableStatic> builder(&allocator);
246 TestTableStatic *object = builder.get();
247 const aos::fbs::testing::TestTable &fbs = object->AsFlatbuffer();
248 VLOG(1) << object->SerializationDebugString();
249 CHECK(builder.AsFlatbufferSpan().Verify());
250 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
251 {
252 ASSERT_FALSE(object->has_scalar());
253 object->set_scalar(123);
254 EXPECT_TRUE(fbs.has_scalar());
255 EXPECT_EQ(123, fbs.scalar());
256 }
257 EXPECT_EQ(R"json({ "scalar": 123 })json",
258 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
259 {
260 ASSERT_FALSE(object->has_vector_of_scalars());
261 auto vector = object->add_vector_of_scalars();
262 ASSERT_TRUE(vector->emplace_back(4));
263 ASSERT_TRUE(vector->emplace_back(5));
264 ASSERT_TRUE(object->has_vector_of_scalars());
265 ASSERT_TRUE(fbs.has_vector_of_scalars());
266 VLOG(1) << vector->SerializationDebugString();
267 EXPECT_TRUE(fbs.has_vector_of_scalars());
268 EXPECT_EQ(2u, fbs.vector_of_scalars()->size());
269 EXPECT_EQ(4, fbs.vector_of_scalars()->Get(0));
270 EXPECT_EQ(5, fbs.vector_of_scalars()->Get(1));
271 }
272 EXPECT_EQ(R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ] })json",
273 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
274 {
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700275 // Set the string to a value that is as long as its static length. An
276 // extra byte for the null character should be automatically allocated.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700277 EXPECT_FALSE(object->has_string());
278 auto string = object->add_string();
279 EXPECT_TRUE(object->has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700280 string->SetString("This is twenty chars");
281 EXPECT_EQ(20u, object->string()->size());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700282 ASSERT_TRUE(fbs.has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700283 ASSERT_EQ(20u, fbs.string()->size());
284 EXPECT_EQ("This is twenty chars", fbs.string()->string_view());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700285 // Check that we null-terminated correctly.
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700286 EXPECT_EQ(20u, strnlen(fbs.string()->c_str(), 21));
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700287 }
288 EXPECT_EQ(
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700289 R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ], "string": "This is twenty chars" })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700290 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
291 {
292 EXPECT_FALSE(object->has_vector_of_strings());
293 auto vector_of_strings = object->add_vector_of_strings();
294 EXPECT_TRUE(object->has_vector_of_strings());
295 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
296 ASSERT_TRUE(sub_string->emplace_back('D'));
297 EXPECT_TRUE(fbs.has_vector_of_strings());
298 ASSERT_EQ(1u, fbs.vector_of_strings()->size());
299 ASSERT_EQ(1u, fbs.vector_of_strings()->Get(0)->size());
300 EXPECT_EQ('D', fbs.vector_of_strings()->Get(0)->Get(0));
301 }
302 EXPECT_EQ(
303 R"json({
304 "scalar": 123,
305 "vector_of_scalars": [
306 4,
307 5
308 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700309 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700310 "vector_of_strings": [
311 "D"
312 ]
313})json",
314 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
315 {.multi_line = true}));
316 {
317 EXPECT_FALSE(object->has_substruct());
318 object->set_substruct({971, 254});
319 EXPECT_TRUE(object->has_substruct());
320 EXPECT_TRUE(fbs.has_substruct());
321 EXPECT_EQ(971, fbs.substruct()->x());
322 EXPECT_EQ(254, fbs.substruct()->y());
323 }
324 EXPECT_EQ(
325 R"json({
326 "scalar": 123,
327 "vector_of_scalars": [
328 4,
329 5
330 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700331 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700332 "vector_of_strings": [
333 "D"
334 ],
335 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700336 "x": 971,
337 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700338 }
339})json",
340 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
341 {.multi_line = true}));
342 {
343 auto subtable = object->add_subtable();
344 subtable->set_foo(1234);
345 EXPECT_TRUE(fbs.has_subtable());
346 EXPECT_EQ(1234, fbs.subtable()->foo());
347 EXPECT_FALSE(fbs.subtable()->has_baz());
348 }
349 EXPECT_EQ(
350 R"json({
351 "scalar": 123,
352 "vector_of_scalars": [
353 4,
354 5
355 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700356 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700357 "vector_of_strings": [
358 "D"
359 ],
360 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700361 "x": 971,
362 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700363 },
364 "subtable": {
365 "foo": 1234
366 }
367})json",
368 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
369 {.multi_line = true}));
370 {
371 auto vector = object->add_vector_of_structs();
372 ASSERT_TRUE(vector->emplace_back({48, 67}));
373 ASSERT_TRUE(vector->emplace_back({118, 148}));
374 ASSERT_TRUE(vector->emplace_back({971, 973}));
375 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
376 EXPECT_TRUE(fbs.has_vector_of_structs());
377 EXPECT_EQ(3u, fbs.vector_of_structs()->size());
378 EXPECT_EQ(48, fbs.vector_of_structs()->Get(0)->x());
379 EXPECT_EQ(67, fbs.vector_of_structs()->Get(0)->y());
380 EXPECT_EQ(118, fbs.vector_of_structs()->Get(1)->x());
381 EXPECT_EQ(object->vector_of_structs()->at(1).x(),
382 fbs.vector_of_structs()->Get(1)->x());
383 EXPECT_EQ((*object->vector_of_structs())[1].x(),
384 fbs.vector_of_structs()->Get(1)->x());
385 EXPECT_EQ(148, fbs.vector_of_structs()->Get(1)->y());
386 EXPECT_EQ(971, fbs.vector_of_structs()->Get(2)->x());
387 EXPECT_EQ(973, fbs.vector_of_structs()->Get(2)->y());
388 }
389 EXPECT_EQ(
390 R"json({
391 "scalar": 123,
392 "vector_of_scalars": [
393 4,
394 5
395 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700396 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700397 "vector_of_strings": [
398 "D"
399 ],
400 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700401 "x": 971,
402 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700403 },
404 "subtable": {
405 "foo": 1234
406 },
407 "vector_of_structs": [
408 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700409 "x": 48,
410 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700411 },
412 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700413 "x": 118,
414 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700415 },
416 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700417 "x": 971,
418 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700419 }
420 ]
421})json",
422 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
423 {.multi_line = true}));
424 {
425 EXPECT_FALSE(object->has_vector_of_tables());
426 auto vector = object->add_vector_of_tables();
427 EXPECT_TRUE(object->has_vector_of_tables());
428 auto subobject = vector->emplace_back();
429 subobject->set_foo(222);
430 EXPECT_TRUE(fbs.has_vector_of_tables());
431 EXPECT_EQ(1u, fbs.vector_of_tables()->size());
432 EXPECT_EQ(222, fbs.vector_of_tables()->Get(0)->foo());
433 EXPECT_EQ(object->vector_of_tables()->at(0).foo(),
434 fbs.vector_of_tables()->Get(0)->foo());
435 }
436 EXPECT_EQ(
437 R"json({
438 "scalar": 123,
439 "vector_of_scalars": [
440 4,
441 5
442 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700443 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700444 "vector_of_strings": [
445 "D"
446 ],
447 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700448 "x": 971,
449 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700450 },
451 "subtable": {
452 "foo": 1234
453 },
454 "vector_of_structs": [
455 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700456 "x": 48,
457 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700458 },
459 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700460 "x": 118,
461 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700462 },
463 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700464 "x": 971,
465 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700466 }
467 ],
468 "vector_of_tables": [
469 {
470 "foo": 222
471 }
472 ]
473})json",
474 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
475 {.multi_line = true}));
476 {
477 EXPECT_FALSE(object->has_included_table());
478 auto subtable = object->add_included_table();
479 EXPECT_TRUE(object->has_included_table());
480 subtable->set_foo(included::TestEnum::B);
481 ASSERT_TRUE(fbs.has_included_table());
482 ASSERT_TRUE(fbs.included_table()->has_foo());
483 EXPECT_EQ(included::TestEnum::B, fbs.included_table()->foo());
484 }
485 EXPECT_EQ(
486 R"json({
487 "scalar": 123,
488 "vector_of_scalars": [
489 4,
490 5
491 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700492 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700493 "vector_of_strings": [
494 "D"
495 ],
496 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700497 "x": 971,
498 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700499 },
500 "subtable": {
501 "foo": 1234
502 },
503 "vector_of_structs": [
504 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700505 "x": 48,
506 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700507 },
508 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700509 "x": 118,
510 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700511 },
512 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700513 "x": 971,
514 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700515 }
516 ],
517 "vector_of_tables": [
518 {
519 "foo": 222
520 }
521 ],
522 "included_table": {
523 "foo": "B"
524 }
525})json",
526 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
527 {.multi_line = true}));
528 {
529 auto aligned_vector = object->add_vector_aligned();
530 ASSERT_EQ(64,
531 std::remove_reference<decltype(*aligned_vector)>::type::kAlign);
532 ASSERT_EQ(64, TestTableStatic::kAlign);
533 ASSERT_TRUE(aligned_vector->emplace_back(444));
534 EXPECT_TRUE(fbs.has_vector_aligned());
535 EXPECT_EQ(1u, fbs.vector_aligned()->size());
536 EXPECT_EQ(0u,
537 reinterpret_cast<size_t>(fbs.vector_aligned()->data()) % 64);
538 EXPECT_EQ(444, fbs.vector_aligned()->Get(0));
539 }
540 VLOG(1) << object->SerializationDebugString();
541 CHECK(builder.AsFlatbufferSpan().Verify());
542 const std::string expected_contents =
543 R"json({
544 "scalar": 123,
545 "vector_of_scalars": [
546 4,
547 5
548 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700549 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700550 "vector_of_strings": [
551 "D"
552 ],
553 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700554 "x": 971,
555 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700556 },
557 "subtable": {
558 "foo": 1234
559 },
560 "vector_aligned": [
561 444
562 ],
563 "vector_of_structs": [
564 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700565 "x": 48,
566 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700567 },
568 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700569 "x": 118,
570 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700571 },
572 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700573 "x": 971,
574 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700575 }
576 ],
577 "vector_of_tables": [
578 {
579 "foo": 222
580 }
581 ],
582 "included_table": {
583 "foo": "B"
584 }
585})json";
586 EXPECT_EQ(expected_contents,
587 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
588 {.multi_line = true}));
589 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
590 VerifyJson<TestTableStatic>(expected_contents);
591 {
592 auto aligned_vector = object->mutable_vector_aligned();
593 ASSERT_TRUE(aligned_vector->reserve(100));
Austin Schuhf8440852024-05-31 10:46:50 -0700594
595 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
596 // Since the allocator is going to allocate in blocks of 64, we end up
597 // with more capacity than we asked for. Better to have it than to leave
598 // it as unusable padding.
599 EXPECT_EQ(115, aligned_vector->capacity());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700600 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify())
601 << aligned_vector->SerializationDebugString();
602 EXPECT_EQ(expected_contents,
603 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
604 {.multi_line = true}));
605 std::vector<int> scalars;
606 scalars.push_back(aligned_vector->at(0));
607 while (aligned_vector->size() < 100u) {
608 scalars.push_back(aligned_vector->size());
609 CHECK(aligned_vector->emplace_back(aligned_vector->size()));
610 }
611 VLOG(1) << aligned_vector->SerializationDebugString();
612 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
613 EXPECT_EQ(absl::StrFormat(
614 R"json({
615 "scalar": 123,
616 "vector_of_scalars": [
617 4,
618 5
619 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700620 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700621 "vector_of_strings": [
622 "D"
623 ],
624 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700625 "x": 971,
626 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700627 },
628 "subtable": {
629 "foo": 1234
630 },
631 "vector_aligned": [
632 %s
633 ],
634 "vector_of_structs": [
635 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700636 "x": 48,
637 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700638 },
639 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700640 "x": 118,
641 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700642 },
643 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700644 "x": 971,
645 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700646 }
647 ],
648 "vector_of_tables": [
649 {
650 "foo": 222
651 }
652 ],
653 "included_table": {
654 "foo": "B"
655 }
656})json",
657 absl::StrJoin(scalars, ",\n ")),
658 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
659 {.multi_line = true}));
660 }
661
662 {
663 auto unspecified_vector = object->add_unspecified_length_vector();
664 ASSERT_NE(nullptr, unspecified_vector);
Austin Schuhf8440852024-05-31 10:46:50 -0700665 ASSERT_EQ(60, unspecified_vector->capacity());
666 for (size_t i = 0; i < 60; ++i) {
667 ASSERT_TRUE(unspecified_vector->emplace_back(0));
668 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700669 ASSERT_FALSE(unspecified_vector->emplace_back(0));
Austin Schuhf8440852024-05-31 10:46:50 -0700670 ASSERT_TRUE(unspecified_vector->reserve(64));
671 ASSERT_EQ(124, unspecified_vector->capacity());
672 for (size_t i = 0; i < 64; ++i) {
673 ASSERT_TRUE(unspecified_vector->emplace_back(1));
674 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700675 ASSERT_FALSE(unspecified_vector->emplace_back(3));
676 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
677 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800678 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700679 }
680}
681
682// Tests that field clearing (and subsequent resetting) works properly.
683TEST_F(StaticFlatbuffersTest, ClearFields) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700684 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700685 Builder<TestTableStatic> builder(&allocator);
686 TestTableStatic *object = builder.get();
687 // For each field, we will confirm the following:
688 // * Clearing a non-existent field causes no issues.
689 // * We can set a field, clear it, and have it not be present.
690 // * We can set the field again afterwards.
691 {
692 object->clear_scalar();
693 ASSERT_TRUE(builder.Verify());
694 object->set_scalar(123);
695 EXPECT_EQ(123, object->AsFlatbuffer().scalar());
696 object->clear_scalar();
697 ASSERT_TRUE(builder.Verify());
698 EXPECT_FALSE(object->has_scalar());
699 object->set_scalar(456);
700 EXPECT_EQ(456, object->AsFlatbuffer().scalar());
701 }
702 {
703 object->clear_vector_of_scalars();
704 ASSERT_TRUE(builder.Verify());
705 EXPECT_FALSE(object->has_vector_of_scalars());
706 auto vector = object->add_vector_of_scalars();
707 ASSERT_TRUE(vector->emplace_back(4));
708 ASSERT_TRUE(vector->emplace_back(5));
709 ASSERT_TRUE(vector->emplace_back(6));
710 // Deliberately force a resize of the vector to ensure that we can exercise
711 // what happens if we clear a non-standard size field.
712 ASSERT_FALSE(vector->emplace_back(7));
713 ASSERT_TRUE(vector->reserve(4));
714 ASSERT_TRUE(vector->emplace_back(7));
715 EXPECT_EQ(
716 R"json({
717 "scalar": 456,
718 "vector_of_scalars": [
719 4,
720 5,
721 6,
722 7
723 ]
724})json",
725 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
726 {.multi_line = true}));
727 ASSERT_TRUE(builder.Verify());
728 object->clear_vector_of_scalars();
729 ASSERT_TRUE(builder.Verify());
730 ASSERT_FALSE(object->has_vector_of_scalars())
731 << aos::FlatbufferToJson(builder.AsFlatbufferSpan());
732 vector = CHECK_NOTNULL(object->add_vector_of_scalars());
733 ASSERT_TRUE(builder.Verify());
734 EXPECT_EQ(0u, object->AsFlatbuffer().vector_of_scalars()->size());
735 ASSERT_TRUE(vector->emplace_back(9));
736 ASSERT_TRUE(vector->emplace_back(7));
737 ASSERT_TRUE(vector->emplace_back(1));
738 // This vector has no knowledge of the past resizing; it should fail to add
739 // an extra number.
740 ASSERT_FALSE(vector->emplace_back(7));
741 }
742 {
743 object->clear_substruct();
744 ASSERT_TRUE(builder.Verify());
745 EXPECT_FALSE(object->has_substruct());
746 object->set_substruct(SubStruct{2, 3});
747 EXPECT_EQ(
748 R"json({
749 "scalar": 456,
750 "vector_of_scalars": [
751 9,
752 7,
753 1
754 ],
755 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700756 "x": 2,
757 "y": 3
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700758 }
759})json",
760 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
761 {.multi_line = true}));
762 object->clear_substruct();
763 ASSERT_TRUE(builder.Verify());
764 EXPECT_FALSE(object->has_substruct());
765 object->set_substruct(SubStruct{4, 5});
766 EXPECT_EQ(
767 R"json({
768 "scalar": 456,
769 "vector_of_scalars": [
770 9,
771 7,
772 1
773 ],
774 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700775 "x": 4,
776 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700777 }
778})json",
779 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
780 {.multi_line = true}));
781 }
782 {
783 object->clear_subtable();
784 ASSERT_TRUE(builder.Verify());
785 EXPECT_FALSE(object->has_subtable());
786 auto subtable = CHECK_NOTNULL(object->add_subtable());
787 subtable->set_baz(9.71);
788 EXPECT_EQ(
789 R"json({
790 "scalar": 456,
791 "vector_of_scalars": [
792 9,
793 7,
794 1
795 ],
796 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700797 "x": 4,
798 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700799 },
800 "subtable": {
801 "baz": 9.71
802 }
803})json",
804 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
805 {.multi_line = true}));
806 object->clear_subtable();
807 ASSERT_TRUE(builder.Verify());
808 EXPECT_FALSE(object->has_subtable());
809 subtable = CHECK_NOTNULL(object->add_subtable());
810 subtable->set_baz(16.78);
811 EXPECT_EQ(
812 R"json({
813 "scalar": 456,
814 "vector_of_scalars": [
815 9,
816 7,
817 1
818 ],
819 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700820 "x": 4,
821 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700822 },
823 "subtable": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700824 "baz": 16.78
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700825 }
826})json",
827 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
828 {.multi_line = true}));
829 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800830 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700831}
832
833// Try to cover ~all supported scalar/flatbuffer types using JSON convenience
834// functions.
835TEST_F(StaticFlatbuffersTest, FlatbufferTypeCoverage) {
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800836 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700837 std::string populated_config =
838 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
839 "aos/flatbuffers/test_dir/type_coverage.json"));
840 // Get rid of a pesky new line.
841 populated_config = populated_config.substr(0, populated_config.size() - 1);
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800842 VerifyJson<aos::testing::ConfigurationStatic>(populated_config);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700843
844 // And now play around with mutating the buffer.
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800845 Builder<aos::testing::ConfigurationStatic> builder =
846 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700847 populated_config);
848 ASSERT_TRUE(builder.Verify());
849 builder.get()->clear_foo_float();
850 ASSERT_TRUE(builder.Verify());
851 ASSERT_FALSE(builder.get()->AsFlatbuffer().has_foo_float());
852 builder.get()->set_foo_float(1.111);
853 ASSERT_TRUE(builder.Verify());
854 ASSERT_FLOAT_EQ(1.111, builder.get()->AsFlatbuffer().foo_float());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800855 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700856}
857
James Kuszmaula75cd7c2023-12-07 15:52:51 -0800858TEST_F(StaticFlatbuffersTest, MinimallyAlignedTable) {
859 VerifyJson<MinimallyAlignedTableStatic>("{\n \"field\": 123\n}");
860 static_assert(4u == alignof(uoffset_t),
861 "The alignment of a uoffset_t is expected to be 4.");
862 ASSERT_EQ(alignof(uoffset_t), MinimallyAlignedTableStatic::kAlign)
863 << "No table should have an alignment of less than the alignment of the "
864 "table's root offset.";
865}
866
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700867// Confirm that we can use the SpanAllocator with a span that provides exactly
868// the required buffer size.
869TEST_F(StaticFlatbuffersTest, ExactSizeSpanAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700870 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800871 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700872 Builder<TestTableStatic> builder(&allocator);
873 TestTableStatic *object = builder.get();
874 object->set_scalar(123);
875 {
876 auto vector = object->add_vector_of_scalars();
877 ASSERT_TRUE(vector->emplace_back(4));
878 ASSERT_TRUE(vector->emplace_back(5));
879 }
880 {
881 auto string = object->add_string();
882 string->SetString("Hello, World!");
883 }
884 {
885 auto vector_of_strings = object->add_vector_of_strings();
886 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
887 ASSERT_TRUE(sub_string->emplace_back('D'));
888 }
889 { object->set_substruct({971, 254}); }
890 {
891 auto subtable = object->add_subtable();
892 subtable->set_foo(1234);
893 }
894 {
895 auto vector = object->add_vector_of_structs();
896 ASSERT_TRUE(vector->emplace_back({48, 67}));
897 ASSERT_TRUE(vector->emplace_back({118, 148}));
898 ASSERT_TRUE(vector->emplace_back({971, 973}));
899 // Max vector size is three; this should fail.
900 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
901 // We don't have any extra space available.
902 ASSERT_FALSE(vector->reserve(4));
903 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
904 }
905 {
906 auto vector = object->add_vector_of_tables();
907 auto subobject = vector->emplace_back();
908 subobject->set_foo(222);
909 }
910 {
911 auto subtable = object->add_included_table();
912 subtable->set_foo(included::TestEnum::B);
913 }
914 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
915 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
916 {.multi_line = true});
917 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800918 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700919}
920
921// Test that when we provide too small of a span to the Builder that it
922// correctly fails.
923TEST_F(StaticFlatbuffersTest, TooSmallSpanAllocator) {
924 std::vector<uint8_t> buffer;
925 buffer.resize(10, 0);
926 aos::fbs::SpanAllocator allocator({buffer.data(), buffer.size()});
927 EXPECT_DEATH(Builder<TestTableStatic>{&allocator}, "Failed to allocate");
928}
929
930// Verify that if we create a span with extra headroom that that lets us
931// dynamically alter the size of vectors in the flatbuffers.
932TEST_F(StaticFlatbuffersTest, ExtraLargeSpanAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700933 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize + 200 * 64];
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800934 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700935 Builder<TestTableStatic> builder(&allocator);
936 TestTableStatic *object = builder.get();
937 {
938 auto vector = object->add_unspecified_length_vector();
939 // Confirm that the vector does indeed start out at zero length.
Austin Schuhf8440852024-05-31 10:46:50 -0700940 ASSERT_EQ(vector->capacity(), 60);
941 for (size_t i = 0; i < 60; ++i) {
942 ASSERT_TRUE(vector->emplace_back(i));
943 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700944 ASSERT_FALSE(vector->emplace_back(4));
945 ASSERT_TRUE(vector->reserve(9000));
946 vector->resize(256);
Austin Schuhf8440852024-05-31 10:46:50 -0700947 for (size_t index = 60; index < 256; ++index) {
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700948 vector->at(index) = static_cast<uint8_t>(index);
949 }
950 }
951 ASSERT_EQ(256, object->AsFlatbuffer().unspecified_length_vector()->size());
952 size_t expected = 0;
953 for (const uint8_t value :
954 *object->AsFlatbuffer().unspecified_length_vector()) {
955 EXPECT_EQ(expected++, value);
956 }
James Kuszmaul22448052023-12-14 15:55:14 -0800957 expected = 0;
958 for (const uint8_t value : *object->unspecified_length_vector()) {
959 EXPECT_EQ(expected++, value);
960 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800961 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700962}
James Kuszmaul22448052023-12-14 15:55:14 -0800963
964// Tests that the iterators on the Vector type work.
965TEST_F(StaticFlatbuffersTest, IteratorTest) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700966 Builder<TestTableStatic> builder(std::make_unique<AlignedVectorAllocator>());
James Kuszmaul22448052023-12-14 15:55:14 -0800967 {
968 auto vector = builder->add_unspecified_length_vector();
969 ASSERT_TRUE(vector->reserve(9000));
970 vector->resize(256);
971 uint8_t set_value = 0;
972 for (uint8_t &destination : *vector) {
973 destination = set_value;
974 ++set_value;
975 }
976 uint8_t expected = 0;
977 for (const uint8_t value : *builder->unspecified_length_vector()) {
978 EXPECT_EQ(expected, value);
979 ++expected;
980 }
981 // Exercise some of the random access iterator functionality to ensure that
982 // we have it implemented.
983 auto begin_it = vector->begin();
984 EXPECT_EQ(begin_it + 256, vector->end());
985 EXPECT_EQ(7, *(begin_it + 7));
986 EXPECT_EQ(255, *(vector->end() - 1));
987 EXPECT_EQ(256, vector->end() - vector->begin());
988 EXPECT_EQ(-256, vector->begin() - vector->end());
989 static_assert(std::random_access_iterator<decltype(vector->begin())>,
990 "The vector iterator does not meet the requirements of a "
991 "random access iterator.");
992 }
993 {
994 auto vector = builder->add_vector_of_structs();
995 vector->resize(3);
996 double set_value = 0;
997 for (SubStruct &destination : *vector) {
998 destination.mutate_x(set_value);
999 destination.mutate_y(-set_value);
1000 set_value += 1.0;
1001 }
1002 double expected = 0;
1003 for (const SubStruct &value : *builder->vector_of_structs()) {
1004 EXPECT_EQ(expected, value.x());
1005 EXPECT_EQ(-expected, value.y());
1006 expected += 1.0;
1007 }
1008 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1009 "The vector iterator does not meet the requirements of a "
1010 "random access iterator.");
1011 }
1012 {
1013 auto vector = builder->add_vector_of_tables();
1014 vector->resize(3);
1015 int set_value = 0;
1016 for (SubTableStatic &destination : *vector) {
1017 destination.set_foo(set_value);
1018 set_value += 1;
1019 }
1020 int expected = 0;
1021 for (const SubTableStatic &value : *builder->vector_of_tables()) {
1022 EXPECT_EQ(expected, value.foo());
1023 EXPECT_FALSE(value.has_baz());
1024 expected += 1;
1025 }
1026 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1027 "The vector iterator does not meet the requirements of a "
1028 "random access iterator.");
1029 }
1030}
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001031
1032// Confirm that we can use the FixedStackAllocator
1033TEST_F(StaticFlatbuffersTest, FixedStackAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -07001034 aos::fbs::FixedStackAllocator<Builder<TestTableStatic>::kBufferSize,
1035 Builder<TestTableStatic>::kAlign>
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001036 allocator;
1037 Builder<TestTableStatic> builder(&allocator);
1038 TestTableStatic *object = builder.get();
1039 object->set_scalar(123);
1040 {
1041 auto vector = object->add_vector_of_scalars();
1042 ASSERT_TRUE(vector->emplace_back(4));
1043 ASSERT_TRUE(vector->emplace_back(5));
1044 }
1045 {
1046 auto string = object->add_string();
1047 string->SetString("Hello, World!");
1048 }
1049 {
1050 auto vector_of_strings = object->add_vector_of_strings();
1051 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
1052 ASSERT_TRUE(sub_string->emplace_back('D'));
1053 }
1054 { object->set_substruct({971, 254}); }
1055 {
1056 auto subtable = object->add_subtable();
1057 subtable->set_foo(1234);
1058 }
1059 {
1060 auto vector = object->add_vector_of_structs();
1061 ASSERT_TRUE(vector->emplace_back({48, 67}));
1062 ASSERT_TRUE(vector->emplace_back({118, 148}));
1063 ASSERT_TRUE(vector->emplace_back({971, 973}));
1064 // Max vector size is three; this should fail.
1065 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1066 // We don't have any extra space available.
1067 ASSERT_FALSE(vector->reserve(4));
1068 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1069 }
1070 {
1071 auto vector = object->add_vector_of_tables();
1072 auto subobject = vector->emplace_back();
1073 subobject->set_foo(222);
1074 }
1075 {
1076 auto subtable = object->add_included_table();
1077 subtable->set_foo(included::TestEnum::B);
1078 }
1079 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1080 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1081 {.multi_line = true});
1082 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1083 TestMemory(builder.buffer());
1084}
1085
James Kuszmaul6be41022023-12-20 11:55:28 -08001086// Uses a small example to manually verify that we can copy from the flatbuffer
1087// object API.
1088TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
1089 aos::fbs::testing::TestTableT object_t;
1090 object_t.scalar = 971;
1091 object_t.vector_of_strings.push_back("971");
1092 object_t.vector_of_structs.push_back({1, 2});
1093 object_t.subtable = std::make_unique<SubTableT>();
Austin Schuh02e0d772024-05-30 16:41:06 -07001094 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaul6be41022023-12-20 11:55:28 -08001095 Builder<TestTableStatic> builder(&allocator);
1096 ASSERT_TRUE(builder->FromFlatbuffer(object_t));
1097 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1098 // Note that vectors and strings get set to zero-length, but present, values.
1099 EXPECT_EQ(
1100 "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
1101 "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
James Kuszmaul98c798d2024-04-24 15:58:09 -07001102 "\"baz\": 0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
1103 "\"x\": 1, \"y\": 2 } ], \"vector_of_tables\": [ ], "
James Kuszmaul6be41022023-12-20 11:55:28 -08001104 "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
1105 "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
1106 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
1107}
1108
1109// More completely covers our object API copying by comparing the flatbuffer
1110// Pack() methods to our FromFlatbuffer() methods.
1111TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
1112 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
1113 std::string populated_config =
1114 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
1115 "aos/flatbuffers/test_dir/type_coverage.json"));
1116 Builder<aos::testing::ConfigurationStatic> json_builder =
1117 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
1118 populated_config);
1119 aos::testing::ConfigurationT object_t;
1120 json_builder->AsFlatbuffer().UnPackTo(&object_t);
1121
1122 Builder<aos::testing::ConfigurationStatic> from_object_static;
1123 ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
1124 flatbuffers::FlatBufferBuilder fbb;
1125 fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
1126 aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
1127 fbb.Release();
1128 EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
1129 aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
1130}
1131
James Kuszmaul1e57af92023-12-20 15:34:54 -08001132// Tests that we can build code that uses the reflection types.
1133TEST_F(StaticFlatbuffersTest, IncludeReflectionTypes) {
1134 VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
1135}
1136
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001137// Tests that we can use the move constructor on a Builder.
1138TEST_F(StaticFlatbuffersTest, BuilderMoveConstructor) {
Austin Schuh02e0d772024-05-30 16:41:06 -07001139 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001140 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
1141 Builder<TestTableStatic> builder_from(&allocator);
1142 Builder<TestTableStatic> builder(std::move(builder_from));
1143 TestTableStatic *object = builder.get();
1144 object->set_scalar(123);
1145 {
1146 auto vector = object->add_vector_of_scalars();
1147 ASSERT_TRUE(vector->emplace_back(4));
1148 ASSERT_TRUE(vector->emplace_back(5));
1149 }
1150 {
1151 auto string = object->add_string();
1152 string->SetString("Hello, World!");
1153 }
1154 {
1155 auto vector_of_strings = object->add_vector_of_strings();
1156 auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
1157 ASSERT_TRUE(sub_string->emplace_back('D'));
1158 }
1159 { object->set_substruct({971, 254}); }
1160 {
1161 auto subtable = object->add_subtable();
1162 subtable->set_foo(1234);
1163 }
1164 {
1165 auto vector = object->add_vector_of_structs();
1166 ASSERT_TRUE(vector->emplace_back({48, 67}));
1167 ASSERT_TRUE(vector->emplace_back({118, 148}));
1168 ASSERT_TRUE(vector->emplace_back({971, 973}));
1169 // Max vector size is three; this should fail.
1170 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1171 // We don't have any extra space available.
1172 ASSERT_FALSE(vector->reserve(4));
1173 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1174 }
1175 {
1176 auto vector = object->add_vector_of_tables();
1177 auto subobject = vector->emplace_back();
1178 subobject->set_foo(222);
1179 }
1180 {
1181 auto subtable = object->add_included_table();
1182 subtable->set_foo(included::TestEnum::B);
1183 }
1184 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1185 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1186 {.multi_line = true});
1187 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1188 TestMemory(builder.buffer());
1189}
1190
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001191} // namespace aos::fbs::testing