blob: 187c72a4371006ddf79e3d57eabcb75c68870dac [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();
Austin Schuh6bdcc372024-06-27 14:49:11 -0700126 auto sub_string = vector_of_strings->emplace_back();
127 CHECK(sub_string != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700128 CHECK(sub_string->emplace_back('D'));
129 }
130 { object->set_substruct({971, 254}); }
131 {
132 auto subtable = object->add_subtable();
133 subtable->set_foo(1234);
134 }
135 {
136 auto vector = object->add_vector_of_structs();
137 CHECK(vector->emplace_back({48, 67}));
138 CHECK(vector->emplace_back({118, 148}));
139 CHECK(vector->emplace_back({971, 973}));
140 // Max vector size is three; this should fail.
141 CHECK(!vector->emplace_back({1114, 2056}));
142 }
143 {
144 auto vector = object->add_vector_of_tables();
145 auto subobject = vector->emplace_back();
146 subobject->set_foo(222);
147 }
148 {
149 auto subtable = object->add_included_table();
150 subtable->set_foo(included::TestEnum::B);
151 }
152 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
153 LOG(INFO) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
154 {.multi_line = true});
155 LOG(INFO) << AnnotateBinaries(test_schema_, builder.buffer());
156}
157
158// Test that compiles the same code that is used by an example in
159// //aos/documentation/aos/docs/flatbuffers.md showing how to convert a
160// Populate*() method for adding a subtable to a flatbuffer.
161namespace {
162flatbuffers::Offset<SubTable> PopulateOld(flatbuffers::FlatBufferBuilder *fbb) {
163 SubTable::Builder builder(*fbb);
164 builder.add_foo(1234);
165 return builder.Finish();
166}
Austin Schuh6bdcc372024-06-27 14:49:11 -0700167void PopulateStatic(SubTableStatic *subtable) {
168 CHECK(subtable != nullptr);
169 subtable->set_foo(1234);
170}
171
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700172} // namespace
Austin Schuh6bdcc372024-06-27 14:49:11 -0700173
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700174TEST_F(StaticFlatbuffersTest, PopulateMethodConversionExample) {
175 // Using a FlatBufferBuilder:
176 flatbuffers::FlatBufferBuilder fbb;
177 // Note: the PopulateOld() *must* be called prior to creating the builder.
178 const flatbuffers::Offset<SubTable> subtable_offset = PopulateOld(&fbb);
179 TestTable::Builder testtable_builder(fbb);
180 testtable_builder.add_subtable(subtable_offset);
181 fbb.Finish(testtable_builder.Finish());
182 aos::FlatbufferDetachedBuffer<TestTable> fbb_finished = fbb.Release();
183
184 // Using the static flatbuffer API.
Austin Schuh02e0d772024-05-30 16:41:06 -0700185 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700186 Builder<TestTableStatic> static_builder(&allocator);
Austin Schuh6bdcc372024-06-27 14:49:11 -0700187 PopulateStatic(static_builder.get()->add_subtable());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700188
189 // And confirm that they both contain the expected flatbuffer:
190 const std::string expected = R"json({ "subtable": { "foo": 1234 } })json";
191 EXPECT_EQ(expected, aos::FlatbufferToJson(fbb_finished));
192 EXPECT_EQ(expected, aos::FlatbufferToJson(static_builder.AsFlatbufferSpan()));
193}
194
195TEST_F(StaticFlatbuffersTest, UnsupportedSchema) {
196 const reflection::Schema *schema = &interesting_schemas_.message();
197 EXPECT_DEATH(
198 GenerateCodeForObject(
199 schema, GetObjectByName(schema, "aos.fbs.testing.TableWithUnion")),
200 "Union not supported");
201 GenerateCodeForObject(
202 schema, GetObjectByName(schema, "aos.fbs.testing.MissingVectorLength"));
203 EXPECT_DEATH(
204 GenerateCodeForObject(
205 schema,
206 GetObjectByName(schema, "aos.fbs.testing.NonIntegerVectorLength")),
207 "vector_badlength must specify a positive integer for the "
208 "static_length attribute.");
209 EXPECT_DEATH(GenerateCodeForObject(
210 schema, GetObjectByName(
211 schema, "aos.fbs.testing.NegativeVectorLength")),
212 "Field vector_badlength must have a non-negative "
213 "static_length.");
214 GenerateCodeForObject(
215 schema, GetObjectByName(schema, "aos.fbs.testing.ZeroVectorLength"));
216 GenerateCodeForObject(
217 schema, GetObjectByName(schema, "aos.fbs.testing.MissingStringLength"));
218 GenerateCodeForObject(
219 schema,
220 GetObjectByName(schema, "aos.fbs.testing.MissingSubStringLength"));
221}
222
223// Tests that we can go through and manually build up a big flatbuffer and that
224// it stays valid at all points.
225TEST_F(StaticFlatbuffersTest, ManuallyConstructFlatbuffer) {
226 {
Austin Schuh02e0d772024-05-30 16:41:06 -0700227 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700228 Builder<SubTableStatic> builder(&allocator);
229 SubTableStatic *object = builder.get();
230 if (!builder.AsFlatbufferSpan().Verify()) {
231 LOG(ERROR) << object->SerializationDebugString() << "\nRoot table offset "
232 << *reinterpret_cast<const uoffset_t *>(
233 builder.buffer().data())
234 << "\nraw bytes\n";
235 aos::fbs::internal::DebugBytes(builder.buffer(), std::cerr);
236 FAIL();
237 return;
238 }
239 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
240 object->set_foo(123);
241 object->set_baz(971);
242 CHECK(builder.AsFlatbufferSpan().Verify());
243 EXPECT_EQ(123, object->AsFlatbuffer().foo());
244 EXPECT_EQ(971, object->AsFlatbuffer().baz());
James Kuszmaul98c798d2024-04-24 15:58:09 -0700245 EXPECT_EQ(R"json({ "foo": 123, "baz": 971 })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700246 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800247 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700248 }
249 {
Austin Schuh02e0d772024-05-30 16:41:06 -0700250 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700251 Builder<TestTableStatic> builder(&allocator);
252 TestTableStatic *object = builder.get();
253 const aos::fbs::testing::TestTable &fbs = object->AsFlatbuffer();
254 VLOG(1) << object->SerializationDebugString();
255 CHECK(builder.AsFlatbufferSpan().Verify());
256 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
257 {
258 ASSERT_FALSE(object->has_scalar());
259 object->set_scalar(123);
260 EXPECT_TRUE(fbs.has_scalar());
261 EXPECT_EQ(123, fbs.scalar());
262 }
263 EXPECT_EQ(R"json({ "scalar": 123 })json",
264 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
265 {
266 ASSERT_FALSE(object->has_vector_of_scalars());
267 auto vector = object->add_vector_of_scalars();
268 ASSERT_TRUE(vector->emplace_back(4));
269 ASSERT_TRUE(vector->emplace_back(5));
270 ASSERT_TRUE(object->has_vector_of_scalars());
271 ASSERT_TRUE(fbs.has_vector_of_scalars());
272 VLOG(1) << vector->SerializationDebugString();
273 EXPECT_TRUE(fbs.has_vector_of_scalars());
274 EXPECT_EQ(2u, fbs.vector_of_scalars()->size());
275 EXPECT_EQ(4, fbs.vector_of_scalars()->Get(0));
276 EXPECT_EQ(5, fbs.vector_of_scalars()->Get(1));
277 }
278 EXPECT_EQ(R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ] })json",
279 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
280 {
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700281 // Set the string to a value that is as long as its static length. An
282 // extra byte for the null character should be automatically allocated.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700283 EXPECT_FALSE(object->has_string());
284 auto string = object->add_string();
285 EXPECT_TRUE(object->has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700286 string->SetString("This is twenty chars");
287 EXPECT_EQ(20u, object->string()->size());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700288 ASSERT_TRUE(fbs.has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700289 ASSERT_EQ(20u, fbs.string()->size());
290 EXPECT_EQ("This is twenty chars", fbs.string()->string_view());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700291 // Check that we null-terminated correctly.
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700292 EXPECT_EQ(20u, strnlen(fbs.string()->c_str(), 21));
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700293 }
294 EXPECT_EQ(
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700295 R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ], "string": "This is twenty chars" })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700296 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
297 {
298 EXPECT_FALSE(object->has_vector_of_strings());
299 auto vector_of_strings = object->add_vector_of_strings();
300 EXPECT_TRUE(object->has_vector_of_strings());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700301 auto sub_string = vector_of_strings->emplace_back();
302 CHECK(sub_string != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700303 ASSERT_TRUE(sub_string->emplace_back('D'));
304 EXPECT_TRUE(fbs.has_vector_of_strings());
305 ASSERT_EQ(1u, fbs.vector_of_strings()->size());
306 ASSERT_EQ(1u, fbs.vector_of_strings()->Get(0)->size());
307 EXPECT_EQ('D', fbs.vector_of_strings()->Get(0)->Get(0));
308 }
309 EXPECT_EQ(
310 R"json({
311 "scalar": 123,
312 "vector_of_scalars": [
313 4,
314 5
315 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700316 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700317 "vector_of_strings": [
318 "D"
319 ]
320})json",
321 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
322 {.multi_line = true}));
323 {
324 EXPECT_FALSE(object->has_substruct());
325 object->set_substruct({971, 254});
326 EXPECT_TRUE(object->has_substruct());
327 EXPECT_TRUE(fbs.has_substruct());
328 EXPECT_EQ(971, fbs.substruct()->x());
329 EXPECT_EQ(254, fbs.substruct()->y());
330 }
331 EXPECT_EQ(
332 R"json({
333 "scalar": 123,
334 "vector_of_scalars": [
335 4,
336 5
337 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700338 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700339 "vector_of_strings": [
340 "D"
341 ],
342 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700343 "x": 971,
344 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700345 }
346})json",
347 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
348 {.multi_line = true}));
349 {
350 auto subtable = object->add_subtable();
351 subtable->set_foo(1234);
352 EXPECT_TRUE(fbs.has_subtable());
353 EXPECT_EQ(1234, fbs.subtable()->foo());
354 EXPECT_FALSE(fbs.subtable()->has_baz());
355 }
356 EXPECT_EQ(
357 R"json({
358 "scalar": 123,
359 "vector_of_scalars": [
360 4,
361 5
362 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700363 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700364 "vector_of_strings": [
365 "D"
366 ],
367 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700368 "x": 971,
369 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700370 },
371 "subtable": {
372 "foo": 1234
373 }
374})json",
375 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
376 {.multi_line = true}));
377 {
378 auto vector = object->add_vector_of_structs();
379 ASSERT_TRUE(vector->emplace_back({48, 67}));
380 ASSERT_TRUE(vector->emplace_back({118, 148}));
381 ASSERT_TRUE(vector->emplace_back({971, 973}));
382 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
383 EXPECT_TRUE(fbs.has_vector_of_structs());
384 EXPECT_EQ(3u, fbs.vector_of_structs()->size());
385 EXPECT_EQ(48, fbs.vector_of_structs()->Get(0)->x());
386 EXPECT_EQ(67, fbs.vector_of_structs()->Get(0)->y());
387 EXPECT_EQ(118, fbs.vector_of_structs()->Get(1)->x());
388 EXPECT_EQ(object->vector_of_structs()->at(1).x(),
389 fbs.vector_of_structs()->Get(1)->x());
390 EXPECT_EQ((*object->vector_of_structs())[1].x(),
391 fbs.vector_of_structs()->Get(1)->x());
392 EXPECT_EQ(148, fbs.vector_of_structs()->Get(1)->y());
393 EXPECT_EQ(971, fbs.vector_of_structs()->Get(2)->x());
394 EXPECT_EQ(973, fbs.vector_of_structs()->Get(2)->y());
395 }
396 EXPECT_EQ(
397 R"json({
398 "scalar": 123,
399 "vector_of_scalars": [
400 4,
401 5
402 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700403 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700404 "vector_of_strings": [
405 "D"
406 ],
407 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700408 "x": 971,
409 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700410 },
411 "subtable": {
412 "foo": 1234
413 },
414 "vector_of_structs": [
415 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700416 "x": 48,
417 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700418 },
419 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700420 "x": 118,
421 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700422 },
423 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700424 "x": 971,
425 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700426 }
427 ]
428})json",
429 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
430 {.multi_line = true}));
431 {
432 EXPECT_FALSE(object->has_vector_of_tables());
433 auto vector = object->add_vector_of_tables();
434 EXPECT_TRUE(object->has_vector_of_tables());
435 auto subobject = vector->emplace_back();
436 subobject->set_foo(222);
437 EXPECT_TRUE(fbs.has_vector_of_tables());
438 EXPECT_EQ(1u, fbs.vector_of_tables()->size());
439 EXPECT_EQ(222, fbs.vector_of_tables()->Get(0)->foo());
440 EXPECT_EQ(object->vector_of_tables()->at(0).foo(),
441 fbs.vector_of_tables()->Get(0)->foo());
442 }
443 EXPECT_EQ(
444 R"json({
445 "scalar": 123,
446 "vector_of_scalars": [
447 4,
448 5
449 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700450 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700451 "vector_of_strings": [
452 "D"
453 ],
454 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700455 "x": 971,
456 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700457 },
458 "subtable": {
459 "foo": 1234
460 },
461 "vector_of_structs": [
462 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700463 "x": 48,
464 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700465 },
466 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700467 "x": 118,
468 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700469 },
470 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700471 "x": 971,
472 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700473 }
474 ],
475 "vector_of_tables": [
476 {
477 "foo": 222
478 }
479 ]
480})json",
481 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
482 {.multi_line = true}));
483 {
484 EXPECT_FALSE(object->has_included_table());
485 auto subtable = object->add_included_table();
486 EXPECT_TRUE(object->has_included_table());
487 subtable->set_foo(included::TestEnum::B);
488 ASSERT_TRUE(fbs.has_included_table());
489 ASSERT_TRUE(fbs.included_table()->has_foo());
490 EXPECT_EQ(included::TestEnum::B, fbs.included_table()->foo());
491 }
492 EXPECT_EQ(
493 R"json({
494 "scalar": 123,
495 "vector_of_scalars": [
496 4,
497 5
498 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700499 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700500 "vector_of_strings": [
501 "D"
502 ],
503 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700504 "x": 971,
505 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700506 },
507 "subtable": {
508 "foo": 1234
509 },
510 "vector_of_structs": [
511 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700512 "x": 48,
513 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700514 },
515 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700516 "x": 118,
517 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700518 },
519 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700520 "x": 971,
521 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700522 }
523 ],
524 "vector_of_tables": [
525 {
526 "foo": 222
527 }
528 ],
529 "included_table": {
530 "foo": "B"
531 }
532})json",
533 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
534 {.multi_line = true}));
535 {
536 auto aligned_vector = object->add_vector_aligned();
537 ASSERT_EQ(64,
538 std::remove_reference<decltype(*aligned_vector)>::type::kAlign);
539 ASSERT_EQ(64, TestTableStatic::kAlign);
540 ASSERT_TRUE(aligned_vector->emplace_back(444));
541 EXPECT_TRUE(fbs.has_vector_aligned());
542 EXPECT_EQ(1u, fbs.vector_aligned()->size());
543 EXPECT_EQ(0u,
544 reinterpret_cast<size_t>(fbs.vector_aligned()->data()) % 64);
545 EXPECT_EQ(444, fbs.vector_aligned()->Get(0));
546 }
547 VLOG(1) << object->SerializationDebugString();
548 CHECK(builder.AsFlatbufferSpan().Verify());
549 const std::string expected_contents =
550 R"json({
551 "scalar": 123,
552 "vector_of_scalars": [
553 4,
554 5
555 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700556 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700557 "vector_of_strings": [
558 "D"
559 ],
560 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700561 "x": 971,
562 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700563 },
564 "subtable": {
565 "foo": 1234
566 },
567 "vector_aligned": [
568 444
569 ],
570 "vector_of_structs": [
571 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700572 "x": 48,
573 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700574 },
575 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700576 "x": 118,
577 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700578 },
579 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700580 "x": 971,
581 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700582 }
583 ],
584 "vector_of_tables": [
585 {
586 "foo": 222
587 }
588 ],
589 "included_table": {
590 "foo": "B"
591 }
592})json";
593 EXPECT_EQ(expected_contents,
594 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
595 {.multi_line = true}));
596 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
597 VerifyJson<TestTableStatic>(expected_contents);
598 {
599 auto aligned_vector = object->mutable_vector_aligned();
600 ASSERT_TRUE(aligned_vector->reserve(100));
Austin Schuhf8440852024-05-31 10:46:50 -0700601
602 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
603 // Since the allocator is going to allocate in blocks of 64, we end up
604 // with more capacity than we asked for. Better to have it than to leave
605 // it as unusable padding.
606 EXPECT_EQ(115, aligned_vector->capacity());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700607 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify())
608 << aligned_vector->SerializationDebugString();
609 EXPECT_EQ(expected_contents,
610 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
611 {.multi_line = true}));
612 std::vector<int> scalars;
613 scalars.push_back(aligned_vector->at(0));
614 while (aligned_vector->size() < 100u) {
615 scalars.push_back(aligned_vector->size());
616 CHECK(aligned_vector->emplace_back(aligned_vector->size()));
617 }
618 VLOG(1) << aligned_vector->SerializationDebugString();
619 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
620 EXPECT_EQ(absl::StrFormat(
621 R"json({
622 "scalar": 123,
623 "vector_of_scalars": [
624 4,
625 5
626 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700627 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700628 "vector_of_strings": [
629 "D"
630 ],
631 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700632 "x": 971,
633 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700634 },
635 "subtable": {
636 "foo": 1234
637 },
638 "vector_aligned": [
639 %s
640 ],
641 "vector_of_structs": [
642 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700643 "x": 48,
644 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700645 },
646 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700647 "x": 118,
648 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700649 },
650 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700651 "x": 971,
652 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700653 }
654 ],
655 "vector_of_tables": [
656 {
657 "foo": 222
658 }
659 ],
660 "included_table": {
661 "foo": "B"
662 }
663})json",
664 absl::StrJoin(scalars, ",\n ")),
665 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
666 {.multi_line = true}));
667 }
668
669 {
670 auto unspecified_vector = object->add_unspecified_length_vector();
671 ASSERT_NE(nullptr, unspecified_vector);
Austin Schuhf8440852024-05-31 10:46:50 -0700672 ASSERT_EQ(60, unspecified_vector->capacity());
673 for (size_t i = 0; i < 60; ++i) {
674 ASSERT_TRUE(unspecified_vector->emplace_back(0));
675 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700676 ASSERT_FALSE(unspecified_vector->emplace_back(0));
Austin Schuhf8440852024-05-31 10:46:50 -0700677 ASSERT_TRUE(unspecified_vector->reserve(64));
678 ASSERT_EQ(124, unspecified_vector->capacity());
679 for (size_t i = 0; i < 64; ++i) {
680 ASSERT_TRUE(unspecified_vector->emplace_back(1));
681 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700682 ASSERT_FALSE(unspecified_vector->emplace_back(3));
683 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
684 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800685 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700686 }
687}
688
689// Tests that field clearing (and subsequent resetting) works properly.
690TEST_F(StaticFlatbuffersTest, ClearFields) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700691 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700692 Builder<TestTableStatic> builder(&allocator);
693 TestTableStatic *object = builder.get();
694 // For each field, we will confirm the following:
695 // * Clearing a non-existent field causes no issues.
696 // * We can set a field, clear it, and have it not be present.
697 // * We can set the field again afterwards.
698 {
699 object->clear_scalar();
700 ASSERT_TRUE(builder.Verify());
701 object->set_scalar(123);
702 EXPECT_EQ(123, object->AsFlatbuffer().scalar());
703 object->clear_scalar();
704 ASSERT_TRUE(builder.Verify());
705 EXPECT_FALSE(object->has_scalar());
706 object->set_scalar(456);
707 EXPECT_EQ(456, object->AsFlatbuffer().scalar());
708 }
709 {
710 object->clear_vector_of_scalars();
711 ASSERT_TRUE(builder.Verify());
712 EXPECT_FALSE(object->has_vector_of_scalars());
713 auto vector = object->add_vector_of_scalars();
714 ASSERT_TRUE(vector->emplace_back(4));
715 ASSERT_TRUE(vector->emplace_back(5));
716 ASSERT_TRUE(vector->emplace_back(6));
717 // Deliberately force a resize of the vector to ensure that we can exercise
718 // what happens if we clear a non-standard size field.
719 ASSERT_FALSE(vector->emplace_back(7));
720 ASSERT_TRUE(vector->reserve(4));
721 ASSERT_TRUE(vector->emplace_back(7));
722 EXPECT_EQ(
723 R"json({
724 "scalar": 456,
725 "vector_of_scalars": [
726 4,
727 5,
728 6,
729 7
730 ]
731})json",
732 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
733 {.multi_line = true}));
734 ASSERT_TRUE(builder.Verify());
735 object->clear_vector_of_scalars();
736 ASSERT_TRUE(builder.Verify());
737 ASSERT_FALSE(object->has_vector_of_scalars())
738 << aos::FlatbufferToJson(builder.AsFlatbufferSpan());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700739 vector = object->add_vector_of_scalars();
740 ASSERT_TRUE(vector != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700741 ASSERT_TRUE(builder.Verify());
742 EXPECT_EQ(0u, object->AsFlatbuffer().vector_of_scalars()->size());
743 ASSERT_TRUE(vector->emplace_back(9));
744 ASSERT_TRUE(vector->emplace_back(7));
745 ASSERT_TRUE(vector->emplace_back(1));
746 // This vector has no knowledge of the past resizing; it should fail to add
747 // an extra number.
748 ASSERT_FALSE(vector->emplace_back(7));
749 }
750 {
751 object->clear_substruct();
752 ASSERT_TRUE(builder.Verify());
753 EXPECT_FALSE(object->has_substruct());
754 object->set_substruct(SubStruct{2, 3});
755 EXPECT_EQ(
756 R"json({
757 "scalar": 456,
758 "vector_of_scalars": [
759 9,
760 7,
761 1
762 ],
763 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700764 "x": 2,
765 "y": 3
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700766 }
767})json",
768 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
769 {.multi_line = true}));
770 object->clear_substruct();
771 ASSERT_TRUE(builder.Verify());
772 EXPECT_FALSE(object->has_substruct());
773 object->set_substruct(SubStruct{4, 5});
774 EXPECT_EQ(
775 R"json({
776 "scalar": 456,
777 "vector_of_scalars": [
778 9,
779 7,
780 1
781 ],
782 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700783 "x": 4,
784 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700785 }
786})json",
787 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
788 {.multi_line = true}));
789 }
790 {
791 object->clear_subtable();
792 ASSERT_TRUE(builder.Verify());
793 EXPECT_FALSE(object->has_subtable());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700794 auto subtable = object->add_subtable();
795 ASSERT_TRUE(subtable != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700796 subtable->set_baz(9.71);
797 EXPECT_EQ(
798 R"json({
799 "scalar": 456,
800 "vector_of_scalars": [
801 9,
802 7,
803 1
804 ],
805 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700806 "x": 4,
807 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700808 },
809 "subtable": {
810 "baz": 9.71
811 }
812})json",
813 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
814 {.multi_line = true}));
815 object->clear_subtable();
816 ASSERT_TRUE(builder.Verify());
817 EXPECT_FALSE(object->has_subtable());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700818 subtable = object->add_subtable();
819 ASSERT_TRUE(subtable != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700820 subtable->set_baz(16.78);
821 EXPECT_EQ(
822 R"json({
823 "scalar": 456,
824 "vector_of_scalars": [
825 9,
826 7,
827 1
828 ],
829 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700830 "x": 4,
831 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700832 },
833 "subtable": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700834 "baz": 16.78
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700835 }
836})json",
837 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
838 {.multi_line = true}));
839 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800840 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700841}
842
843// Try to cover ~all supported scalar/flatbuffer types using JSON convenience
844// functions.
845TEST_F(StaticFlatbuffersTest, FlatbufferTypeCoverage) {
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800846 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700847 std::string populated_config =
848 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
849 "aos/flatbuffers/test_dir/type_coverage.json"));
850 // Get rid of a pesky new line.
851 populated_config = populated_config.substr(0, populated_config.size() - 1);
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800852 VerifyJson<aos::testing::ConfigurationStatic>(populated_config);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700853
854 // And now play around with mutating the buffer.
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800855 Builder<aos::testing::ConfigurationStatic> builder =
856 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700857 populated_config);
858 ASSERT_TRUE(builder.Verify());
859 builder.get()->clear_foo_float();
860 ASSERT_TRUE(builder.Verify());
861 ASSERT_FALSE(builder.get()->AsFlatbuffer().has_foo_float());
862 builder.get()->set_foo_float(1.111);
863 ASSERT_TRUE(builder.Verify());
864 ASSERT_FLOAT_EQ(1.111, builder.get()->AsFlatbuffer().foo_float());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800865 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700866}
867
James Kuszmaula75cd7c2023-12-07 15:52:51 -0800868TEST_F(StaticFlatbuffersTest, MinimallyAlignedTable) {
869 VerifyJson<MinimallyAlignedTableStatic>("{\n \"field\": 123\n}");
870 static_assert(4u == alignof(uoffset_t),
871 "The alignment of a uoffset_t is expected to be 4.");
872 ASSERT_EQ(alignof(uoffset_t), MinimallyAlignedTableStatic::kAlign)
873 << "No table should have an alignment of less than the alignment of the "
874 "table's root offset.";
875}
876
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700877// Confirm that we can use the SpanAllocator with a span that provides exactly
878// the required buffer size.
879TEST_F(StaticFlatbuffersTest, ExactSizeSpanAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700880 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800881 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700882 Builder<TestTableStatic> builder(&allocator);
883 TestTableStatic *object = builder.get();
884 object->set_scalar(123);
885 {
886 auto vector = object->add_vector_of_scalars();
887 ASSERT_TRUE(vector->emplace_back(4));
888 ASSERT_TRUE(vector->emplace_back(5));
889 }
890 {
891 auto string = object->add_string();
892 string->SetString("Hello, World!");
893 }
894 {
895 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -0700896 auto sub_string = vector_of_strings->emplace_back();
897 ASSERT_TRUE(sub_string != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700898 ASSERT_TRUE(sub_string->emplace_back('D'));
899 }
900 { object->set_substruct({971, 254}); }
901 {
902 auto subtable = object->add_subtable();
903 subtable->set_foo(1234);
904 }
905 {
906 auto vector = object->add_vector_of_structs();
907 ASSERT_TRUE(vector->emplace_back({48, 67}));
908 ASSERT_TRUE(vector->emplace_back({118, 148}));
909 ASSERT_TRUE(vector->emplace_back({971, 973}));
910 // Max vector size is three; this should fail.
911 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
912 // We don't have any extra space available.
913 ASSERT_FALSE(vector->reserve(4));
914 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
915 }
916 {
917 auto vector = object->add_vector_of_tables();
918 auto subobject = vector->emplace_back();
919 subobject->set_foo(222);
920 }
921 {
922 auto subtable = object->add_included_table();
923 subtable->set_foo(included::TestEnum::B);
924 }
925 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
926 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
927 {.multi_line = true});
928 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800929 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700930}
931
932// Test that when we provide too small of a span to the Builder that it
933// correctly fails.
934TEST_F(StaticFlatbuffersTest, TooSmallSpanAllocator) {
935 std::vector<uint8_t> buffer;
936 buffer.resize(10, 0);
937 aos::fbs::SpanAllocator allocator({buffer.data(), buffer.size()});
938 EXPECT_DEATH(Builder<TestTableStatic>{&allocator}, "Failed to allocate");
939}
940
941// Verify that if we create a span with extra headroom that that lets us
942// dynamically alter the size of vectors in the flatbuffers.
943TEST_F(StaticFlatbuffersTest, ExtraLargeSpanAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700944 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize + 200 * 64];
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800945 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700946 Builder<TestTableStatic> builder(&allocator);
947 TestTableStatic *object = builder.get();
948 {
949 auto vector = object->add_unspecified_length_vector();
950 // Confirm that the vector does indeed start out at zero length.
Austin Schuhf8440852024-05-31 10:46:50 -0700951 ASSERT_EQ(vector->capacity(), 60);
952 for (size_t i = 0; i < 60; ++i) {
953 ASSERT_TRUE(vector->emplace_back(i));
954 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700955 ASSERT_FALSE(vector->emplace_back(4));
956 ASSERT_TRUE(vector->reserve(9000));
957 vector->resize(256);
Austin Schuhf8440852024-05-31 10:46:50 -0700958 for (size_t index = 60; index < 256; ++index) {
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700959 vector->at(index) = static_cast<uint8_t>(index);
960 }
961 }
962 ASSERT_EQ(256, object->AsFlatbuffer().unspecified_length_vector()->size());
963 size_t expected = 0;
964 for (const uint8_t value :
965 *object->AsFlatbuffer().unspecified_length_vector()) {
966 EXPECT_EQ(expected++, value);
967 }
James Kuszmaul22448052023-12-14 15:55:14 -0800968 expected = 0;
969 for (const uint8_t value : *object->unspecified_length_vector()) {
970 EXPECT_EQ(expected++, value);
971 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800972 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700973}
James Kuszmaul22448052023-12-14 15:55:14 -0800974
975// Tests that the iterators on the Vector type work.
976TEST_F(StaticFlatbuffersTest, IteratorTest) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700977 Builder<TestTableStatic> builder(std::make_unique<AlignedVectorAllocator>());
James Kuszmaul22448052023-12-14 15:55:14 -0800978 {
979 auto vector = builder->add_unspecified_length_vector();
980 ASSERT_TRUE(vector->reserve(9000));
981 vector->resize(256);
982 uint8_t set_value = 0;
983 for (uint8_t &destination : *vector) {
984 destination = set_value;
985 ++set_value;
986 }
987 uint8_t expected = 0;
988 for (const uint8_t value : *builder->unspecified_length_vector()) {
989 EXPECT_EQ(expected, value);
990 ++expected;
991 }
992 // Exercise some of the random access iterator functionality to ensure that
993 // we have it implemented.
994 auto begin_it = vector->begin();
995 EXPECT_EQ(begin_it + 256, vector->end());
996 EXPECT_EQ(7, *(begin_it + 7));
997 EXPECT_EQ(255, *(vector->end() - 1));
998 EXPECT_EQ(256, vector->end() - vector->begin());
999 EXPECT_EQ(-256, vector->begin() - vector->end());
1000 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1001 "The vector iterator does not meet the requirements of a "
1002 "random access iterator.");
1003 }
1004 {
1005 auto vector = builder->add_vector_of_structs();
1006 vector->resize(3);
1007 double set_value = 0;
1008 for (SubStruct &destination : *vector) {
1009 destination.mutate_x(set_value);
1010 destination.mutate_y(-set_value);
1011 set_value += 1.0;
1012 }
1013 double expected = 0;
1014 for (const SubStruct &value : *builder->vector_of_structs()) {
1015 EXPECT_EQ(expected, value.x());
1016 EXPECT_EQ(-expected, value.y());
1017 expected += 1.0;
1018 }
1019 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1020 "The vector iterator does not meet the requirements of a "
1021 "random access iterator.");
1022 }
1023 {
1024 auto vector = builder->add_vector_of_tables();
1025 vector->resize(3);
1026 int set_value = 0;
1027 for (SubTableStatic &destination : *vector) {
1028 destination.set_foo(set_value);
1029 set_value += 1;
1030 }
1031 int expected = 0;
1032 for (const SubTableStatic &value : *builder->vector_of_tables()) {
1033 EXPECT_EQ(expected, value.foo());
1034 EXPECT_FALSE(value.has_baz());
1035 expected += 1;
1036 }
1037 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1038 "The vector iterator does not meet the requirements of a "
1039 "random access iterator.");
1040 }
1041}
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001042
1043// Confirm that we can use the FixedStackAllocator
1044TEST_F(StaticFlatbuffersTest, FixedStackAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -07001045 aos::fbs::FixedStackAllocator<Builder<TestTableStatic>::kBufferSize,
1046 Builder<TestTableStatic>::kAlign>
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001047 allocator;
1048 Builder<TestTableStatic> builder(&allocator);
1049 TestTableStatic *object = builder.get();
1050 object->set_scalar(123);
1051 {
1052 auto vector = object->add_vector_of_scalars();
1053 ASSERT_TRUE(vector->emplace_back(4));
1054 ASSERT_TRUE(vector->emplace_back(5));
1055 }
1056 {
1057 auto string = object->add_string();
1058 string->SetString("Hello, World!");
1059 }
1060 {
1061 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -07001062 auto sub_string = vector_of_strings->emplace_back();
1063 ASSERT_TRUE(sub_string != nullptr);
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001064 ASSERT_TRUE(sub_string->emplace_back('D'));
1065 }
1066 { object->set_substruct({971, 254}); }
1067 {
1068 auto subtable = object->add_subtable();
1069 subtable->set_foo(1234);
1070 }
1071 {
1072 auto vector = object->add_vector_of_structs();
1073 ASSERT_TRUE(vector->emplace_back({48, 67}));
1074 ASSERT_TRUE(vector->emplace_back({118, 148}));
1075 ASSERT_TRUE(vector->emplace_back({971, 973}));
1076 // Max vector size is three; this should fail.
1077 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1078 // We don't have any extra space available.
1079 ASSERT_FALSE(vector->reserve(4));
1080 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1081 }
1082 {
1083 auto vector = object->add_vector_of_tables();
1084 auto subobject = vector->emplace_back();
1085 subobject->set_foo(222);
1086 }
1087 {
1088 auto subtable = object->add_included_table();
1089 subtable->set_foo(included::TestEnum::B);
1090 }
1091 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1092 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1093 {.multi_line = true});
1094 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1095 TestMemory(builder.buffer());
1096}
1097
James Kuszmaul6be41022023-12-20 11:55:28 -08001098// Uses a small example to manually verify that we can copy from the flatbuffer
1099// object API.
1100TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
1101 aos::fbs::testing::TestTableT object_t;
1102 object_t.scalar = 971;
1103 object_t.vector_of_strings.push_back("971");
1104 object_t.vector_of_structs.push_back({1, 2});
1105 object_t.subtable = std::make_unique<SubTableT>();
Austin Schuh02e0d772024-05-30 16:41:06 -07001106 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaul6be41022023-12-20 11:55:28 -08001107 Builder<TestTableStatic> builder(&allocator);
1108 ASSERT_TRUE(builder->FromFlatbuffer(object_t));
1109 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1110 // Note that vectors and strings get set to zero-length, but present, values.
1111 EXPECT_EQ(
1112 "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
1113 "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
James Kuszmaul98c798d2024-04-24 15:58:09 -07001114 "\"baz\": 0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
1115 "\"x\": 1, \"y\": 2 } ], \"vector_of_tables\": [ ], "
James Kuszmaul6be41022023-12-20 11:55:28 -08001116 "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
1117 "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
1118 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
1119}
1120
1121// More completely covers our object API copying by comparing the flatbuffer
1122// Pack() methods to our FromFlatbuffer() methods.
1123TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
1124 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
1125 std::string populated_config =
1126 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
1127 "aos/flatbuffers/test_dir/type_coverage.json"));
1128 Builder<aos::testing::ConfigurationStatic> json_builder =
1129 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
1130 populated_config);
1131 aos::testing::ConfigurationT object_t;
1132 json_builder->AsFlatbuffer().UnPackTo(&object_t);
1133
1134 Builder<aos::testing::ConfigurationStatic> from_object_static;
1135 ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
1136 flatbuffers::FlatBufferBuilder fbb;
1137 fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
1138 aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
1139 fbb.Release();
1140 EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
1141 aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
1142}
1143
James Kuszmaul1e57af92023-12-20 15:34:54 -08001144// Tests that we can build code that uses the reflection types.
1145TEST_F(StaticFlatbuffersTest, IncludeReflectionTypes) {
1146 VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
1147}
1148
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001149// Tests that we can use the move constructor on a Builder.
1150TEST_F(StaticFlatbuffersTest, BuilderMoveConstructor) {
Austin Schuh02e0d772024-05-30 16:41:06 -07001151 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001152 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
1153 Builder<TestTableStatic> builder_from(&allocator);
1154 Builder<TestTableStatic> builder(std::move(builder_from));
1155 TestTableStatic *object = builder.get();
1156 object->set_scalar(123);
1157 {
1158 auto vector = object->add_vector_of_scalars();
1159 ASSERT_TRUE(vector->emplace_back(4));
1160 ASSERT_TRUE(vector->emplace_back(5));
1161 }
1162 {
1163 auto string = object->add_string();
1164 string->SetString("Hello, World!");
1165 }
1166 {
1167 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -07001168 auto sub_string = vector_of_strings->emplace_back();
1169 ASSERT_TRUE(sub_string != nullptr);
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001170 ASSERT_TRUE(sub_string->emplace_back('D'));
1171 }
1172 { object->set_substruct({971, 254}); }
1173 {
1174 auto subtable = object->add_subtable();
1175 subtable->set_foo(1234);
1176 }
1177 {
1178 auto vector = object->add_vector_of_structs();
1179 ASSERT_TRUE(vector->emplace_back({48, 67}));
1180 ASSERT_TRUE(vector->emplace_back({118, 148}));
1181 ASSERT_TRUE(vector->emplace_back({971, 973}));
1182 // Max vector size is three; this should fail.
1183 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1184 // We don't have any extra space available.
1185 ASSERT_FALSE(vector->reserve(4));
1186 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1187 }
1188 {
1189 auto vector = object->add_vector_of_tables();
1190 auto subobject = vector->emplace_back();
1191 subobject->set_foo(222);
1192 }
1193 {
1194 auto subtable = object->add_included_table();
1195 subtable->set_foo(included::TestEnum::B);
1196 }
1197 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1198 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1199 {.multi_line = true});
1200 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1201 TestMemory(builder.buffer());
1202}
1203
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001204} // namespace aos::fbs::testing