Skip to content

Commit 4b43388

Browse files
fniksiccopybara-github
authored andcommitted
Add a "fuzz_test" property to FuzzTest tests.
PiperOrigin-RevId: 894156773
1 parent 96e0bf4 commit 4b43388

2 files changed

Lines changed: 85 additions & 6 deletions

File tree

e2e_tests/functional_test.cc

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ using ::fuzztest::domain_implementor::PrintMode;
6262
using ::testing::_;
6363
using ::testing::AllOf;
6464
using ::testing::AnyOf;
65+
using ::testing::Contains;
6566
using ::testing::ContainsRegex;
6667
using ::testing::Eq;
6768
using ::testing::FieldsAre;
@@ -116,16 +117,11 @@ int CountTargetRuns(absl::string_view std_err) {
116117

117118
class UnitTestModeTest : public ::testing::Test {
118119
protected:
119-
RunResults Run(
120+
RunResults RunWithExactFuzzerFlags(
120121
absl::string_view test_filter,
121122
absl::string_view target_binary = kDefaultTargetBinary,
122123
const absl::flat_hash_map<std::string, std::string>& env = {},
123124
absl::flat_hash_map<std::string, std::string> fuzzer_flags = {}) {
124-
fuzzer_flags["print_subprocess_log"] = "true";
125-
fuzzer_flags["unguided"] = "true";
126-
if (!fuzzer_flags.contains("fuzz_for")) {
127-
fuzzer_flags["fuzz_for"] = "10s";
128-
}
129125
RunOptions run_options;
130126
run_options.flags = {
131127
{GTEST_FLAG_PREFIX_ "filter", std::string(test_filter)},
@@ -134,6 +130,20 @@ class UnitTestModeTest : public ::testing::Test {
134130
run_options.env = WithTestSanitizerOptions(env);
135131
return RunBinary(BinaryPath(target_binary), run_options);
136132
}
133+
134+
RunResults Run(
135+
absl::string_view test_filter,
136+
absl::string_view target_binary = kDefaultTargetBinary,
137+
const absl::flat_hash_map<std::string, std::string>& env = {},
138+
absl::flat_hash_map<std::string, std::string> fuzzer_flags = {}) {
139+
fuzzer_flags["print_subprocess_log"] = "true";
140+
fuzzer_flags["unguided"] = "true";
141+
if (!fuzzer_flags.contains("fuzz_for")) {
142+
fuzzer_flags["fuzz_for"] = "10s";
143+
}
144+
return RunWithExactFuzzerFlags(test_filter, target_binary, env,
145+
std::move(fuzzer_flags));
146+
}
137147
};
138148

139149
TEST_F(UnitTestModeTest, PassingTestPassesInUnitTestingMode) {
@@ -668,6 +678,52 @@ TEST_F(UnitTestModeTest, InputsAreSkippedWhenRequestedInTests) {
668678
EXPECT_THAT_LOG(std_err, HasSubstr("Skipped input"));
669679
}
670680

681+
// Identifies fuzz tests as those with test suite name "FuzzTest".
682+
MATCHER(IsXmlWithExactlyFuzzTestsHavingFuzzTestProperty, "") {
683+
absl::string_view xml = arg;
684+
absl::string_view attrs;
685+
while (RE2::FindAndConsume(&xml, R"re((?s)<testcase\s(.*?)>)re", &attrs)) {
686+
const bool is_fuzz_test =
687+
RE2::PartialMatch(attrs, R"re(classname="FuzzTest")re");
688+
if (!attrs.empty() && attrs.back() == '/') {
689+
// Self-closing tag; no properties.
690+
if (is_fuzz_test) {
691+
*result_listener << "found a fuzz test without fuzz_test property";
692+
return false;
693+
}
694+
continue;
695+
}
696+
bool has_fuzz_test_property = false;
697+
absl::string_view body;
698+
if (RE2::Consume(&xml, R"re((?s)(.*?)</testcase>)re", &body)) {
699+
has_fuzz_test_property =
700+
RE2::PartialMatch(body, R"re(<property name="fuzz_test")re");
701+
}
702+
if (is_fuzz_test != has_fuzz_test_property) {
703+
*result_listener << "found a "
704+
<< (is_fuzz_test ? "fuzz test " : "unit test ")
705+
<< (has_fuzz_test_property ? "with " : "without ")
706+
<< "fuzz_test property";
707+
return false;
708+
}
709+
}
710+
return true;
711+
}
712+
713+
TEST_F(UnitTestModeTest, FuzzTestsRecordFuzzTestProperty) {
714+
TempDir out_dir;
715+
const std::string xml_output_file = out_dir.path() / "output.xml";
716+
auto [status, std_out, std_err] =
717+
RunWithExactFuzzerFlags("*", "testdata/unit_test_and_fuzz_tests",
718+
{{"XML_OUTPUT_FILE", xml_output_file}});
719+
720+
// Ensure that we've executed at least one unit test and one fuzz test.
721+
EXPECT_THAT_LOG(std_out, AllOf(HasSubstr("UnitTest.AlwaysPasses"),
722+
HasSubstr("FuzzTest.AlwaysPasses")));
723+
EXPECT_THAT(ReadFile(xml_output_file),
724+
Optional(IsXmlWithExactlyFuzzTestsHavingFuzzTestProperty()));
725+
}
726+
671727
// Tests for the FuzzTest command line interface.
672728
class GenericCommandLineInterfaceTest : public ::testing::Test {
673729
protected:
@@ -1247,6 +1303,28 @@ TEST_F(FuzzingModeCommandLineInterfaceTest,
12471303
EXPECT_THAT(status, Eq(ExitCode(0)));
12481304
}
12491305

1306+
TEST_F(FuzzingModeCommandLineInterfaceTest,
1307+
FuzzTestsRecordFuzzTestPropertyWhenRunningWithCentipede) {
1308+
#ifndef FUZZTEST_USE_CENTIPEDE
1309+
GTEST_SKIP() << "Skipping Centipede-specific test";
1310+
#endif
1311+
TempDir temp_dir;
1312+
const std::string xml_output_file = temp_dir.path() / "output.xml";
1313+
auto [status, std_out, std_err] = RunWith(
1314+
{
1315+
{"fuzz_for", "1s"},
1316+
{"corpus_database", temp_dir.path() / "corpus_database"},
1317+
{"internal_centipede_command", ShellEscape(CentipedePath())},
1318+
},
1319+
{{"XML_OUTPUT_FILE", xml_output_file}},
1320+
/*timeout=*/absl::Minutes(1), "testdata/unit_test_and_fuzz_tests");
1321+
1322+
// Ensure that we've executed at least one fuzz test.
1323+
EXPECT_THAT_LOG(std_out, HasSubstr("FuzzTest.AlwaysPasses"));
1324+
EXPECT_THAT(ReadFile(xml_output_file),
1325+
Optional(IsXmlWithExactlyFuzzTestsHavingFuzzTestProperty()));
1326+
}
1327+
12501328
TEST_F(FuzzingModeCommandLineInterfaceTest,
12511329
FailsTestForSetupFailureWithoutCorpusDatabase) {
12521330
#ifndef FUZZTEST_USE_CENTIPEDE

fuzztest/internal/googletest_adaptor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class GTest_TestAdaptor : public ::testing::Test {
3838
configuration_(std::move(configuration)) {}
3939

4040
void TestBody() override {
41+
RecordProperty("fuzz_test", "true");
4142
auto test = test_.make();
4243
configuration_.fuzz_tests_in_current_shard = GetFuzzTestsInCurrentShard();
4344
// We replay a reproducer in the same process to help debugging when

0 commit comments

Comments
 (0)