Skip to content

Commit 753f6f7

Browse files
FuzzTest Teamcopybara-github
FuzzTest Team
authored andcommitted
Add flatbuffers domain
PiperOrigin-RevId: 732932597
1 parent d9103df commit 753f6f7

14 files changed

+1197
-2
lines changed

.github/workflows/cmake_test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ jobs:
7777
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
7878
-D CMAKE_BUILD_TYPE=RelWithDebug \
7979
-D FUZZTEST_BUILD_TESTING=on \
80+
-D FUZZTEST_BUILD_FLATBUFFERS=on \
8081
&& cmake --build build -j $(nproc) \
8182
&& ctest --test-dir build -j $(nproc) --output-on-failure
8283
- name: Run all tests in default mode with gcc
@@ -90,6 +91,7 @@ jobs:
9091
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
9192
-D CMAKE_BUILD_TYPE=RelWithDebug \
9293
-D FUZZTEST_BUILD_TESTING=on \
94+
-D FUZZTEST_BUILD_FLATBUFFERS=on \
9395
&& cmake --build build_gcc -j $(nproc) \
9496
&& ctest --test-dir build_gcc -j $(nproc) --output-on-failure
9597
- name: Run end-to-end tests in fuzzing mode
@@ -104,6 +106,7 @@ jobs:
104106
-D CMAKE_BUILD_TYPE=RelWithDebug \
105107
-D FUZZTEST_FUZZING_MODE=on \
106108
-D FUZZTEST_BUILD_TESTING=on \
109+
-D FUZZTEST_BUILD_FLATBUFFERS=on \
107110
&& cmake --build build -j $(nproc) \
108111
&& ctest --test-dir build -j $(nproc) --output-on-failure -R "functional_test"
109112
- name: Save new cache based on main

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.19)
22
project(fuzztest)
33

44
option(FUZZTEST_BUILD_TESTING "Building the tests." OFF)
5+
option(FUZZTEST_BUILD_FLATBUFFERS "Building the flatbuffers support." OFF)
56
option(FUZZTEST_FUZZING_MODE "Building the fuzztest in fuzzing mode." OFF)
67
set(FUZZTEST_COMPATIBILITY_MODE "" CACHE STRING "Compatibility mode. Available options: <empty>, libfuzzer")
78
set(CMAKE_CXX_STANDARD 17)

MODULE.bazel

Lines changed: 4 additions & 2 deletions
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",

