Skip to content

Commit d26693c

Browse files
FuzzTest Teamcopybara-github
FuzzTest Team
authored andcommitted
Add flatbuffers domain
PiperOrigin-RevId: 732932597
1 parent 09422aa commit d26693c

File tree

8 files changed

+1044
-2
lines changed

8 files changed

+1044
-2
lines changed

MODULE.bazel

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ bazel_dep(
4242
name = "platforms",
4343
version = "0.0.10",
4444
)
45+
bazel_dep(
46+
name = "flatbuffers",
47+
version = "25.2.10"
48+
)
4549
# GoogleTest is not a dev dependency, because it's needed when FuzzTest is used
4650
# with GoogleTest integration (e.g., googletest_adaptor). Note that the FuzzTest
4751
# framework can be used without GoogleTest integration as well.
@@ -55,8 +59,6 @@ bazel_dep(
5559
name = "protobuf",
5660
version = "30.2",
5761
)
58-
# TODO(lszekeres): Make this a dev dependency, as the protobuf library is only
59-
# required for testing.
6062
bazel_dep(
6163
name = "rules_proto",
6264
version = "7.1.0",

domain_tests/BUILD

+15
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ cc_test(
3333
],
3434
)
3535

36+
cc_test(
37+
name = "arbitrary_domains_flatbuffers_test",
38+
srcs = ["arbitrary_domains_flatbuffers_test.cc"],
39+
deps = [
40+
":domain_testing",
41+
"@abseil-cpp//absl/random",
42+
"@com_google_fuzztest//fuzztest:domain",
43+
"@com_google_fuzztest//fuzztest:flatbuffers",
44+
"@com_google_fuzztest//fuzztest:meta",
45+
"@com_google_fuzztest//fuzztest:test_flatbuffers_cc_fbs",
46+
"@flatbuffers//:runtime_cc",
47+
"@googletest//:gtest_main",
48+
],
49+
)
50+
3651
cc_test(
3752
name = "arbitrary_domains_protobuf_test",
3853
srcs = ["arbitrary_domains_protobuf_test.cc"],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <optional>
16+
#include <string_view>
17+
#include <utility>
18+
#include <vector>
19+
20+
#include "gmock/gmock.h"
21+
#include "gtest/gtest.h"
22+
#include "absl/random/random.h"
23+
#include "flatbuffers/base.h"
24+
#include "flatbuffers/buffer.h"
25+
#include "flatbuffers/flatbuffer_builder.h"
26+
#include "flatbuffers/string.h"
27+
#include "./fuzztest/domain.h"
28+
#include "./domain_tests/domain_testing.h"
29+
#include "./fuzztest/flatbuffers.h"
30+
#include "./fuzztest/internal/meta.h"
31+
#include "./fuzztest/test_flatbuffers_generated.h"
32+
33+
namespace fuzztest {
34+
namespace {
35+
36+
using ::fuzztest::internal::OptionalRequiredTestFbsTable;
37+
using ::fuzztest::internal::SimpleTestFbsTable;
38+
using ::testing::Contains;
39+
using ::testing::IsTrue;
40+
using ::testing::ResultOf;
41+
42+
TEST(FlatbuffersMetaTest, IsFlatbuffersTable) {
43+
static_assert(internal::is_flatbuffers_table_v<SimpleTestFbsTable>);
44+
static_assert(!internal::is_flatbuffers_table_v<int>);
45+
}
46+
47+
TEST(FlatbuffersTableImplTest, SimpleTestFbsTableValueRoundTrip) {
48+
auto domain = Arbitrary<SimpleTestFbsTable>();
49+
50+
flatbuffers::FlatBufferBuilder fbb;
51+
auto table_offset = internal::CreateSimpleTestFbsTableDirect(
52+
fbb, true, 1.0, "foo bar baz", internal::TestFbsEnum_Second);
53+
fbb.Finish(table_offset);
54+
auto table = flatbuffers::GetRoot<SimpleTestFbsTable>(fbb.GetBufferPointer());
55+
56+
auto corpus = domain.FromValue(table);
57+
ASSERT_TRUE(corpus.has_value());
58+
ASSERT_OK(domain.ValidateCorpusValue(*corpus));
59+
60+
auto ir = domain.SerializeCorpus(corpus.value());
61+
62+
auto new_corpus = domain.ParseCorpus(ir);
63+
ASSERT_TRUE(new_corpus.has_value());
64+
ASSERT_OK(domain.ValidateCorpusValue(*new_corpus));
65+
66+
auto new_table = domain.GetValue(*new_corpus);
67+
EXPECT_EQ(new_table->b(), true);
68+
EXPECT_EQ(new_table->f(), 1.0);
69+
EXPECT_EQ(new_table->str()->str(), "foo bar baz");
70+
EXPECT_TRUE(new_table->e() == internal::TestFbsEnum_Second);
71+
}
72+
73+
TEST(FlatbuffersTableImplTest, InitGeneratesSeeds) {
74+
auto domain = Arbitrary<SimpleTestFbsTable>();
75+
76+
flatbuffers::FlatBufferBuilder fbb;
77+
auto table_offset = internal::CreateSimpleTestFbsTableDirect(
78+
fbb, true, 1.0, "foo bar baz", internal::TestFbsEnum_Second);
79+
fbb.Finish(table_offset);
80+
auto table = flatbuffers::GetRoot<SimpleTestFbsTable>(fbb.GetBufferPointer());
81+
82+
domain.WithSeeds({table});
83+
84+
std::vector<Value<decltype(domain)>> values;
85+
absl::BitGen bitgen;
86+
values.reserve(1000);
87+
for (int i = 0; i < 1000; ++i) {
88+
Value value(domain, bitgen);
89+
values.push_back(std::move(value));
90+
}
91+
92+
EXPECT_THAT(
93+
values,
94+
Contains(ResultOf(
95+
[table](const auto& val) {
96+
bool has_same_str =
97+
val.user_value->str() == nullptr && table->str() == nullptr;
98+
if (val.user_value->str() != nullptr && table->str() != nullptr) {
99+
has_same_str =
100+
val.user_value->str()->str() == table->str()->str();
101+
}
102+
return (val.user_value->b() == table->b() &&
103+
val.user_value->f() == table->f() &&
104+
val.user_value->e() == table->e() && has_same_str);
105+
},
106+
IsTrue())));
107+
}
108+
109+
TEST(FlatbuffersTableImplTest, EventuallyMutatesAllTableFields) {
110+
auto domain = Arbitrary<SimpleTestFbsTable>();
111+
112+
absl::BitGen bitgen;
113+
Value val(domain, bitgen);
114+
115+
const auto verify_field_changes = [&](std::string_view name, auto has,
116+
auto get) {
117+
Set<decltype(get(val.user_value))> values;
118+
119+
int iterations = 10'000;
120+
while (--iterations > 0 && values.size() < 2) {
121+
values.insert(get(val.user_value));
122+
val.Mutate(domain, bitgen, {}, false);
123+
}
124+
EXPECT_GT(iterations, 0)
125+
<< "Field: " << name << " -- " << testing::PrintToString(values);
126+
};
127+
128+
verify_field_changes(
129+
"b", [](auto v) { return true; }, [](auto v) { return v->b(); });
130+
verify_field_changes(
131+
"f", [](auto v) { return true; }, [](auto v) { return v->f(); });
132+
verify_field_changes(
133+
"str", [](auto v) { return v->str() != nullptr; },
134+
[](auto v) { return v->str()->str(); });
135+
verify_field_changes(
136+
"e", [](auto v) { return true; }, [](auto v) { return v->e(); });
137+
}
138+
139+
TEST(FlatbuffersTableImplTest, OptionalFieldsEventuallyBecomeEmpty) {
140+
auto domain = Arbitrary<OptionalRequiredTestFbsTable>();
141+
142+
absl::BitGen bitgen;
143+
Value val(domain, bitgen);
144+
145+
const auto verify_field_becomes_null = [&](std::string_view name, auto has) {
146+
for (int i = 0; i < 10'000; ++i) {
147+
val.Mutate(domain, bitgen, {}, false);
148+
if (!has(val.user_value)) {
149+
break;
150+
}
151+
}
152+
EXPECT_FALSE(has(val.user_value)) << "Field never became unset: " << name;
153+
};
154+
155+
verify_field_becomes_null("opt_scalar",
156+
[](auto v) { return v->opt_scalar().has_value(); });
157+
verify_field_becomes_null("opt_str",
158+
[](auto v) { return v->opt_str() != nullptr; });
159+
}
160+
161+
TEST(FlatbuffersTableImplTest, DefaultAndRequiredFieldsAlwaysSet) {
162+
auto domain = Arbitrary<OptionalRequiredTestFbsTable>();
163+
164+
absl::BitGen bitgen;
165+
Value val(domain, bitgen);
166+
167+
const auto verify_field_always_set = [&](std::string_view name, auto has) {
168+
for (int i = 0; i < 10'000; ++i) {
169+
val.Mutate(domain, bitgen, {}, false);
170+
if (!has(val.user_value)) {
171+
break;
172+
}
173+
}
174+
EXPECT_TRUE(has(val.user_value)) << "Field is not set: " << name;
175+
};
176+
177+
verify_field_always_set("def_scalar", [](auto v) { return true; });
178+
verify_field_always_set("req_str",
179+
[](auto v) { return v->req_str() != nullptr; });
180+
}
181+
182+
} // namespace
183+
} // namespace fuzztest

fuzztest/BUILD

+52
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# FuzzTest: a coverage-guided fuzzing / property-based testing framework.
1616

1717
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
18+
load("@flatbuffers//:build_defs.bzl", "flatbuffer_library_public")
1819
load("@rules_proto//proto:defs.bzl", "proto_library")
1920

2021
package(default_visibility = ["//visibility:public"])
@@ -345,6 +346,7 @@ cc_library(
345346
":serialization",
346347
":status",
347348
":type_support",
349+
"@abseil-cpp//absl/algorithm:container",
348350
"@abseil-cpp//absl/base:core_headers",
349351
"@abseil-cpp//absl/base:no_destructor",
350352
"@abseil-cpp//absl/container:flat_hash_map",
@@ -422,6 +424,34 @@ cc_library(
422424
],
423425
)
424426

427+
cc_library(
428+
name = "flatbuffers",
429+
srcs = ["internal/domains/flatbuffers_domain_impl.h"],
430+
hdrs = ["flatbuffers.h"],
431+
deps = [
432+
":any",
433+
":domain_core",
434+
":logging",
435+
":meta",
436+
":serialization",
437+
":status",
438+
":type_support",
439+
"@abseil-cpp//absl/algorithm:container",
440+
"@abseil-cpp//absl/base:core_headers",
441+
"@abseil-cpp//absl/container:flat_hash_map",
442+
"@abseil-cpp//absl/container:flat_hash_set",
443+
"@abseil-cpp//absl/random",
444+
"@abseil-cpp//absl/random:bit_gen_ref",
445+
"@abseil-cpp//absl/random:distributions",
446+
"@abseil-cpp//absl/status",
447+
"@abseil-cpp//absl/status:statusor",
448+
"@abseil-cpp//absl/strings",
449+
"@abseil-cpp//absl/strings:str_format",
450+
"@abseil-cpp//absl/synchronization",
451+
"@flatbuffers//:runtime_cc",
452+
],
453+
)
454+
425455
cc_library(
426456
name = "fixture_driver",
427457
srcs = ["internal/fixture_driver.cc"],
@@ -799,6 +829,28 @@ cc_proto_library(
799829
deps = [":test_protobuf"],
800830
)
801831

832+
flatbuffer_library_public(
833+
name = "test_flatbuffers_fbs",
834+
srcs = ["internal/test_flatbuffers.fbs"],
835+
outs = [
836+
"test_flatbuffers_bfbs_generated.h",
837+
"test_flatbuffers_generated.h",
838+
],
839+
flatc_args = [
840+
"--bfbs-gen-embed",
841+
"--gen-name-strings",
842+
],
843+
language_flag = "-c",
844+
)
845+
846+
cc_library(
847+
name = "test_flatbuffers_cc_fbs",
848+
srcs = [":test_flatbuffers_fbs"],
849+
hdrs = [":test_flatbuffers_fbs"],
850+
features = ["-parse_headers"],
851+
deps = ["@flatbuffers//:runtime_cc"],
852+
)
853+
802854
cc_library(
803855
name = "type_support",
804856
srcs = ["internal/type_support.cc"],

fuzztest/flatbuffers.h

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FUZZTEST_FUZZTEST_FLATBUFFERS_H_
16+
#define FUZZTEST_FUZZTEST_FLATBUFFERS_H_
17+
18+
#include "./fuzztest/internal/domains/flatbuffers_domain_impl.h" // IWYU pragma: export
19+
#endif // FUZZTEST_FUZZTEST_FLATBUFFERS_H_

0 commit comments

Comments
 (0)