blob: aee80670d27c5f5402960c80810d8fc42b345c00 [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
Austin Schuh99f7c6a2024-06-25 22:07:44 -070015#include "absl/log/check.h"
16#include "absl/log/log.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070017#include "absl/strings/str_format.h"
18#include "absl/strings/str_join.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070019#include "absl/types/span.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070020#include "external/com_github_google_flatbuffers/src/annotated_binary_text_gen.h"
21#include "external/com_github_google_flatbuffers/src/binary_annotator.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070022#include "flatbuffers/base.h"
23#include "flatbuffers/buffer.h"
24#include "flatbuffers/flatbuffer_builder.h"
25#include "flatbuffers/stl_emulation.h"
26#include "flatbuffers/string.h"
27#include "flatbuffers/vector.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070028#include "gtest/gtest.h"
29
30#include "aos/flatbuffers.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070031#include "aos/flatbuffers/base.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070032#include "aos/flatbuffers/builder.h"
33#include "aos/flatbuffers/interesting_schemas.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070034#include "aos/flatbuffers/static_vector.h"
35#include "aos/flatbuffers/test_dir/include_generated.h"
James Kuszmaul1e57af92023-12-20 15:34:54 -080036#include "aos/flatbuffers/test_dir/include_reflection_static.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070037#include "aos/flatbuffers/test_dir/include_static.h"
38#include "aos/flatbuffers/test_dir/type_coverage_generated.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070039#include "aos/flatbuffers/test_dir/type_coverage_static.h"
Stephan Pleines6191f1d2024-05-30 20:44:45 -070040#include "aos/flatbuffers/test_generated.h"
James Kuszmaulf5eb4682023-09-22 17:16:59 -070041#include "aos/flatbuffers/test_schema.h"
42#include "aos/flatbuffers/test_static.h"
43#include "aos/json_to_flatbuffer.h"
44#include "aos/testing/path.h"
45#include "aos/testing/tmpdir.h"
46#include "aos/util/file.h"
47
48namespace aos::fbs::testing {
49
50namespace {
51// Uses the binary schema to annotate a provided flatbuffer. Returns the
52// annotated flatbuffer.
53std::string AnnotateBinaries(
54 const aos::NonSizePrefixedFlatbuffer<reflection::Schema> &schema,
55 flatbuffers::span<uint8_t> binary_data) {
56 flatbuffers::BinaryAnnotator binary_annotator(
57 schema.span().data(), schema.span().size(), binary_data.data(),
58 binary_data.size());
59
60 auto annotations = binary_annotator.Annotate();
61 const std::string schema_filename =
62 aos::testing::TestTmpDir() + "/schema.bfbs";
63
64 aos::WriteFlatbufferToFile(schema_filename, schema);
65
66 flatbuffers::AnnotatedBinaryTextGenerator text_generator(
67 flatbuffers::AnnotatedBinaryTextGenerator::Options{}, annotations,
68 binary_data.data(), binary_data.size());
69
70 text_generator.Generate(aos::testing::TestTmpDir() + "/foo.bfbs",
71 schema_filename);
72
73 return aos::util::ReadFileToStringOrDie(aos::testing::TestTmpDir() +
74 "/foo.afb");
75}
76const reflection::Object *GetObjectByName(const reflection::Schema *schema,
77 std::string_view name) {
78 for (const reflection::Object *object : *schema->objects()) {
79 if (object->name()->string_view() == name) {
80 return object;
81 }
82 }
83 return nullptr;
84}
James Kuszmaul1c9693f2023-12-08 09:45:26 -080085
86// Accesses all the values in the supplied span. Used to ensure that memory
87// sanitizers can observe uninitialized memory.
88void TestMemory(std::span<uint8_t> memory) {
89 std::stringstream str;
90 internal::DebugBytes(memory, str);
91 EXPECT_LT(0u, str.view().size());
92}
James Kuszmaulf5eb4682023-09-22 17:16:59 -070093} // namespace
94
95class StaticFlatbuffersTest : public ::testing::Test {
96 protected:
97 template <typename T>
98 void VerifyJson(const std::string_view data) {
99 Builder<T> json_builder = aos::JsonToStaticFlatbuffer<T>(data);
100
101 EXPECT_EQ(data, aos::FlatbufferToJson(json_builder.AsFlatbufferSpan(),
102 {.multi_line = true}));
103 }
104 aos::FlatbufferSpan<reflection::Schema> test_schema_{TestTableSchema()};
105 aos::FlatbufferSpan<reflection::Schema> interesting_schemas_{
106 UnsupportedSchema()};
107};
108
109// Test that compiles the same code that is used by an example in
110// //aos/documentation/aos/docs/flatbuffers.md.
111TEST_F(StaticFlatbuffersTest, DocumentationExample) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700112 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700113 Builder<TestTableStatic> builder(&allocator);
114 TestTableStatic *object = builder.get();
115 object->set_scalar(123);
116 {
117 auto vector = object->add_vector_of_scalars();
118 CHECK(vector->emplace_back(4));
119 CHECK(vector->emplace_back(5));
120 }
121 {
122 auto string = object->add_string();
123 string->SetString("Hello, World!");
124 }
125 {
126 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -0700127 auto sub_string = vector_of_strings->emplace_back();
128 CHECK(sub_string != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700129 CHECK(sub_string->emplace_back('D'));
130 }
131 { object->set_substruct({971, 254}); }
132 {
133 auto subtable = object->add_subtable();
134 subtable->set_foo(1234);
135 }
136 {
137 auto vector = object->add_vector_of_structs();
138 CHECK(vector->emplace_back({48, 67}));
139 CHECK(vector->emplace_back({118, 148}));
140 CHECK(vector->emplace_back({971, 973}));
141 // Max vector size is three; this should fail.
142 CHECK(!vector->emplace_back({1114, 2056}));
143 }
144 {
145 auto vector = object->add_vector_of_tables();
146 auto subobject = vector->emplace_back();
147 subobject->set_foo(222);
148 }
149 {
150 auto subtable = object->add_included_table();
151 subtable->set_foo(included::TestEnum::B);
152 }
153 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
154 LOG(INFO) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
155 {.multi_line = true});
156 LOG(INFO) << AnnotateBinaries(test_schema_, builder.buffer());
157}
158
159// Test that compiles the same code that is used by an example in
160// //aos/documentation/aos/docs/flatbuffers.md showing how to convert a
161// Populate*() method for adding a subtable to a flatbuffer.
162namespace {
163flatbuffers::Offset<SubTable> PopulateOld(flatbuffers::FlatBufferBuilder *fbb) {
164 SubTable::Builder builder(*fbb);
165 builder.add_foo(1234);
166 return builder.Finish();
167}
Austin Schuh6bdcc372024-06-27 14:49:11 -0700168void PopulateStatic(SubTableStatic *subtable) {
169 CHECK(subtable != nullptr);
170 subtable->set_foo(1234);
171}
172
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700173} // namespace
Austin Schuh6bdcc372024-06-27 14:49:11 -0700174
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700175TEST_F(StaticFlatbuffersTest, PopulateMethodConversionExample) {
176 // Using a FlatBufferBuilder:
177 flatbuffers::FlatBufferBuilder fbb;
178 // Note: the PopulateOld() *must* be called prior to creating the builder.
179 const flatbuffers::Offset<SubTable> subtable_offset = PopulateOld(&fbb);
180 TestTable::Builder testtable_builder(fbb);
181 testtable_builder.add_subtable(subtable_offset);
182 fbb.Finish(testtable_builder.Finish());
183 aos::FlatbufferDetachedBuffer<TestTable> fbb_finished = fbb.Release();
184
185 // Using the static flatbuffer API.
Austin Schuh02e0d772024-05-30 16:41:06 -0700186 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700187 Builder<TestTableStatic> static_builder(&allocator);
Austin Schuh6bdcc372024-06-27 14:49:11 -0700188 PopulateStatic(static_builder.get()->add_subtable());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700189
190 // And confirm that they both contain the expected flatbuffer:
191 const std::string expected = R"json({ "subtable": { "foo": 1234 } })json";
192 EXPECT_EQ(expected, aos::FlatbufferToJson(fbb_finished));
193 EXPECT_EQ(expected, aos::FlatbufferToJson(static_builder.AsFlatbufferSpan()));
194}
195
196TEST_F(StaticFlatbuffersTest, UnsupportedSchema) {
197 const reflection::Schema *schema = &interesting_schemas_.message();
198 EXPECT_DEATH(
199 GenerateCodeForObject(
200 schema, GetObjectByName(schema, "aos.fbs.testing.TableWithUnion")),
201 "Union not supported");
202 GenerateCodeForObject(
203 schema, GetObjectByName(schema, "aos.fbs.testing.MissingVectorLength"));
204 EXPECT_DEATH(
205 GenerateCodeForObject(
206 schema,
207 GetObjectByName(schema, "aos.fbs.testing.NonIntegerVectorLength")),
208 "vector_badlength must specify a positive integer for the "
209 "static_length attribute.");
210 EXPECT_DEATH(GenerateCodeForObject(
211 schema, GetObjectByName(
212 schema, "aos.fbs.testing.NegativeVectorLength")),
213 "Field vector_badlength must have a non-negative "
214 "static_length.");
215 GenerateCodeForObject(
216 schema, GetObjectByName(schema, "aos.fbs.testing.ZeroVectorLength"));
217 GenerateCodeForObject(
218 schema, GetObjectByName(schema, "aos.fbs.testing.MissingStringLength"));
219 GenerateCodeForObject(
220 schema,
221 GetObjectByName(schema, "aos.fbs.testing.MissingSubStringLength"));
222}
223
224// Tests that we can go through and manually build up a big flatbuffer and that
225// it stays valid at all points.
226TEST_F(StaticFlatbuffersTest, ManuallyConstructFlatbuffer) {
227 {
Austin Schuh02e0d772024-05-30 16:41:06 -0700228 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700229 Builder<SubTableStatic> builder(&allocator);
230 SubTableStatic *object = builder.get();
231 if (!builder.AsFlatbufferSpan().Verify()) {
232 LOG(ERROR) << object->SerializationDebugString() << "\nRoot table offset "
233 << *reinterpret_cast<const uoffset_t *>(
234 builder.buffer().data())
235 << "\nraw bytes\n";
236 aos::fbs::internal::DebugBytes(builder.buffer(), std::cerr);
237 FAIL();
238 return;
239 }
240 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
241 object->set_foo(123);
242 object->set_baz(971);
243 CHECK(builder.AsFlatbufferSpan().Verify());
244 EXPECT_EQ(123, object->AsFlatbuffer().foo());
245 EXPECT_EQ(971, object->AsFlatbuffer().baz());
James Kuszmaul98c798d2024-04-24 15:58:09 -0700246 EXPECT_EQ(R"json({ "foo": 123, "baz": 971 })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700247 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800248 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700249 }
250 {
Austin Schuh02e0d772024-05-30 16:41:06 -0700251 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700252 Builder<TestTableStatic> builder(&allocator);
253 TestTableStatic *object = builder.get();
254 const aos::fbs::testing::TestTable &fbs = object->AsFlatbuffer();
255 VLOG(1) << object->SerializationDebugString();
256 CHECK(builder.AsFlatbufferSpan().Verify());
257 EXPECT_EQ("{ }", aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
258 {
259 ASSERT_FALSE(object->has_scalar());
260 object->set_scalar(123);
261 EXPECT_TRUE(fbs.has_scalar());
262 EXPECT_EQ(123, fbs.scalar());
263 }
264 EXPECT_EQ(R"json({ "scalar": 123 })json",
265 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
266 {
267 ASSERT_FALSE(object->has_vector_of_scalars());
268 auto vector = object->add_vector_of_scalars();
269 ASSERT_TRUE(vector->emplace_back(4));
270 ASSERT_TRUE(vector->emplace_back(5));
271 ASSERT_TRUE(object->has_vector_of_scalars());
272 ASSERT_TRUE(fbs.has_vector_of_scalars());
273 VLOG(1) << vector->SerializationDebugString();
274 EXPECT_TRUE(fbs.has_vector_of_scalars());
275 EXPECT_EQ(2u, fbs.vector_of_scalars()->size());
276 EXPECT_EQ(4, fbs.vector_of_scalars()->Get(0));
277 EXPECT_EQ(5, fbs.vector_of_scalars()->Get(1));
278 }
279 EXPECT_EQ(R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ] })json",
280 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
281 {
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700282 // Set the string to a value that is as long as its static length. An
283 // extra byte for the null character should be automatically allocated.
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700284 EXPECT_FALSE(object->has_string());
285 auto string = object->add_string();
286 EXPECT_TRUE(object->has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700287 string->SetString("This is twenty chars");
288 EXPECT_EQ(20u, object->string()->size());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700289 ASSERT_TRUE(fbs.has_string());
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700290 ASSERT_EQ(20u, fbs.string()->size());
291 EXPECT_EQ("This is twenty chars", fbs.string()->string_view());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700292 // Check that we null-terminated correctly.
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700293 EXPECT_EQ(20u, strnlen(fbs.string()->c_str(), 21));
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700294 }
295 EXPECT_EQ(
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700296 R"json({ "scalar": 123, "vector_of_scalars": [ 4, 5 ], "string": "This is twenty chars" })json",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700297 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
298 {
299 EXPECT_FALSE(object->has_vector_of_strings());
300 auto vector_of_strings = object->add_vector_of_strings();
301 EXPECT_TRUE(object->has_vector_of_strings());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700302 auto sub_string = vector_of_strings->emplace_back();
303 CHECK(sub_string != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700304 ASSERT_TRUE(sub_string->emplace_back('D'));
305 EXPECT_TRUE(fbs.has_vector_of_strings());
306 ASSERT_EQ(1u, fbs.vector_of_strings()->size());
307 ASSERT_EQ(1u, fbs.vector_of_strings()->Get(0)->size());
308 EXPECT_EQ('D', fbs.vector_of_strings()->Get(0)->Get(0));
309 }
310 EXPECT_EQ(
311 R"json({
312 "scalar": 123,
313 "vector_of_scalars": [
314 4,
315 5
316 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700317 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700318 "vector_of_strings": [
319 "D"
320 ]
321})json",
322 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
323 {.multi_line = true}));
324 {
325 EXPECT_FALSE(object->has_substruct());
326 object->set_substruct({971, 254});
327 EXPECT_TRUE(object->has_substruct());
328 EXPECT_TRUE(fbs.has_substruct());
329 EXPECT_EQ(971, fbs.substruct()->x());
330 EXPECT_EQ(254, fbs.substruct()->y());
331 }
332 EXPECT_EQ(
333 R"json({
334 "scalar": 123,
335 "vector_of_scalars": [
336 4,
337 5
338 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700339 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700340 "vector_of_strings": [
341 "D"
342 ],
343 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700344 "x": 971,
345 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700346 }
347})json",
348 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
349 {.multi_line = true}));
350 {
351 auto subtable = object->add_subtable();
352 subtable->set_foo(1234);
353 EXPECT_TRUE(fbs.has_subtable());
354 EXPECT_EQ(1234, fbs.subtable()->foo());
355 EXPECT_FALSE(fbs.subtable()->has_baz());
356 }
357 EXPECT_EQ(
358 R"json({
359 "scalar": 123,
360 "vector_of_scalars": [
361 4,
362 5
363 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700364 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700365 "vector_of_strings": [
366 "D"
367 ],
368 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700369 "x": 971,
370 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700371 },
372 "subtable": {
373 "foo": 1234
374 }
375})json",
376 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
377 {.multi_line = true}));
378 {
379 auto vector = object->add_vector_of_structs();
380 ASSERT_TRUE(vector->emplace_back({48, 67}));
381 ASSERT_TRUE(vector->emplace_back({118, 148}));
382 ASSERT_TRUE(vector->emplace_back({971, 973}));
383 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
384 EXPECT_TRUE(fbs.has_vector_of_structs());
385 EXPECT_EQ(3u, fbs.vector_of_structs()->size());
386 EXPECT_EQ(48, fbs.vector_of_structs()->Get(0)->x());
387 EXPECT_EQ(67, fbs.vector_of_structs()->Get(0)->y());
388 EXPECT_EQ(118, fbs.vector_of_structs()->Get(1)->x());
389 EXPECT_EQ(object->vector_of_structs()->at(1).x(),
390 fbs.vector_of_structs()->Get(1)->x());
391 EXPECT_EQ((*object->vector_of_structs())[1].x(),
392 fbs.vector_of_structs()->Get(1)->x());
393 EXPECT_EQ(148, fbs.vector_of_structs()->Get(1)->y());
394 EXPECT_EQ(971, fbs.vector_of_structs()->Get(2)->x());
395 EXPECT_EQ(973, fbs.vector_of_structs()->Get(2)->y());
396 }
397 EXPECT_EQ(
398 R"json({
399 "scalar": 123,
400 "vector_of_scalars": [
401 4,
402 5
403 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700404 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700405 "vector_of_strings": [
406 "D"
407 ],
408 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700409 "x": 971,
410 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700411 },
412 "subtable": {
413 "foo": 1234
414 },
415 "vector_of_structs": [
416 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700417 "x": 48,
418 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700419 },
420 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700421 "x": 118,
422 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700423 },
424 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700425 "x": 971,
426 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700427 }
428 ]
429})json",
430 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
431 {.multi_line = true}));
432 {
433 EXPECT_FALSE(object->has_vector_of_tables());
434 auto vector = object->add_vector_of_tables();
435 EXPECT_TRUE(object->has_vector_of_tables());
436 auto subobject = vector->emplace_back();
437 subobject->set_foo(222);
438 EXPECT_TRUE(fbs.has_vector_of_tables());
439 EXPECT_EQ(1u, fbs.vector_of_tables()->size());
440 EXPECT_EQ(222, fbs.vector_of_tables()->Get(0)->foo());
441 EXPECT_EQ(object->vector_of_tables()->at(0).foo(),
442 fbs.vector_of_tables()->Get(0)->foo());
443 }
444 EXPECT_EQ(
445 R"json({
446 "scalar": 123,
447 "vector_of_scalars": [
448 4,
449 5
450 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700451 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700452 "vector_of_strings": [
453 "D"
454 ],
455 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700456 "x": 971,
457 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700458 },
459 "subtable": {
460 "foo": 1234
461 },
462 "vector_of_structs": [
463 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700464 "x": 48,
465 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700466 },
467 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700468 "x": 118,
469 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700470 },
471 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700472 "x": 971,
473 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700474 }
475 ],
476 "vector_of_tables": [
477 {
478 "foo": 222
479 }
480 ]
481})json",
482 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
483 {.multi_line = true}));
484 {
485 EXPECT_FALSE(object->has_included_table());
486 auto subtable = object->add_included_table();
487 EXPECT_TRUE(object->has_included_table());
488 subtable->set_foo(included::TestEnum::B);
489 ASSERT_TRUE(fbs.has_included_table());
490 ASSERT_TRUE(fbs.included_table()->has_foo());
491 EXPECT_EQ(included::TestEnum::B, fbs.included_table()->foo());
492 }
493 EXPECT_EQ(
494 R"json({
495 "scalar": 123,
496 "vector_of_scalars": [
497 4,
498 5
499 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700500 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700501 "vector_of_strings": [
502 "D"
503 ],
504 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700505 "x": 971,
506 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700507 },
508 "subtable": {
509 "foo": 1234
510 },
511 "vector_of_structs": [
512 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700513 "x": 48,
514 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700515 },
516 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700517 "x": 118,
518 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700519 },
520 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700521 "x": 971,
522 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700523 }
524 ],
525 "vector_of_tables": [
526 {
527 "foo": 222
528 }
529 ],
530 "included_table": {
531 "foo": "B"
532 }
533})json",
534 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
535 {.multi_line = true}));
536 {
537 auto aligned_vector = object->add_vector_aligned();
538 ASSERT_EQ(64,
539 std::remove_reference<decltype(*aligned_vector)>::type::kAlign);
540 ASSERT_EQ(64, TestTableStatic::kAlign);
541 ASSERT_TRUE(aligned_vector->emplace_back(444));
542 EXPECT_TRUE(fbs.has_vector_aligned());
543 EXPECT_EQ(1u, fbs.vector_aligned()->size());
544 EXPECT_EQ(0u,
545 reinterpret_cast<size_t>(fbs.vector_aligned()->data()) % 64);
546 EXPECT_EQ(444, fbs.vector_aligned()->Get(0));
547 }
548 VLOG(1) << object->SerializationDebugString();
549 CHECK(builder.AsFlatbufferSpan().Verify());
550 const std::string expected_contents =
551 R"json({
552 "scalar": 123,
553 "vector_of_scalars": [
554 4,
555 5
556 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700557 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700558 "vector_of_strings": [
559 "D"
560 ],
561 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700562 "x": 971,
563 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700564 },
565 "subtable": {
566 "foo": 1234
567 },
568 "vector_aligned": [
569 444
570 ],
571 "vector_of_structs": [
572 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700573 "x": 48,
574 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700575 },
576 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700577 "x": 118,
578 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700579 },
580 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700581 "x": 971,
582 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700583 }
584 ],
585 "vector_of_tables": [
586 {
587 "foo": 222
588 }
589 ],
590 "included_table": {
591 "foo": "B"
592 }
593})json";
594 EXPECT_EQ(expected_contents,
595 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
596 {.multi_line = true}));
597 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
598 VerifyJson<TestTableStatic>(expected_contents);
599 {
600 auto aligned_vector = object->mutable_vector_aligned();
601 ASSERT_TRUE(aligned_vector->reserve(100));
Austin Schuhf8440852024-05-31 10:46:50 -0700602
603 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
604 // Since the allocator is going to allocate in blocks of 64, we end up
605 // with more capacity than we asked for. Better to have it than to leave
606 // it as unusable padding.
607 EXPECT_EQ(115, aligned_vector->capacity());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700608 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify())
609 << aligned_vector->SerializationDebugString();
610 EXPECT_EQ(expected_contents,
611 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
612 {.multi_line = true}));
613 std::vector<int> scalars;
614 scalars.push_back(aligned_vector->at(0));
615 while (aligned_vector->size() < 100u) {
616 scalars.push_back(aligned_vector->size());
617 CHECK(aligned_vector->emplace_back(aligned_vector->size()));
618 }
619 VLOG(1) << aligned_vector->SerializationDebugString();
620 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
621 EXPECT_EQ(absl::StrFormat(
622 R"json({
623 "scalar": 123,
624 "vector_of_scalars": [
625 4,
626 5
627 ],
Sanjay Narayanan71de06a2024-05-06 15:24:48 -0700628 "string": "This is twenty chars",
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700629 "vector_of_strings": [
630 "D"
631 ],
632 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700633 "x": 971,
634 "y": 254
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700635 },
636 "subtable": {
637 "foo": 1234
638 },
639 "vector_aligned": [
640 %s
641 ],
642 "vector_of_structs": [
643 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700644 "x": 48,
645 "y": 67
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700646 },
647 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700648 "x": 118,
649 "y": 148
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700650 },
651 {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700652 "x": 971,
653 "y": 973
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700654 }
655 ],
656 "vector_of_tables": [
657 {
658 "foo": 222
659 }
660 ],
661 "included_table": {
662 "foo": "B"
663 }
664})json",
665 absl::StrJoin(scalars, ",\n ")),
666 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
667 {.multi_line = true}));
668 }
669
670 {
671 auto unspecified_vector = object->add_unspecified_length_vector();
672 ASSERT_NE(nullptr, unspecified_vector);
Austin Schuhf8440852024-05-31 10:46:50 -0700673 ASSERT_EQ(60, unspecified_vector->capacity());
674 for (size_t i = 0; i < 60; ++i) {
675 ASSERT_TRUE(unspecified_vector->emplace_back(0));
676 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700677 ASSERT_FALSE(unspecified_vector->emplace_back(0));
Austin Schuhf8440852024-05-31 10:46:50 -0700678 ASSERT_TRUE(unspecified_vector->reserve(64));
679 ASSERT_EQ(124, unspecified_vector->capacity());
680 for (size_t i = 0; i < 64; ++i) {
681 ASSERT_TRUE(unspecified_vector->emplace_back(1));
682 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700683 ASSERT_FALSE(unspecified_vector->emplace_back(3));
684 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
685 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800686 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700687 }
688}
689
690// Tests that field clearing (and subsequent resetting) works properly.
691TEST_F(StaticFlatbuffersTest, ClearFields) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700692 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700693 Builder<TestTableStatic> builder(&allocator);
694 TestTableStatic *object = builder.get();
695 // For each field, we will confirm the following:
696 // * Clearing a non-existent field causes no issues.
697 // * We can set a field, clear it, and have it not be present.
698 // * We can set the field again afterwards.
699 {
700 object->clear_scalar();
701 ASSERT_TRUE(builder.Verify());
702 object->set_scalar(123);
703 EXPECT_EQ(123, object->AsFlatbuffer().scalar());
704 object->clear_scalar();
705 ASSERT_TRUE(builder.Verify());
706 EXPECT_FALSE(object->has_scalar());
707 object->set_scalar(456);
708 EXPECT_EQ(456, object->AsFlatbuffer().scalar());
709 }
710 {
711 object->clear_vector_of_scalars();
712 ASSERT_TRUE(builder.Verify());
713 EXPECT_FALSE(object->has_vector_of_scalars());
714 auto vector = object->add_vector_of_scalars();
715 ASSERT_TRUE(vector->emplace_back(4));
716 ASSERT_TRUE(vector->emplace_back(5));
717 ASSERT_TRUE(vector->emplace_back(6));
718 // Deliberately force a resize of the vector to ensure that we can exercise
719 // what happens if we clear a non-standard size field.
720 ASSERT_FALSE(vector->emplace_back(7));
721 ASSERT_TRUE(vector->reserve(4));
722 ASSERT_TRUE(vector->emplace_back(7));
723 EXPECT_EQ(
724 R"json({
725 "scalar": 456,
726 "vector_of_scalars": [
727 4,
728 5,
729 6,
730 7
731 ]
732})json",
733 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
734 {.multi_line = true}));
735 ASSERT_TRUE(builder.Verify());
736 object->clear_vector_of_scalars();
737 ASSERT_TRUE(builder.Verify());
738 ASSERT_FALSE(object->has_vector_of_scalars())
739 << aos::FlatbufferToJson(builder.AsFlatbufferSpan());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700740 vector = object->add_vector_of_scalars();
741 ASSERT_TRUE(vector != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700742 ASSERT_TRUE(builder.Verify());
743 EXPECT_EQ(0u, object->AsFlatbuffer().vector_of_scalars()->size());
744 ASSERT_TRUE(vector->emplace_back(9));
745 ASSERT_TRUE(vector->emplace_back(7));
746 ASSERT_TRUE(vector->emplace_back(1));
747 // This vector has no knowledge of the past resizing; it should fail to add
748 // an extra number.
749 ASSERT_FALSE(vector->emplace_back(7));
750 }
751 {
752 object->clear_substruct();
753 ASSERT_TRUE(builder.Verify());
754 EXPECT_FALSE(object->has_substruct());
755 object->set_substruct(SubStruct{2, 3});
756 EXPECT_EQ(
757 R"json({
758 "scalar": 456,
759 "vector_of_scalars": [
760 9,
761 7,
762 1
763 ],
764 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700765 "x": 2,
766 "y": 3
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700767 }
768})json",
769 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
770 {.multi_line = true}));
771 object->clear_substruct();
772 ASSERT_TRUE(builder.Verify());
773 EXPECT_FALSE(object->has_substruct());
774 object->set_substruct(SubStruct{4, 5});
775 EXPECT_EQ(
776 R"json({
777 "scalar": 456,
778 "vector_of_scalars": [
779 9,
780 7,
781 1
782 ],
783 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700784 "x": 4,
785 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700786 }
787})json",
788 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
789 {.multi_line = true}));
790 }
791 {
792 object->clear_subtable();
793 ASSERT_TRUE(builder.Verify());
794 EXPECT_FALSE(object->has_subtable());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700795 auto subtable = object->add_subtable();
796 ASSERT_TRUE(subtable != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700797 subtable->set_baz(9.71);
798 EXPECT_EQ(
799 R"json({
800 "scalar": 456,
801 "vector_of_scalars": [
802 9,
803 7,
804 1
805 ],
806 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700807 "x": 4,
808 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700809 },
810 "subtable": {
811 "baz": 9.71
812 }
813})json",
814 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
815 {.multi_line = true}));
816 object->clear_subtable();
817 ASSERT_TRUE(builder.Verify());
818 EXPECT_FALSE(object->has_subtable());
Austin Schuh6bdcc372024-06-27 14:49:11 -0700819 subtable = object->add_subtable();
820 ASSERT_TRUE(subtable != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700821 subtable->set_baz(16.78);
822 EXPECT_EQ(
823 R"json({
824 "scalar": 456,
825 "vector_of_scalars": [
826 9,
827 7,
828 1
829 ],
830 "substruct": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700831 "x": 4,
832 "y": 5
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700833 },
834 "subtable": {
James Kuszmaul98c798d2024-04-24 15:58:09 -0700835 "baz": 16.78
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700836 }
837})json",
838 aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
839 {.multi_line = true}));
840 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800841 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700842}
843
844// Try to cover ~all supported scalar/flatbuffer types using JSON convenience
845// functions.
846TEST_F(StaticFlatbuffersTest, FlatbufferTypeCoverage) {
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800847 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700848 std::string populated_config =
849 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
850 "aos/flatbuffers/test_dir/type_coverage.json"));
851 // Get rid of a pesky new line.
852 populated_config = populated_config.substr(0, populated_config.size() - 1);
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800853 VerifyJson<aos::testing::ConfigurationStatic>(populated_config);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700854
855 // And now play around with mutating the buffer.
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800856 Builder<aos::testing::ConfigurationStatic> builder =
857 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700858 populated_config);
859 ASSERT_TRUE(builder.Verify());
860 builder.get()->clear_foo_float();
861 ASSERT_TRUE(builder.Verify());
862 ASSERT_FALSE(builder.get()->AsFlatbuffer().has_foo_float());
863 builder.get()->set_foo_float(1.111);
864 ASSERT_TRUE(builder.Verify());
865 ASSERT_FLOAT_EQ(1.111, builder.get()->AsFlatbuffer().foo_float());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800866 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700867}
868
James Kuszmaula75cd7c2023-12-07 15:52:51 -0800869TEST_F(StaticFlatbuffersTest, MinimallyAlignedTable) {
870 VerifyJson<MinimallyAlignedTableStatic>("{\n \"field\": 123\n}");
871 static_assert(4u == alignof(uoffset_t),
872 "The alignment of a uoffset_t is expected to be 4.");
873 ASSERT_EQ(alignof(uoffset_t), MinimallyAlignedTableStatic::kAlign)
874 << "No table should have an alignment of less than the alignment of the "
875 "table's root offset.";
876}
877
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700878// Confirm that we can use the SpanAllocator with a span that provides exactly
879// the required buffer size.
880TEST_F(StaticFlatbuffersTest, ExactSizeSpanAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700881 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800882 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700883 Builder<TestTableStatic> builder(&allocator);
884 TestTableStatic *object = builder.get();
885 object->set_scalar(123);
886 {
887 auto vector = object->add_vector_of_scalars();
888 ASSERT_TRUE(vector->emplace_back(4));
889 ASSERT_TRUE(vector->emplace_back(5));
890 }
891 {
892 auto string = object->add_string();
893 string->SetString("Hello, World!");
894 }
895 {
896 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -0700897 auto sub_string = vector_of_strings->emplace_back();
898 ASSERT_TRUE(sub_string != nullptr);
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700899 ASSERT_TRUE(sub_string->emplace_back('D'));
900 }
901 { object->set_substruct({971, 254}); }
902 {
903 auto subtable = object->add_subtable();
904 subtable->set_foo(1234);
905 }
906 {
907 auto vector = object->add_vector_of_structs();
908 ASSERT_TRUE(vector->emplace_back({48, 67}));
909 ASSERT_TRUE(vector->emplace_back({118, 148}));
910 ASSERT_TRUE(vector->emplace_back({971, 973}));
911 // Max vector size is three; this should fail.
912 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
913 // We don't have any extra space available.
914 ASSERT_FALSE(vector->reserve(4));
915 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
916 }
917 {
918 auto vector = object->add_vector_of_tables();
919 auto subobject = vector->emplace_back();
920 subobject->set_foo(222);
921 }
922 {
923 auto subtable = object->add_included_table();
924 subtable->set_foo(included::TestEnum::B);
925 }
926 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
927 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
928 {.multi_line = true});
929 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800930 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700931}
932
933// Test that when we provide too small of a span to the Builder that it
934// correctly fails.
935TEST_F(StaticFlatbuffersTest, TooSmallSpanAllocator) {
936 std::vector<uint8_t> buffer;
937 buffer.resize(10, 0);
938 aos::fbs::SpanAllocator allocator({buffer.data(), buffer.size()});
939 EXPECT_DEATH(Builder<TestTableStatic>{&allocator}, "Failed to allocate");
940}
941
942// Verify that if we create a span with extra headroom that that lets us
943// dynamically alter the size of vectors in the flatbuffers.
944TEST_F(StaticFlatbuffersTest, ExtraLargeSpanAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700945 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize + 200 * 64];
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800946 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700947 Builder<TestTableStatic> builder(&allocator);
948 TestTableStatic *object = builder.get();
949 {
950 auto vector = object->add_unspecified_length_vector();
951 // Confirm that the vector does indeed start out at zero length.
Austin Schuhf8440852024-05-31 10:46:50 -0700952 ASSERT_EQ(vector->capacity(), 60);
953 for (size_t i = 0; i < 60; ++i) {
954 ASSERT_TRUE(vector->emplace_back(i));
955 }
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700956 ASSERT_FALSE(vector->emplace_back(4));
957 ASSERT_TRUE(vector->reserve(9000));
958 vector->resize(256);
Austin Schuhf8440852024-05-31 10:46:50 -0700959 for (size_t index = 60; index < 256; ++index) {
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700960 vector->at(index) = static_cast<uint8_t>(index);
961 }
962 }
963 ASSERT_EQ(256, object->AsFlatbuffer().unspecified_length_vector()->size());
964 size_t expected = 0;
965 for (const uint8_t value :
966 *object->AsFlatbuffer().unspecified_length_vector()) {
967 EXPECT_EQ(expected++, value);
968 }
James Kuszmaul22448052023-12-14 15:55:14 -0800969 expected = 0;
970 for (const uint8_t value : *object->unspecified_length_vector()) {
971 EXPECT_EQ(expected++, value);
972 }
James Kuszmaul1c9693f2023-12-08 09:45:26 -0800973 TestMemory(builder.buffer());
James Kuszmaulf5eb4682023-09-22 17:16:59 -0700974}
James Kuszmaul22448052023-12-14 15:55:14 -0800975
976// Tests that the iterators on the Vector type work.
977TEST_F(StaticFlatbuffersTest, IteratorTest) {
Austin Schuh02e0d772024-05-30 16:41:06 -0700978 Builder<TestTableStatic> builder(std::make_unique<AlignedVectorAllocator>());
James Kuszmaul22448052023-12-14 15:55:14 -0800979 {
980 auto vector = builder->add_unspecified_length_vector();
981 ASSERT_TRUE(vector->reserve(9000));
982 vector->resize(256);
983 uint8_t set_value = 0;
984 for (uint8_t &destination : *vector) {
985 destination = set_value;
986 ++set_value;
987 }
988 uint8_t expected = 0;
989 for (const uint8_t value : *builder->unspecified_length_vector()) {
990 EXPECT_EQ(expected, value);
991 ++expected;
992 }
993 // Exercise some of the random access iterator functionality to ensure that
994 // we have it implemented.
995 auto begin_it = vector->begin();
996 EXPECT_EQ(begin_it + 256, vector->end());
997 EXPECT_EQ(7, *(begin_it + 7));
998 EXPECT_EQ(255, *(vector->end() - 1));
999 EXPECT_EQ(256, vector->end() - vector->begin());
1000 EXPECT_EQ(-256, vector->begin() - vector->end());
1001 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1002 "The vector iterator does not meet the requirements of a "
1003 "random access iterator.");
1004 }
1005 {
1006 auto vector = builder->add_vector_of_structs();
1007 vector->resize(3);
1008 double set_value = 0;
1009 for (SubStruct &destination : *vector) {
1010 destination.mutate_x(set_value);
1011 destination.mutate_y(-set_value);
1012 set_value += 1.0;
1013 }
1014 double expected = 0;
1015 for (const SubStruct &value : *builder->vector_of_structs()) {
1016 EXPECT_EQ(expected, value.x());
1017 EXPECT_EQ(-expected, value.y());
1018 expected += 1.0;
1019 }
1020 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1021 "The vector iterator does not meet the requirements of a "
1022 "random access iterator.");
1023 }
1024 {
1025 auto vector = builder->add_vector_of_tables();
1026 vector->resize(3);
1027 int set_value = 0;
1028 for (SubTableStatic &destination : *vector) {
1029 destination.set_foo(set_value);
1030 set_value += 1;
1031 }
1032 int expected = 0;
1033 for (const SubTableStatic &value : *builder->vector_of_tables()) {
1034 EXPECT_EQ(expected, value.foo());
1035 EXPECT_FALSE(value.has_baz());
1036 expected += 1;
1037 }
1038 static_assert(std::random_access_iterator<decltype(vector->begin())>,
1039 "The vector iterator does not meet the requirements of a "
1040 "random access iterator.");
1041 }
1042}
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001043
1044// Confirm that we can use the FixedStackAllocator
1045TEST_F(StaticFlatbuffersTest, FixedStackAllocator) {
Austin Schuh02e0d772024-05-30 16:41:06 -07001046 aos::fbs::FixedStackAllocator<Builder<TestTableStatic>::kBufferSize,
1047 Builder<TestTableStatic>::kAlign>
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001048 allocator;
1049 Builder<TestTableStatic> builder(&allocator);
1050 TestTableStatic *object = builder.get();
1051 object->set_scalar(123);
1052 {
1053 auto vector = object->add_vector_of_scalars();
1054 ASSERT_TRUE(vector->emplace_back(4));
1055 ASSERT_TRUE(vector->emplace_back(5));
1056 }
1057 {
1058 auto string = object->add_string();
1059 string->SetString("Hello, World!");
1060 }
1061 {
1062 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -07001063 auto sub_string = vector_of_strings->emplace_back();
1064 ASSERT_TRUE(sub_string != nullptr);
Maxwell Hendersonfb1e3bc2024-02-04 13:55:22 -08001065 ASSERT_TRUE(sub_string->emplace_back('D'));
1066 }
1067 { object->set_substruct({971, 254}); }
1068 {
1069 auto subtable = object->add_subtable();
1070 subtable->set_foo(1234);
1071 }
1072 {
1073 auto vector = object->add_vector_of_structs();
1074 ASSERT_TRUE(vector->emplace_back({48, 67}));
1075 ASSERT_TRUE(vector->emplace_back({118, 148}));
1076 ASSERT_TRUE(vector->emplace_back({971, 973}));
1077 // Max vector size is three; this should fail.
1078 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1079 // We don't have any extra space available.
1080 ASSERT_FALSE(vector->reserve(4));
1081 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1082 }
1083 {
1084 auto vector = object->add_vector_of_tables();
1085 auto subobject = vector->emplace_back();
1086 subobject->set_foo(222);
1087 }
1088 {
1089 auto subtable = object->add_included_table();
1090 subtable->set_foo(included::TestEnum::B);
1091 }
1092 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1093 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1094 {.multi_line = true});
1095 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1096 TestMemory(builder.buffer());
1097}
1098
James Kuszmaul6be41022023-12-20 11:55:28 -08001099// Uses a small example to manually verify that we can copy from the flatbuffer
1100// object API.
1101TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
1102 aos::fbs::testing::TestTableT object_t;
1103 object_t.scalar = 971;
1104 object_t.vector_of_strings.push_back("971");
1105 object_t.vector_of_structs.push_back({1, 2});
1106 object_t.subtable = std::make_unique<SubTableT>();
Austin Schuh02e0d772024-05-30 16:41:06 -07001107 aos::fbs::AlignedVectorAllocator allocator;
James Kuszmaul6be41022023-12-20 11:55:28 -08001108 Builder<TestTableStatic> builder(&allocator);
1109 ASSERT_TRUE(builder->FromFlatbuffer(object_t));
1110 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1111 // Note that vectors and strings get set to zero-length, but present, values.
1112 EXPECT_EQ(
1113 "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
1114 "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
James Kuszmaul98c798d2024-04-24 15:58:09 -07001115 "\"baz\": 0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
1116 "\"x\": 1, \"y\": 2 } ], \"vector_of_tables\": [ ], "
James Kuszmaul6be41022023-12-20 11:55:28 -08001117 "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
1118 "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
1119 aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
1120}
1121
1122// More completely covers our object API copying by comparing the flatbuffer
1123// Pack() methods to our FromFlatbuffer() methods.
1124TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
1125 VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
1126 std::string populated_config =
1127 aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
1128 "aos/flatbuffers/test_dir/type_coverage.json"));
1129 Builder<aos::testing::ConfigurationStatic> json_builder =
1130 aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
1131 populated_config);
1132 aos::testing::ConfigurationT object_t;
1133 json_builder->AsFlatbuffer().UnPackTo(&object_t);
1134
1135 Builder<aos::testing::ConfigurationStatic> from_object_static;
1136 ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
1137 flatbuffers::FlatBufferBuilder fbb;
1138 fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
1139 aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
1140 fbb.Release();
1141 EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
1142 aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
1143}
1144
James Kuszmaul1e57af92023-12-20 15:34:54 -08001145// Tests that we can build code that uses the reflection types.
1146TEST_F(StaticFlatbuffersTest, IncludeReflectionTypes) {
1147 VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
1148}
1149
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001150// Tests that we can use the move constructor on a Builder.
1151TEST_F(StaticFlatbuffersTest, BuilderMoveConstructor) {
Austin Schuh02e0d772024-05-30 16:41:06 -07001152 alignas(64) uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001153 aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
1154 Builder<TestTableStatic> builder_from(&allocator);
1155 Builder<TestTableStatic> builder(std::move(builder_from));
1156 TestTableStatic *object = builder.get();
1157 object->set_scalar(123);
1158 {
1159 auto vector = object->add_vector_of_scalars();
1160 ASSERT_TRUE(vector->emplace_back(4));
1161 ASSERT_TRUE(vector->emplace_back(5));
1162 }
1163 {
1164 auto string = object->add_string();
1165 string->SetString("Hello, World!");
1166 }
1167 {
1168 auto vector_of_strings = object->add_vector_of_strings();
Austin Schuh6bdcc372024-06-27 14:49:11 -07001169 auto sub_string = vector_of_strings->emplace_back();
1170 ASSERT_TRUE(sub_string != nullptr);
James Kuszmauld4b4f1d2024-03-13 15:57:35 -07001171 ASSERT_TRUE(sub_string->emplace_back('D'));
1172 }
1173 { object->set_substruct({971, 254}); }
1174 {
1175 auto subtable = object->add_subtable();
1176 subtable->set_foo(1234);
1177 }
1178 {
1179 auto vector = object->add_vector_of_structs();
1180 ASSERT_TRUE(vector->emplace_back({48, 67}));
1181 ASSERT_TRUE(vector->emplace_back({118, 148}));
1182 ASSERT_TRUE(vector->emplace_back({971, 973}));
1183 // Max vector size is three; this should fail.
1184 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1185 // We don't have any extra space available.
1186 ASSERT_FALSE(vector->reserve(4));
1187 ASSERT_FALSE(vector->emplace_back({1114, 2056}));
1188 }
1189 {
1190 auto vector = object->add_vector_of_tables();
1191 auto subobject = vector->emplace_back();
1192 subobject->set_foo(222);
1193 }
1194 {
1195 auto subtable = object->add_included_table();
1196 subtable->set_foo(included::TestEnum::B);
1197 }
1198 ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
1199 VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
1200 {.multi_line = true});
1201 VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
1202 TestMemory(builder.buffer());
1203}
1204
James Kuszmaulf5eb4682023-09-22 17:16:59 -07001205} // namespace aos::fbs::testing