cmake/BuildDependencies.cmake

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ set(proto_TAG v30.2)
2121
set(nlohmann_json_URL https://github.com/nlohmann/json.git)
2222
set(nlohmann_json_TAG v3.11.3)
2323

24+
set(flatbuffers_URL https://github.com/google/flatbuffers.git)
25+
set(flatbuffers_TAG v25.2.10)
26+
2427
if(POLICY CMP0135)
2528
cmake_policy(SET CMP0135 NEW)
2629
set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)
@@ -50,6 +53,14 @@ FetchContent_Declare(
5053
URL_HASH MD5=${antlr_cpp_MD5}
5154
)
5255

56+
if (FUZZTEST_BUILD_FLATBUFFERS)
57+
FetchContent_Declare(
58+
flatbuffers
59+
GIT_REPOSITORY ${flatbuffers_URL}
60+
GIT_TAG ${flatbuffers_TAG}
61+
)
62+
endif()
63+
5364
if (FUZZTEST_BUILD_TESTING)
5465

5566
FetchContent_Declare(
@@ -87,3 +98,9 @@ if (FUZZTEST_BUILD_TESTING)
8798
FetchContent_MakeAvailable(nlohmann_json)
8899

89100
endif ()
101+
102+
if (FUZZTEST_BUILD_FLATBUFFERS)
103+
set(FLATBUFFERS_BUILD_TESTS OFF)
104+
set(FLATBUFFERS_BUILD_INSTALL OFF)
105+
FetchContent_MakeAvailable(flatbuffers)
106+
endif()

cmake/generate_cmake_from_bazel.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@abseil-cpp//absl/types:optional": "absl::optional",
5353
"@abseil-cpp//absl/types:span": "absl::span",
5454
"@abseil-cpp//absl/types:variant": "absl::variant",
55+
"@flatbuffers//:runtime_cc": "flatbuffers",
5556
"@googletest//:gtest": "GTest::gtest",
5657
"@googletest//:gtest_main": "GTest::gmock_main",
5758
"@protobuf//:protobuf": "protobuf::libprotobuf",

domain_tests/BUILD

Lines changed: 15 additions & 0 deletions
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"],

domain_tests/CMakeLists.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,28 @@ fuzztest_cc_test(
1919
GTest::gmock_main
2020
)
2121

22+
if (FUZZTEST_BUILD_FLATBUFFERS)
23+
fuzztest_cc_test(
24+
NAME
25+
arbitrary_domains_flatbuffers_test
26+
SRCS
27+
"arbitrary_domains_flatbuffers_test.cc"
28+
DEPS
29+
absl::flat_hash_set
30+
absl::random_random
31+
absl::random_bit_gen_ref
32+
absl::strings
33+
flatbuffers
34+
fuzztest::domain
35+
fuzztest::domain_testing
36+
test_flatbuffers
37+
GTest::gmock_main
38+
)
39+
add_dependencies(fuzztest_arbitrary_domains_flatbuffers_test
40+
GENERATE_test_flatbuffers
41+
)
42+
endif()
43+
2244
fuzztest_cc_test(
2345
NAME
2446
arbitrary_domains_protobuf_test
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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/internal/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+
static_assert(!internal::is_flatbuffers_table_v<std::optional<bool>>);
46+
}
47+
48+
TEST(FlatbuffersTableImplTest, SimpleTestFbsTableValueRoundTrip) {
49+
auto domain = Arbitrary<SimpleTestFbsTable>();
50+
51+
flatbuffers::FlatBufferBuilder fbb;
52+
auto table_offset = internal::CreateSimpleTestFbsTableDirect(
53+
fbb, true, 1.0, "foo bar baz", internal::TestFbsEnum_Second);
54+
fbb.Finish(table_offset);
55+
auto table = flatbuffers::GetRoot<SimpleTestFbsTable>(fbb.GetBufferPointer());
56+
57+
auto corpus = domain.FromValue(table);
58+
ASSERT_TRUE(corpus.has_value());
59+
ASSERT_OK(domain.ValidateCorpusValue(*corpus));
60+
61+
auto ir = domain.SerializeCorpus(corpus.value());
62+
63+
auto new_corpus = domain.ParseCorpus(ir);
64+
ASSERT_TRUE(new_corpus.has_value());
65+
ASSERT_OK(domain.ValidateCorpusValue(*new_corpus));
66+
67+
auto new_table = domain.GetValue(*new_corpus);
68+
EXPECT_EQ(new_table->b(), true);
69+
EXPECT_EQ(new_table->f(), 1.0);
70+
EXPECT_EQ(new_table->str()->str(), "foo bar baz");
71+
EXPECT_TRUE(new_table->e() == internal::TestFbsEnum_Second);
72+
}
73+
74+
TEST(FlatbuffersTableImplTest, InitGeneratesSeeds) {
75+
auto domain = Arbitrary<SimpleTestFbsTable>();
76+
77+
flatbuffers::FlatBufferBuilder fbb;
78+
auto table_offset = internal::CreateSimpleTestFbsTableDirect(
79+
fbb, true, 1.0, "foo bar baz", internal::TestFbsEnum_Second);
80+
fbb.Finish(table_offset);
81+
auto table = flatbuffers::GetRoot<SimpleTestFbsTable>(fbb.GetBufferPointer());
82+
83+
domain.WithSeeds({table});
84+
85+
std::vector<Value<decltype(domain)>> values;
86+
absl::BitGen bitgen;
87+
values.reserve(1000);
88+
for (int i = 0; i < 1000; ++i) {
89+
Value value(domain, bitgen);
90+
values.push_back(std::move(value));
91+
}
92+
93+
EXPECT_THAT(
94+
values,
95+
Contains(ResultOf(
96+
[table](const auto& val) {
97+
bool has_same_str =
98+
val.user_value->str() == nullptr && table->str() == nullptr;
99+
if (val.user_value->str() != nullptr && table->str() != nullptr) {
100+
has_same_str =
101+
val.user_value->str()->str() == table->str()->str();
102+
}
103+
return (val.user_value->b() == table->b() &&
104+
val.user_value->f() == table->f() &&
105+
val.user_value->e() == table->e() && has_same_str);
106+
},
107+
IsTrue())));
108+
}
109+
110+
TEST(FlatbuffersTableImplTest, EventuallyMutatesAllTableFields) {
111+
auto domain = Arbitrary<SimpleTestFbsTable>();
112+
113+
absl::BitGen bitgen;
114+
Value val(domain, bitgen);
115+
116+
const auto verify_field_changes = [&](std::string_view name, 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("b", [](auto v) { return v->b(); });
129+
verify_field_changes("f", [](auto v) { return v->f(); });
130+
verify_field_changes("str", [](auto v) { return v->str()->str(); });
131+
verify_field_changes("e", [](auto v) { return v->e(); });
132+
}
133+
134+
TEST(FlatbuffersTableImplTest, OptionalFieldsEventuallyBecomeEmpty) {
135+
auto domain = Arbitrary<OptionalRequiredTestFbsTable>();
136+
137+
absl::BitGen bitgen;
138+
Value val(domain, bitgen);
139+
140+
const auto verify_field_becomes_null = [&](std::string_view name, auto has) {
141+
for (int i = 0; i < 10'000; ++i) {
142+
val.Mutate(domain, bitgen, {}, false);
143+
if (!has(val.user_value)) {
144+
break;
145+
}
146+
}
147+
EXPECT_FALSE(has(val.user_value)) << "Field never became unset: " << name;
148+
};
149+
150+
verify_field_becomes_null("opt_scalar",
151+
[](auto v) { return v->opt_scalar().has_value(); });
152+
verify_field_becomes_null("opt_str",
153+
[](auto v) { return v->opt_str() != nullptr; });
154+
}
155+
156+
TEST(FlatbuffersTableImplTest, DefaultAndRequiredFieldsAlwaysSet) {
157+
auto domain = Arbitrary<OptionalRequiredTestFbsTable>();
158+
159+
absl::BitGen bitgen;
160+
Value val(domain, bitgen);
161+
162+
const auto verify_field_always_set = [&](std::string_view name, auto has) {
163+
for (int i = 0; i < 10'000; ++i) {
164+
val.Mutate(domain, bitgen, {}, false);
165+
if (!has(val.user_value)) {
166+
break;
167+
}
168+
}
169+
EXPECT_TRUE(has(val.user_value)) << "Field is not set: " << name;
170+
};
171+
172+
verify_field_always_set("def_scalar", [](auto v) { return true; });
173+
verify_field_always_set("req_str",
174+
[](auto v) { return v->req_str() != nullptr; });
175+
}
176+
177+
} // namespace
178+
} // namespace fuzztest

fuzztest/BUILD

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ cc_library(
345345
":serialization",
346346
":status",
347347
":type_support",
348+
"@abseil-cpp//absl/algorithm:container",
348349
"@abseil-cpp//absl/base:core_headers",
349350
"@abseil-cpp//absl/base:no_destructor",
350351
"@abseil-cpp//absl/container:flat_hash_map",
@@ -422,6 +423,35 @@ cc_library(
422423
],
423424
)
424425

426+
cc_library(
427+
name = "flatbuffers",
428+
srcs = ["internal/domains/flatbuffers_domain_impl.h"],
429+
hdrs = ["flatbuffers.h"],
430+
deps = [
431+
":any",
432+
":domain_core",
433+
":logging",
434+
":meta",
435+
":serialization",
436+
":status",
437+
":type_support",
438+
"@abseil-cpp//absl/algorithm:container",
439+
"@abseil-cpp//absl/base:core_headers",
440+
"@abseil-cpp//absl/base:nullability",
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"],
@@ -801,6 +831,28 @@ cc_proto_library(
801831
deps = [":test_protobuf"],
802832
)
803833

834+
# Derived from //third_party/flatbuffers:flatbuffer_cc_library but allows output prefix for single
835+
# source target and to have embedded schema file in the outputs.
836+
genrule(
837+
name = "test_flatbuffers_fbs",
838+
srcs = ["internal/test_flatbuffers.fbs"],
839+
outs = [
840+
"internal/test_flatbuffers_bfbs_generated.h",
841+
"internal/test_flatbuffers_generated.h",
842+
],
843+
cmd = "$(location @flatbuffers//:flatc) -c -o $(@D)/internal --bfbs-gen-embed --gen-name-strings $(SRCS)",
844+
message = "Generating flatbuffer files for test_flatbuffers_fbs",
845+
tools = ["@flatbuffers//:flatc"],
846+
)
847+
848+
cc_library(
849+
name = "test_flatbuffers_cc_fbs",
850+
srcs = [":test_flatbuffers_fbs"],
851+
hdrs = [":test_flatbuffers_fbs"],
852+
features = ["-parse_headers"],
853+
deps = ["@flatbuffers//:runtime_cc"],
854+
)
855+
804856
cc_library(
805857
name = "type_support",
806858
srcs = ["internal/type_support.cc"],

0 commit comments

Comments
 (0)