@@ -62,6 +62,7 @@ using ::fuzztest::domain_implementor::PrintMode;
6262using ::testing::_;
6363using ::testing::AllOf;
6464using ::testing::AnyOf;
65+ using ::testing::Contains;
6566using ::testing::ContainsRegex;
6667using ::testing::Eq;
6768using ::testing::FieldsAre;
@@ -116,16 +117,11 @@ int CountTargetRuns(absl::string_view std_err) {
116117
117118class 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
139149TEST_F (UnitTestModeTest, PassingTestPassesInUnitTestingMode) {
@@ -668,6 +678,49 @@ 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+ EXPECT_THAT (ReadFile (xml_output_file),
721+ Optional (IsXmlWithExactlyFuzzTestsHavingFuzzTestProperty ()));
722+ }
723+
671724// Tests for the FuzzTest command line interface.
672725class GenericCommandLineInterfaceTest : public ::testing::Test {
673726 protected:
0 commit comments