| // Copyright 2022 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <csignal> |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <filesystem> |
| #include <optional> |
| #include <string> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/substitute.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/time.h" |
| #include "./fuzztest/internal/io.h" |
| #include "./fuzztest/internal/logging.h" |
| #include "./fuzztest/internal/serialization.h" |
| #include "./fuzztest/internal/subprocess.h" |
| #include "./fuzztest/internal/type_support.h" |
| #include "re2/re2.h" |
| |
| namespace fuzztest::internal { |
| namespace { |
| |
| #define FUZZTEST_FLAG_PREFIX_ "" |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::AnyOf; |
| using ::testing::ContainsRegex; |
| using ::testing::Eq; |
| using ::testing::FieldsAre; |
| using ::testing::HasSubstr; |
| using ::testing::IsEmpty; |
| using ::testing::IsSubsetOf; |
| using ::testing::Le; |
| using ::testing::Ne; |
| using ::testing::Not; |
| using ::testing::Optional; |
| using ::testing::SizeIs; |
| using ::testing::StartsWith; |
| |
| constexpr absl::string_view kDefaultTargetBinary = |
| "testdata/fuzz_tests_for_functional_testing"; |
| |
| std::string CreateFuzzTestFlag(absl::string_view flag_name, |
| absl::string_view flag_value) { |
| return absl::StrCat("--", FUZZTEST_FLAG_PREFIX_, flag_name, "=", flag_value); |
| } |
| |
| std::string BinaryPath(const absl::string_view name) { |
| const auto test_srcdir = absl::NullSafeStringView(getenv("TEST_SRCDIR")); |
| FUZZTEST_INTERNAL_CHECK_PRECONDITION( |
| !test_srcdir.empty(), |
| "Please set TEST_SRCDIR to non-empty value or use bazel to run the " |
| "test."); |
| const std::string binary_path = absl::StrCat( |
| test_srcdir, "/com_google_fuzztest/e2e_tests/", name, |
| absl::EndsWith(name, ".stripped") ? "" : ".stripped"); |
| |
| FUZZTEST_INTERNAL_CHECK(std::filesystem::exists(binary_path), |
| absl::StrCat("Can't find ", binary_path)); |
| return binary_path; |
| } |
| |
| absl::flat_hash_map<std::string, std::string> WithTestSanitizerOptions( |
| absl::flat_hash_map<std::string, std::string> env) { |
| if (!env.contains("ASAN_OPTIONS")) |
| env["ASAN_OPTIONS"] = "handle_abort=0:handle_sigfpe=0"; |
| if (!env.contains("MSAN_OPTIONS")) |
| env["MSAN_OPTIONS"] = "handle_abort=0:handle_sigfpe=0"; |
| return env; |
| } |
| |
| class TempDir { |
| public: |
| TempDir() { |
| dirname_ = "/tmp/replay_test_XXXXXX"; |
| dirname_ = mkdtemp(dirname_.data()); |
| EXPECT_TRUE(std::filesystem::is_directory(dirname_)); |
| } |
| |
| const std::string& dirname() const { return dirname_; } |
| |
| ~TempDir() { std::filesystem::remove_all(dirname_); } |
| |
| private: |
| std::string dirname_; |
| }; |
| |
| class UnitTestModeTest : public ::testing::Test { |
| protected: |
| RunResults Run( |
| absl::string_view test_filter, |
| absl::string_view target_binary = kDefaultTargetBinary, |
| const absl::flat_hash_map<std::string, std::string>& env = {}, |
| const absl::flat_hash_map<std::string, std::string>& fuzzer_flags = {}) { |
| std::vector<std::string> commandline = { |
| BinaryPath(target_binary), |
| absl::StrCat("--", GTEST_FLAG_PREFIX_, "filter=", test_filter)}; |
| |
| for (const auto& flag : fuzzer_flags) { |
| commandline.push_back(CreateFuzzTestFlag(flag.first, flag.second)); |
| } |
| return RunCommand(commandline, WithTestSanitizerOptions(env), |
| absl::Minutes(10)); |
| } |
| }; |
| |
| TEST_F(UnitTestModeTest, PassingTestPassesInUnitTestingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.PassesWithPositiveInput"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, InvalidSeedsAreSkippedAndReported) { |
| auto [status, std_out, std_err] = |
| Run(/*test_filter=*/"*", |
| /*target_binary=*/"testdata/fuzz_tests_with_invalid_seeds"); |
| EXPECT_THAT(std_err, HasSubstr("[!] Skipping WithSeeds() value in")); |
| EXPECT_THAT(std_err, |
| HasSubstr("Could not turn value into corpus type:\n{17}")); |
| EXPECT_THAT(std_err, HasSubstr("The value 17 is not InRange(0, 10):\n{17}")); |
| // Valid seeds are not reported. |
| EXPECT_THAT(std_err, Not(HasSubstr("{6}"))); |
| // Tests should still run. |
| EXPECT_THAT(std_out, HasSubstr("[ PASSED ] 3 tests.")); |
| EXPECT_THAT(status, ExitCode(0)); |
| } |
| |
| TEST_F(UnitTestModeTest, CorpusIsMutatedInUnitTestMode) { |
| auto [status, std_out, std_err] = Run("MySuite.PassesString"); |
| EXPECT_THAT(std_err, HasSubstr("==<<Saw size=0>>==")); |
| EXPECT_THAT(std_err, HasSubstr("==<<Saw size=1>>==")); |
| EXPECT_THAT(std_err, HasSubstr("==<<Saw size=2>>==")); |
| EXPECT_THAT(std_err, HasSubstr("==<<Saw size=3>>==")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, UnitTestModeLimitsNumberOfIterationsByWallTime) { |
| // We run this in unittest mode to check that we reduce the number of |
| // iterations to accommodate for the longer run time. |
| auto [status, std_out, std_err] = Run("MySuite.OneIterationTakesTooMuchTime"); |
| EXPECT_THAT(std_out, |
| HasSubstr("[ OK ] MySuite.OneIterationTakesTooMuchTime")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| int CountSubstrs(absl::string_view haystack, absl::string_view needle) { |
| int count = 0; |
| while (true) { |
| size_t pos = haystack.find(needle); |
| if (pos == haystack.npos) return count; |
| ++count; |
| haystack.remove_prefix(pos + needle.size()); |
| } |
| } |
| |
| RE2 MakeReproducerRegex(absl::string_view suite_name, |
| absl::string_view test_name, absl::string_view args) { |
| return RE2( |
| absl::Substitute(R"re(TEST\($0, $1.*\) {\n.*$1\(\n.*$2.*\n.*\).*\n.*})re", |
| suite_name, test_name, args)); |
| } |
| |
| std::string RemoveReproducer(std::string str, absl::string_view suite_name, |
| absl::string_view test_name, |
| absl::string_view args) { |
| EXPECT_TRUE( |
| RE2::Replace(&str, MakeReproducerRegex(suite_name, test_name, args), "")); |
| return str; |
| } |
| |
| // Matches strings that contain a reproducer test. |
| // |
| // For example, `HasReproducerTest("MySuite", "MyTest", "1, \"foo\"")` would |
| // match the following string: |
| // |
| // TEST(MySuite, MyTestRegression) { |
| // MyTest( |
| // 1, "foo" |
| // ); |
| // } |
| // |
| // The parameters `suite_name`, `test_name`, and `args` are regular expressions; |
| // make sure they are properly escaped! |
| MATCHER_P3(HasReproducerTest, suite_name, test_name, args, "") { |
| // `ContainsRegex` doesn't support the following regex externally. |
| return RE2::PartialMatch(arg, |
| MakeReproducerRegex(suite_name, test_name, args)); |
| } |
| |
| void GoogleTestExpectationsDontAbortInUnitTestModeImpl( |
| const RunResults& run_results) { |
| const auto& [status, std_out, std_err] = run_results; |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")) << std_err; |
| // We expect both to run without crashing. |
| EXPECT_THAT(std_out, HasSubstr("[ FAILED ] MySuite.GoogleTestExpect")) |
| << std_out; |
| EXPECT_THAT(std_out, HasSubstr("[ FAILED ] MySuite.GoogleTestAssert")) |
| << std_out; |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| |
| // There is no repro example on stdout, and there is one on stderr. |
| EXPECT_THAT( |
| std_out, |
| AllOf(Not(HasReproducerTest("MySuite", "GoogleTestExpect", ".*")), |
| Not(HasReproducerTest("MySuite", "GoogleTestAssert", ".*")))); |
| EXPECT_THAT(std_err, |
| AllOf(HasReproducerTest("MySuite", "GoogleTestExpect", ".*"), |
| HasReproducerTest("MySuite", "GoogleTestAssert", ".*"))); |
| } |
| |
| TEST_F(UnitTestModeTest, GoogleTestExpectationsDontAbortInUnitTestMode) { |
| GoogleTestExpectationsDontAbortInUnitTestModeImpl( |
| Run("MySuite.GoogleTestExpect:MySuite.GoogleTestAssert")); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| GoogleTestExpectationsDontAbortInUnitTestModeWhenAsanHandlesAbort) { |
| GoogleTestExpectationsDontAbortInUnitTestModeImpl(Run( |
| "MySuite.GoogleTestExpect:MySuite.GoogleTestAssert", kDefaultTargetBinary, |
| /*env=*/{{"ASAN_OPTIONS", "handle_abort=2"}})); |
| } |
| |
| TEST_F(UnitTestModeTest, GlobalEnvironmentGoesThroughCompleteLifecycle) { |
| auto [status, std_out, std_err] = Run("MySuite.GoogleTestExpect"); |
| EXPECT_EQ( |
| 1, CountSubstrs(std_err, "<<GlobalEnvironment::GlobalEnvironment()>>")); |
| EXPECT_EQ(1, CountSubstrs(std_err, "<<GlobalEnvironment::SetUp()>>")); |
| EXPECT_EQ(1, CountSubstrs(std_err, "<<GlobalEnvironment::TearDown()>>")); |
| EXPECT_EQ( |
| 1, CountSubstrs(std_err, "<<GlobalEnvironment::~GlobalEnvironment()>>")); |
| } |
| |
| TEST_F(UnitTestModeTest, FixtureGoesThroughCompleteLifecycle) { |
| auto [status, std_out, std_err] = Run("FixtureTest.NeverFails"); |
| EXPECT_EQ(1, CountSubstrs(std_err, "<<FixtureTest::FixtureTest()>>")); |
| EXPECT_EQ(1, CountSubstrs(std_err, "<<FixtureTest::~FixtureTest()>>")); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| GoogleTestPerIterationFixtureInstantiatedOncePerIteration) { |
| auto [status, std_out, std_err] = |
| Run("CallCountPerIteration.CallCountIsAlwaysIncrementedFromInitialValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| GoogleTestPerFuzzTestFixtureInstantiatedOncePerFuzzTest) { |
| auto [status, std_out, std_err] = |
| Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen"); |
| EXPECT_EQ( |
| 1, CountSubstrs(std_err, "<<CallCountGoogleTest::call_count_ == 10>>")); |
| } |
| |
| TEST_F(UnitTestModeTest, GoogleTestStaticTestSuiteFunctionsCalledOnce) { |
| auto [status, std_out, std_err] = |
| Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen:" |
| "CallCountPerFuzzTest.NeverFails"); |
| EXPECT_EQ(1, |
| CountSubstrs(std_err, "<<CallCountGoogleTest::SetUpTestSuite()>>")); |
| EXPECT_EQ( |
| 1, CountSubstrs(std_err, "<<CallCountGoogleTest::TearDownTestSuite()>>")); |
| } |
| |
| TEST_F(UnitTestModeTest, GoogleTestWorksWithProtoExtensionsUsedInSeeds) { |
| auto [status, std_out, std_err] = Run("MySuite.CheckProtoExtensions"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("Uses proto extensions")); |
| } |
| |
| TEST_F(UnitTestModeTest, UnitTestAndFuzzTestCanShareSuiteName) { |
| auto [status, std_out, std_err] = |
| Run("SharedSuite.WorksAsUnitTest:SharedSuite.WorksAsFuzzTest"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, RepeatedFieldsHaveMinSizeWhenInitialized) { |
| auto [status, std_out, std_err] = Run("MySuite.RepeatedFieldHasMinimumSize"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, OptionalProtoFieldCanHaveNoValue) { |
| auto [status, std_out, std_err] = Run("MySuite.FailsWhenFieldI32HasNoValue"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(UnitTestModeTest, OptionalProtoFieldThatIsUnsetNeverHasValue) { |
| auto [status, std_out, std_err] = Run("MySuite.FailsWhenFieldI64HasValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, ProtoFieldsThatAreUnsetNeverHaveValue) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhen64IntegralFieldsHaveValues"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| ProtoFieldThatAreCustomizedAndAlwaysSetHaveCorrectValue) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenFieldsOfTypeDoubleHasNoValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, ProtoFieldsThatAreAlwaysSetAlwaysHaveValue) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhen64IntegralFieldsHaveNoValues"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, CanCustomizeProtoFieldsWithTransformers) { |
| auto [status, std_out, std_err] = |
| Run("MySuite." |
| "FailsIfRepeatedEnumsHaveZeroValueAndOptionalEnumHasNonZeroValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| RequiredProtoFieldWillBeSetWhenNullnessIsNotCustomized) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenRequiredInt32FieldHasNoValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, RequiredProtoFieldThatIsNotAlwaysSetCanHaveNoValue) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenRequiredEnumFieldHasNoValue"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("cannot have null values")); |
| } |
| |
| TEST_F(UnitTestModeTest, OptionalProtoFieldThatIsNotAlwaysSetCanHaveNoValue) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenOptionalFieldU32HasNoValue"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(UnitTestModeTest, ProtobufOfMutatesTheProto) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenI32ContainsTheSecretNumber"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("Secret number is found")); |
| } |
| |
| TEST_F(UnitTestModeTest, ProtobufEnumEqualsLabel4) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsIfProtobufEnumEqualsLabel4"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT( |
| std_err, |
| HasSubstr("argument 0: fuzztest::internal::TestProtobuf::Label4")); |
| } |
| |
| TEST_F(UnitTestModeTest, WorksWithRecursiveStructs) { |
| auto [status, std_out, std_err] = Run("MySuite.WorksWithRecursiveStructs"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| // Nullptr has multiple possible human-readable representations. |
| EXPECT_THAT(std_err, AnyOf(HasSubstr("argument 0: {0, 1}"), |
| HasSubstr("argument 0: {(nil), 1}"))); |
| } |
| |
| TEST_F(UnitTestModeTest, WorksWithStructsWithConstructors) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.WorksWithStructsWithConstructors"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {1, \"abc\"}")); |
| } |
| |
| TEST_F(UnitTestModeTest, WorksWithStructsWithEmptyTuples) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.WorksWithStructsWithEmptyTuples"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {}")); |
| } |
| |
| TEST_F(UnitTestModeTest, WorksWithEmptyStructs) { |
| auto [status, std_out, std_err] = Run("MySuite.WorksWithEmptyStructs"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {}")); |
| } |
| |
| TEST_F(UnitTestModeTest, WorksWithStructsWithEmptyFields) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.WorksWithStructsWithEmptyFields"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {{}}")); |
| } |
| |
| TEST_F(UnitTestModeTest, WorksWithEmptyInheritance) { |
| auto [status, std_out, std_err] = Run("MySuite.WorksWithEmptyInheritance"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {0, \"abc\"}")); |
| } |
| |
| TEST_F(UnitTestModeTest, ArbitraryWorksWithEmptyInheritance) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.ArbitraryWorksWithEmptyInheritance"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("argument 0:")); |
| } |
| |
| TEST_F(UnitTestModeTest, FlatMapCorrectlyPrintsValues) { |
| auto [status, std_out, std_err] = Run("MySuite.FlatMapCorrectlyPrintsValues"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| // This is the argument to the output domain. |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {\"AAA\", \"BBB\"}")); |
| // This is the argument to the input domain. |
| EXPECT_THAT(std_err, Not(HasSubstr("argument 0: 3"))); |
| } |
| |
| TEST_F(UnitTestModeTest, PropertyFunctionAcceptsTupleOfItsSingleParameter) { |
| auto [status, std_out, std_err] = Run("MySuite.UnpacksTupleOfOne"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(UnitTestModeTest, PropertyFunctionAcceptsTupleOfItsThreeParameters) { |
| auto [status, std_out, std_err] = Run("MySuite.UnpacksTupleOfThree"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(UnitTestModeTest, PropertyFunctionAcceptsTupleContainingTuple) { |
| auto [status, std_out, std_err] = Run("MySuite.UnpacksTupleContainingTuple"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(UnitTestModeTest, ProtoFieldsCanBeAlwaysSet) { |
| auto [status, std_out, std_err] = Run("MySuite.FailsWhenSubprotoIsNull"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, ProtoFieldsCanBeUnset) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenSubprotoFieldsAreSet"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, RepeatedProtoFieldsCanBeCustomized) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenRepeatedSubprotoIsSmallOrHasAnEmptyElement"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, DefaultOptionalPolicyAppliesToAllOptionalFields) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenAnyOptionalFieldsHaveValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F( |
| UnitTestModeTest, |
| DefaultOptionalPolicyAppliesToAllOptionalFieldsWithoutOverwrittenDomain) { |
| auto [status, std_out, std_err] = Run( |
| "MySuite." |
| "FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenDomain"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F( |
| UnitTestModeTest, |
| DefaultOptionalPolicyAppliesToAllOptionalFieldsWithoutOverwrittenPolicy) { |
| auto [status, std_out, std_err] = Run( |
| "MySuite." |
| "FailsWhenAnyOptionalFieldsHaveValueButNotFieldsWithOverwrittenPolicy"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, DetectsRecursiveStructureIfOptionalsSetByDefault) { |
| auto [status, std_out, std_err] = Run("MySuite.FailsIfCantInitializeProto"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("recursive fields")); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| AvoidsFailureIfSetByDefaultPolicyIsOverwrittenOnRecursiveStructures) { |
| auto [status, std_out, std_err] = |
| Run("MySuite." |
| "InitializesRecursiveProtoIfInfiniteRecursivePolicyIsOverwritten"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| DefaultRepeatedFieldsMinSizeAppliesToAllRepeatedFields) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsIfRepeatedFieldsDontHaveTheMinimumSize"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, |
| DefaultRepeatedFieldsMaxSizeAppliesToAllRepeatedFields) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsIfRepeatedFieldsDontHaveTheMaximumSize"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, FailsWhenRepeatedFieldsSizeRangeIsInvalid) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsToInitializeIfRepeatedFieldsSizeRangeIsInvalid"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("size range is not valid")); |
| } |
| |
| TEST_F(UnitTestModeTest, UsesPolicyProvidedDefaultDomainForProtos) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenSubprotosDontSetOptionalI32"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, ChecksTypeOfProvidedDefaultDomainForProtos) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenWrongDefaultProtobufDomainIsProvided"); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| EXPECT_THAT(std_err, HasSubstr("does not match the expected message type")); |
| } |
| |
| TEST_F(UnitTestModeTest, PoliciesApplyToFieldsInOrder) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenI32FieldValuesDontRespectAllPolicies"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, AlwaysSetAndUnsetWorkOnOneofFields) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FailsWhenOneofFieldDoesntHaveOneofValue"); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(UnitTestModeTest, StackLimitWorks) { |
| #if defined(__has_feature) |
| #if !__has_feature(coverage_sanitizer) |
| GTEST_SKIP() << "No coverage instrumentation: skipping the stack limit test " |
| "in the unit test mode. Please run with --config=fuzztest to " |
| "enable these tests!"; |
| #endif |
| #endif |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() << "Skipping the stack limit test when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| |
| auto [status, std_out, std_err] = |
| Run("MySuite.DataDependentStackOverflow", kDefaultTargetBinary, |
| /*env=*/{}, /*fuzzer_flags=*/{{"stack_limit_kb", "1000"}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT(std_err, HasSubstr("Configured limit is 1024000.")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| // Tests for the FuzzTest command line interface. |
| class GenericCommandLineInterfaceTest : public ::testing::Test { |
| protected: |
| RunResults RunWith( |
| const absl::flat_hash_map<std::string, std::string>& flags, |
| const absl::flat_hash_map<std::string, std::string>& env = {}, |
| absl::Duration timeout = absl::Minutes(10), |
| absl::string_view binary = kDefaultTargetBinary) { |
| std::vector<std::string> args = {BinaryPath(binary)}; |
| for (const auto& [key, value] : flags) { |
| args.push_back(CreateFuzzTestFlag(key, value)); |
| } |
| return RunCommand(args, WithTestSanitizerOptions(env), timeout); |
| } |
| }; |
| |
| TEST_F(GenericCommandLineInterfaceTest, FuzzTestsAreFoundInTheBinary) { |
| auto [status, std_out, std_err] = RunWith({{"list_fuzz_tests", "true"}}); |
| EXPECT_THAT(std_out, HasSubstr("[*] Fuzz test: MySuite.Coverage")); |
| EXPECT_THAT(std_out, HasSubstr("[*] Fuzz test: MySuite.DivByZero")); |
| EXPECT_THAT(std_out, |
| HasSubstr("[*] Fuzz test: MySuite.PassesWithPositiveInput")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(GenericCommandLineInterfaceTest, |
| DynamicallyRegisteredFuzzTestsAreFound) { |
| auto [status, std_out, std_err] = |
| RunWith(/*flags=*/{{"list_fuzz_tests", "true"}}, /*env=*/{}, |
| /*timeout=*/absl::Minutes(1), |
| /*binary=*/"testdata/dynamically_registered_fuzz_tests"); |
| EXPECT_THAT(std_out, HasSubstr("[*] Fuzz test: TestSuiteOne.DoesNothing/1")); |
| EXPECT_THAT(std_out, HasSubstr("[*] Fuzz test: TestSuiteTwo.DoesNothing/2")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| // Tests for the FuzzTest command line interface in fuzzing mode, which can only |
| // run with coverage instrumentation enabled. |
| class FuzzingModeCommandLineInterfaceTest |
| : public GenericCommandLineInterfaceTest { |
| protected: |
| void SetUp() override { |
| GenericCommandLineInterfaceTest::SetUp(); |
| #if defined(__has_feature) |
| #if !__has_feature(coverage_sanitizer) |
| GTEST_SKIP() << "No coverage instrumentation: skipping the fuzzing mode " |
| "command line interface tests. " |
| "Please run with --config=fuzztest or --config=centipede " |
| "to enable these tests!"; |
| #endif |
| #endif |
| } |
| }; |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, WrongFuzzTestNameTriggersError) { |
| auto [status, std_out, std_err] = RunWith({{"fuzz", "WrongName"}}); |
| EXPECT_THAT(std_err, HasSubstr("No FUZZ_TEST matches the name: WrongName")); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| MatchingMultipleFuzzTestsTriggersError) { |
| auto [status, std_out, std_err] = RunWith({{"fuzz", "Bad"}}); |
| EXPECT_THAT( |
| std_err, |
| HasSubstr( |
| "Multiple FUZZ_TESTs match the name: Bad\n\nPlease select one. " |
| "Matching tests:\n MySuite.BadFilter\n MySuite.BadWithMinSize\n")); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, RunsAbortTestAndDetectsAbort) { |
| auto [status, std_out, std_err] = RunWith({{"fuzz", "MySuite.Aborts"}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| FuzzTestCanBeSelectedForFuzzingUsingSubstring) { |
| auto [status, std_out, std_err] = RunWith({{"fuzz", "Abort"}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| IgnoresNegativeFuzzingRunsLimitInEnvVar) { |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.PassesWithPositiveInput"}}, |
| {{"FUZZTEST_MAX_FUZZING_RUNS", "-1"}}, |
| /*timeout=*/absl::Seconds(1)); |
| EXPECT_THAT(std_err, HasSubstr("will not limit fuzzing runs")) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, LimitsFuzzingRunsWhenEnvVarIsSet) { |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.PassesWithPositiveInput"}}, |
| {{"FUZZTEST_MAX_FUZZING_RUNS", "100"}}); |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| EXPECT_THAT(std_err, HasSubstr("[S0.100] end-fuzz")) << std_err; |
| #else |
| EXPECT_THAT(std_err, |
| // 100 fuzzing runs + 1 seed run. |
| HasSubstr("Total runs: 101")) |
| << std_err; |
| #endif // FUZZTEST_USE_CENTIPEDE |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, LimitsFuzzingRunsWhenTimeoutIsSet) { |
| auto [status, std_out, std_err] = RunWith( |
| {{"fuzz", "MySuite.PassesWithPositiveInput"}, {"fuzz_for", "1s"}}); |
| EXPECT_THAT(std_err, HasSubstr("Fuzzing timeout set to: 1s")) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, ReproducerIsDumpedWhenEnvVarIsSet) { |
| TempDir out_dir; |
| |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.StringFast"}}, |
| {{"FUZZTEST_REPRODUCERS_OUT_DIR", out_dir.dirname()}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| |
| auto replay_files = ReadFileOrDirectory(out_dir.dirname()); |
| ASSERT_EQ(replay_files.size(), 1) << std_err; |
| auto parsed = IRObject::FromString(replay_files[0].data); |
| ASSERT_TRUE(parsed) << std_err; |
| auto args = parsed->ToCorpus<std::tuple<std::string>>(); |
| EXPECT_THAT(args, Optional(FieldsAre(StartsWith("Fuzz")))) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, SavesCorpusWhenEnvVarIsSet) { |
| TempDir out_dir; |
| // We cannot use a non-crashing test since there is no easy way to limit the |
| // run time here. |
| // |
| // Although theoretically possible, it is extreme unlikely that the test would |
| // find the crash without saving some corpus. |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_TESTSUITE_OUT_DIR", out_dir.dirname()}}); |
| |
| auto corpus_files = ReadFileOrDirectory(out_dir.dirname()); |
| EXPECT_THAT(corpus_files, Not(IsEmpty())) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, RestoresCorpusWhenEnvVarIsSet) { |
| TempDir corpus_dir; |
| // We cannot use a non-crashing test since there is no easy way to limit the |
| // run time here. |
| // |
| // Although theoretically possible, it is extreme unlikely that the test would |
| // find the crash without saving some corpus. |
| auto [producer_status, producer_std_out, producer_std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}}); |
| |
| auto corpus_files = ReadFileOrDirectory(corpus_dir.dirname()); |
| ASSERT_THAT(corpus_files, Not(IsEmpty())) << producer_std_err; |
| |
| auto [consumer_status, consumer_std_out, consumer_std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_TESTSUITE_IN_DIR", corpus_dir.dirname()}}); |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| EXPECT_THAT(consumer_std_err, HasSubstr(absl::StrFormat( |
| "inputs_added: %d", corpus_files.size()))); |
| #else |
| EXPECT_THAT(consumer_std_err, |
| HasSubstr(absl::StrFormat("Parsed %d inputs and ignored 0 inputs", |
| corpus_files.size()))); |
| #endif // FUZZTEST_USE_CENTIPEDE |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizesCorpusWhenEnvVarIsSet) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() |
| << "Skipping tests for corpus minimization when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| TempDir corpus_dir; |
| TempDir minimized_corpus_dir; |
| // We cannot use a non-crashing test since there is no easy way to limit the |
| // run time here. |
| // |
| // Although theoretically possible, it is extreme unlikely that the test would |
| // find the crash without saving some corpus. |
| auto [producer_status, producer_std_out, producer_std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}}); |
| |
| auto corpus_files = ReadFileOrDirectory(corpus_dir.dirname()); |
| ASSERT_THAT(corpus_files, Not(IsEmpty())) << producer_std_err; |
| std::vector<std::string> corpus_data; |
| for (const FilePathAndData& corpus_file : corpus_files) { |
| corpus_data.push_back(corpus_file.data); |
| } |
| |
| auto [minimizer_status, minimizer_std_out, minimizer_std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_MINIMIZE_TESTSUITE_DIR", corpus_dir.dirname()}, |
| {"FUZZTEST_TESTSUITE_OUT_DIR", minimized_corpus_dir.dirname()}}); |
| |
| auto minimized_corpus_files = |
| ReadFileOrDirectory(minimized_corpus_dir.dirname()); |
| EXPECT_THAT(minimized_corpus_files, |
| AllOf(Not(IsEmpty()), SizeIs(Le(corpus_files.size())))) |
| << minimizer_std_err; |
| std::vector<std::string> minimized_corpus_data; |
| for (const FilePathAndData& minimized_corpus_file : minimized_corpus_files) { |
| minimized_corpus_data.push_back(minimized_corpus_file.data); |
| } |
| EXPECT_THAT(minimized_corpus_data, IsSubsetOf(corpus_data)); |
| |
| EXPECT_THAT( |
| minimizer_std_err, |
| AllOf(HasSubstr(absl::StrFormat("Parsed %d inputs and ignored 0 inputs", |
| corpus_files.size())), |
| HasSubstr(absl::StrFormat( |
| "Selected %d corpus inputs in minimization mode", |
| minimized_corpus_files.size())))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizesDuplicatedCorpus) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() |
| << "Skipping tests for corpus minimization when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| TempDir corpus_dir; |
| TempDir minimized_corpus_dir; |
| // We cannot use a non-crashing test since there is no easy way to limit the |
| // run time here. |
| // |
| // Although theoretically possible, it is extreme unlikely that the test would |
| // find the crash without saving some corpus. |
| auto [producer_status, producer_std_out, producer_std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}}); |
| |
| auto corpus_files = ReadFileOrDirectory(corpus_dir.dirname()); |
| ASSERT_THAT(corpus_files, Not(IsEmpty())) << producer_std_err; |
| for (const auto& corpus_file : corpus_files) { |
| ASSERT_TRUE(WriteFile(corpus_file.path + "_dup", corpus_file.data)); |
| } |
| |
| auto [minimizer_status, minimizer_std_out, minimizer_std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, |
| {{"FUZZTEST_MINIMIZE_TESTSUITE_DIR", corpus_dir.dirname()}, |
| {"FUZZTEST_TESTSUITE_OUT_DIR", minimized_corpus_dir.dirname()}}); |
| |
| auto minimized_corpus_files = |
| ReadFileOrDirectory(minimized_corpus_dir.dirname()); |
| EXPECT_THAT(minimized_corpus_files, |
| AllOf(Not(IsEmpty()), SizeIs(Le(corpus_files.size())))) |
| << minimizer_std_err; |
| |
| EXPECT_THAT( |
| minimizer_std_err, |
| AllOf(HasSubstr(absl::StrFormat("Parsed %d inputs and ignored 0 inputs", |
| corpus_files.size() * 2)), |
| // TODO(b/207375007): Due to non-determinism, sometimes duplicated |
| // input can reach new coverage and thus be counted into the corpus |
| // (but not reflected in the files since they are |
| // content-addressed). We use AnyOf to mitigate the flakiness. |
| AnyOf(HasSubstr(absl::StrFormat( |
| "Selected %d corpus inputs in minimization mode", |
| minimized_corpus_files.size())), |
| HasSubstr(absl::StrFormat( |
| "Selected %d corpus inputs in minimization mode", |
| minimized_corpus_files.size() + 1)), |
| HasSubstr(absl::StrFormat( |
| "Selected %d corpus inputs in minimization mode", |
| minimized_corpus_files.size() + 2))))); |
| } |
| |
| class ReplayFile { |
| public: |
| template <typename T> |
| ReplayFile(std::in_place_t, const T& corpus) { |
| filename_ = absl::StrCat(dir_.dirname(), "/replay_file"); |
| WriteFile(filename_, internal::IRObject::FromCorpus(corpus).ToString()); |
| } |
| |
| auto GetReplayEnv() const { |
| return absl::flat_hash_map<std::string, std::string>{ |
| {"FUZZTEST_REPLAY", filename_}}; |
| } |
| |
| auto GetMinimizeEnv() const { |
| return absl::flat_hash_map<std::string, std::string>{ |
| {"FUZZTEST_MINIMIZE_REPRODUCER", filename_}}; |
| } |
| |
| private: |
| TempDir dir_; |
| std::string filename_; |
| }; |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ReplayingNonCrashingReproducerDoesNotCrash) { |
| ReplayFile replay(std::in_place, std::tuple<std::string>{"NotFuzz"}); |
| |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, replay.GetReplayEnv()); |
| EXPECT_THAT(status, Eq(ExitCode(0))) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ReplayingCrashingReproducerCrashes) { |
| ReplayFile replay(std::in_place, |
| std::tuple<std::string>{"Fuzz with some tail."}); |
| |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.String"}}, replay.GetReplayEnv()); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz with some tail.\"")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| // The TypeErased tests below try the same as above, but with Domain<T> domains |
| // to makes sure the type erasure of the corpus_type can be correctly |
| // serialized. |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ReproducerIsDumpedWhenEnvVarIsSetTypeErased) { |
| TempDir out_dir; |
| |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.WithDomainClass"}}, |
| {{"FUZZTEST_REPRODUCERS_OUT_DIR", out_dir.dirname()}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: 10")) << std_err; |
| EXPECT_THAT(status, Ne(ExitCode(0))) << std_err; |
| |
| auto replay_files = ReadFileOrDirectory(out_dir.dirname()); |
| ASSERT_EQ(replay_files.size(), 1) << std_err; |
| auto parsed = IRObject::FromString(replay_files[0].data); |
| ASSERT_TRUE(parsed) << std_err; |
| auto args = parsed->ToCorpus<std::tuple<uint8_t, double>>(); |
| EXPECT_THAT(args, Optional(FieldsAre(10, _))) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ReplayingNonCrashingReproducerDoesNotCrashTypeErased) { |
| ReplayFile replay(std::in_place, std::tuple<uint8_t, double>{11, 11}); |
| |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.WithDomainClass"}}, replay.GetReplayEnv()); |
| EXPECT_THAT(status, Eq(ExitCode(0))) << std_err; |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ReplayingCrashingReproducerCrashesTypeErased) { |
| ReplayFile replay(std::in_place, std::tuple<uint8_t, double>{10, 1979.125}); |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.WithDomainClass"}}, replay.GetReplayEnv()); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: 10")); |
| EXPECT_THAT(std_err, HasSubstr("argument 1: 1979.125")); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizerFindsSmallerInput) { |
| std::string current_input = "ABCXDEF"; |
| while (current_input != "X") { |
| TempDir out_dir; |
| ReplayFile replay(std::in_place, std::tuple<std::string>{current_input}); |
| auto env = replay.GetMinimizeEnv(); |
| env["FUZZTEST_REPRODUCERS_OUT_DIR"] = out_dir.dirname(); |
| |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.Minimizer"}}, env); |
| ASSERT_THAT(std_err, HasSubstr("argument 0: \"")); |
| ASSERT_THAT(status, Eq(Signal(SIGABRT))); |
| |
| auto replay_files = ReadFileOrDirectory(out_dir.dirname()); |
| ASSERT_EQ(replay_files.size(), 1) << std_err; |
| auto parsed = IRObject::FromString(replay_files[0].data); |
| ASSERT_TRUE(parsed) << std_err; |
| auto args = parsed->ToCorpus<std::tuple<std::string>>(); |
| ASSERT_THAT(args, Optional(FieldsAre(HasSubstr("X")))); |
| std::string escaped; |
| StringPrinter{}.PrintUserValue(std::get<0>(*args), &escaped, |
| PrintMode::kHumanReadable); |
| fprintf(stderr, "Found smaller case <%s>\n", escaped.c_str()); |
| current_input = std::get<0>(*args); |
| } |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| FuzzerStatsArePrintedOnTermination) { |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.PassesWithPositiveInput"}}, |
| /*env=*/{}, |
| /*timeout=*/absl::Seconds(1)); |
| EXPECT_THAT(std_err, HasSubstr("Fuzzing was terminated")); |
| EXPECT_THAT(std_err, HasSubstr("=== Fuzzing stats")); |
| EXPECT_THAT(std_err, HasSubstr("Total runs:")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, SilenceTargetWorking) { |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.TargetPrintSomethingThenAbrt"}}, |
| /*env=*/{{"FUZZTEST_SILENCE_TARGET", "1"}}); |
| EXPECT_THAT(std_out, Not(HasSubstr("Hello World from target stdout"))); |
| EXPECT_THAT(std_err, HasSubstr("=== Fuzzing stats")); |
| EXPECT_THAT(std_err, Not(HasSubstr("Hello World from target stderr"))); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, NonFatalFailureAllowsMinimization) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() |
| << "Skipping tests for non-fatal failure minimization when running with " |
| "Centipede. Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.NonFatalFailureAllowsMinimization"}}); |
| // The final failure should be with the known minimal result, even though many |
| // "larger" inputs also trigger the failure. |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"0123\"")); |
| |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, GoogleTestHasCurrentTestInfo) { |
| auto [status, std_out, std_err] = RunWith( |
| {{"fuzz", "MySuite.GoogleTestHasCurrentTestInfo"}, {"fuzz_for", "1s"}}); |
| EXPECT_THAT(std_out, |
| HasSubstr("[ OK ] MySuite.GoogleTestHasCurrentTestInfo")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, ConfiguresStackLimitByFlag) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() << "Skipping the stack limit test when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.DataDependentStackOverflow"}, |
| {"stack_limit_kb", "1000"}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT(std_err, HasSubstr("Configured limit is 1024000.")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ConfiguresStackLimitByEnvVarWithWarning) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() << "Skipping the stack limit test when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.DataDependentStackOverflow"}}, |
| {{"FUZZTEST_STACK_LIMIT", "1234567"}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT(std_err, |
| HasSubstr("Stack limit is set by FUZZTEST_STACK_LIMIT env var " |
| "- this is going to be deprecated soon. Consider " |
| "switching to --" FUZZTEST_FLAG_PREFIX_ |
| "stack_limit_kb flag.")); |
| EXPECT_THAT(std_err, HasSubstr("Configured limit is 1234567.")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| ConfiguresStackLimitByEnvVarAndOverridesFlag) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() << "Skipping the stack limit test when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.DataDependentStackOverflow"}, |
| {"stack_limit_kb", "1000"}}, |
| {{"FUZZTEST_STACK_LIMIT", "1234567"}}); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT(std_err, HasSubstr("Configured limit is 1234567.")); |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, |
| DoesNotPrintWarningForDisabledLimitFlagsByDefault) { |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.PassesWithPositiveInput"}}, |
| /*env=*/{}, |
| /*timeout=*/absl::Seconds(1)); |
| EXPECT_THAT(std_err, |
| Not(HasSubstr("limit is specified but will be ignored for now"))); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, RssLimitFlagExistsButIsDisabled) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() << "Skipping the rss limit test when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = RunWith( |
| {{"fuzz", "MySuite.PassesWithPositiveInput"}, {"rss_limit_mb", "1234"}}, |
| /*env=*/{}, |
| /*timeout=*/absl::Seconds(1)); |
| EXPECT_THAT(std_err, |
| HasSubstr("RSS limit is specified but will be ignored for now")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_F(FuzzingModeCommandLineInterfaceTest, TimeLimitFlagExistsButIsDisabled) { |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() << "Skipping the time limit test when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = |
| RunWith({{"fuzz", "MySuite.PassesWithPositiveInput"}, |
| {"time_limit_per_input", "1s"}}, |
| /*env=*/{}, |
| /*timeout=*/absl::Seconds(1)); |
| EXPECT_THAT( |
| std_err, |
| HasSubstr( |
| "Per-input time limit is specified but will be ignored for now")); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| std::string CentipedePath() { |
| const auto test_srcdir = absl::NullSafeStringView(getenv("TEST_SRCDIR")); |
| FUZZTEST_INTERNAL_CHECK_PRECONDITION( |
| !test_srcdir.empty(), |
| "Please set TEST_SRCDIR to non-empty value or use bazel to run the " |
| "test."); |
| const std::string binary_path = absl::StrCat( |
| test_srcdir, |
| "/com_google_fuzztest/centipede/centipede_uninstrumented"); |
| |
| FUZZTEST_INTERNAL_CHECK(std::filesystem::exists(binary_path), |
| absl::StrCat("Can't find ", binary_path)); |
| return binary_path; |
| } |
| |
| struct ExecutionModelParam { |
| bool multi_process; |
| }; |
| |
| std::vector<ExecutionModelParam> GetAvailableExecutionModels() { |
| std::vector<ExecutionModelParam> results = {{/*multi_process=*/false}}; |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| results.push_back({/*multi_process=*/true}); |
| #endif |
| return results; |
| } |
| |
| // Tests for the fixture logic in fuzzing mode, which can only run |
| // with coverage instrumentation enabled. |
| class FuzzingModeFixtureTest |
| : public ::testing::TestWithParam<ExecutionModelParam> { |
| protected: |
| void SetUp() override { |
| #if defined(__has_feature) |
| #if !__has_feature(coverage_sanitizer) |
| GTEST_SKIP() << "No coverage instrumentation: skipping the fuzzing mode " |
| "fixture tests. " |
| "Please run with --config=fuzztest or --config=centipede " |
| "to enable these tests!"; |
| #endif |
| #endif |
| } |
| |
| RunResults Run(absl::string_view test_name, int iterations) { |
| if (GetParam().multi_process) { |
| TempDir workdir; |
| return RunCommand( |
| {CentipedePath(), "--print_runner_log", "--exit_on_crash", |
| absl::StrCat("--workdir=", workdir.dirname()), |
| absl::StrCat("--binary=", BinaryPath(kDefaultTargetBinary), " ", |
| CreateFuzzTestFlag("fuzz", test_name)), |
| absl::StrCat("--num_runs=", iterations)}, |
| /*environment=*/{}, |
| /*timeout=*/absl::InfiniteDuration()); |
| } else { |
| return RunCommand( |
| {BinaryPath(kDefaultTargetBinary), |
| CreateFuzzTestFlag("fuzz", test_name)}, |
| {{"FUZZTEST_MAX_FUZZING_RUNS", absl::StrCat(iterations)}}, |
| /*timeout=*/absl::InfiniteDuration()); |
| } |
| } |
| |
| // Counts the number of times the target binary has been run. Needed because |
| // Centipede runs the binary multiple times. |
| int CountTargetRuns(absl::string_view std_err) { |
| if (GetParam().multi_process) { |
| return CountSubstrs(std_err, "Centipede fuzz target runner; argv[0]:"); |
| } else { |
| return 1; |
| } |
| } |
| }; |
| |
| TEST_P(FuzzingModeFixtureTest, GlobalEnvironmentIsSetUpForFailingTest) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.GoogleTestExpect", /*iterations=*/10); |
| EXPECT_GT(CountTargetRuns(std_err), 0); |
| EXPECT_EQ( |
| CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<GlobalEnvironment::GlobalEnvironment()>>")); |
| EXPECT_EQ(CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<GlobalEnvironment::SetUp()>>")); |
| } |
| |
| TEST_P(FuzzingModeFixtureTest, |
| GlobalEnvironmentGoesThroughCompleteLifecycleForSuccessfulTest) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.GoogleTestNeverFails", /*iterations=*/10); |
| EXPECT_GT(CountTargetRuns(std_err), 0); |
| EXPECT_EQ( |
| CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<GlobalEnvironment::GlobalEnvironment()>>")); |
| EXPECT_EQ(CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<GlobalEnvironment::SetUp()>>")); |
| EXPECT_EQ(CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<GlobalEnvironment::TearDown()>>")); |
| EXPECT_EQ( |
| CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<GlobalEnvironment::~GlobalEnvironment()>>")); |
| } |
| |
| TEST_P(FuzzingModeFixtureTest, FixtureGoesThroughCompleteLifecycle) { |
| auto [status, std_out, std_err] = Run("FixtureTest.NeverFails", |
| /*iterations=*/10); |
| EXPECT_GT(CountTargetRuns(std_err), 0); |
| EXPECT_EQ(CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<FixtureTest::FixtureTest()>>")); |
| EXPECT_EQ(CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<FixtureTest::~FixtureTest()>>")); |
| } |
| |
| TEST_P(FuzzingModeFixtureTest, |
| GoogleTestPerIterationFixtureInstantiatedOncePerIteration) { |
| auto [status, std_out, std_err] = |
| Run("CallCountPerIteration." |
| "CallCountIsAlwaysIncrementedFromInitialValue", |
| /*iterations=*/10); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_P(FuzzingModeFixtureTest, |
| GoogleTestPerFuzzTestFixtureInstantiatedOncePerFuzzTest) { |
| auto [status, std_out, std_err] = |
| Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen", /*iterations=*/10); |
| EXPECT_THAT(std_err, HasSubstr("<<CallCountGoogleTest::call_count_ == 10>>")); |
| } |
| |
| TEST_P(FuzzingModeFixtureTest, GoogleTestStaticTestSuiteFunctionsCalledOnce) { |
| auto [status, std_out, std_err] = |
| Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen", /*iterations=*/10); |
| EXPECT_GT(CountTargetRuns(std_err), 0); |
| EXPECT_EQ(CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<CallCountGoogleTest::SetUpTestSuite()>>")); |
| EXPECT_EQ( |
| CountTargetRuns(std_err), |
| CountSubstrs(std_err, "<<CallCountGoogleTest::TearDownTestSuite()>>")); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(FuzzingModeFixtureTestWithExecutionModel, |
| FuzzingModeFixtureTest, |
| testing::ValuesIn(GetAvailableExecutionModels())); |
| |
| // Tests for the crash finding ability of the fuzzing mode, which can |
| // only run with coverage instrumentation enabled. |
| class FuzzingModeCrashFindingTest |
| : public ::testing::TestWithParam<ExecutionModelParam> { |
| protected: |
| void SetUp() override { |
| #if defined(__has_feature) |
| #if !__has_feature(coverage_sanitizer) |
| GTEST_SKIP() << "No coverage instrumentation: skipping the fuzzing mode " |
| "crash finding tests. " |
| "Please run with --config=fuzztest or --config=centipede " |
| "to enable these tests!"; |
| #endif |
| #endif |
| } |
| |
| RunResults Run(absl::string_view test_name, |
| absl::string_view target_binary = kDefaultTargetBinary, |
| absl::Duration timeout = absl::InfiniteDuration()) { |
| // We start the test binaries with an empty environment. This is |
| // useful because we don't want to propagate env vars set by |
| // Bazel, such as TEST_SHARD_INDEX, TEST_TOTAL_SHARDS, |
| // TEST_PREMATURE_EXIT_FILE, TEST_WARNINGS_OUTPUT_FILE, etc. (See |
| // https://bazel.build/reference/test-encyclopedia#initial-conditions.) |
| // There are however env vars that we do want to propagate, which |
| // we now need to do explicitly. |
| absl::flat_hash_map<std::string, std::string> environment = |
| WithTestSanitizerOptions({}); |
| if (GetParam().multi_process) { |
| TempDir workdir; |
| return RunCommand( |
| {CentipedePath(), "--exit_on_crash", "--timeout_per_input=0", |
| absl::StrCat("--stop_at=", absl::Now() + timeout), |
| absl::StrCat("--workdir=", workdir.dirname()), |
| absl::StrCat("--binary=", BinaryPath(target_binary), " ", |
| CreateFuzzTestFlag("fuzz", test_name))}, |
| environment, timeout + absl::Seconds(10)); |
| } else { |
| return RunCommand( |
| {BinaryPath(target_binary), CreateFuzzTestFlag("fuzz", test_name)}, |
| environment, timeout); |
| } |
| } |
| |
| void ExpectTargetAbort(TerminationStatus status, absl::string_view std_err) { |
| if (GetParam().multi_process) { |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| EXPECT_TRUE(RE2::PartialMatch( |
| std_err, absl::StrCat("Exit code\\s*:\\s*", SIGABRT))) |
| << std_err; |
| } else { |
| EXPECT_THAT(status, Eq(Signal(SIGABRT))); |
| } |
| } |
| }; |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| BufferOverflowIsDetectedWithStringViewInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.BufferOverreadWithStringView"); |
| EXPECT_THAT( |
| std_err, |
| AnyOf(HasSubstr("ERROR: AddressSanitizer: container-overflow"), |
| HasSubstr("ERROR: AddressSanitizer: heap-buffer-overflow"))); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| DereferencingEmptyOptionalTriggersLibcppAssertionsWhenEnabled) { |
| #if defined(_LIBCPP_VERSION) && defined(_LIBCPP_ENABLE_ASSERTIONS) |
| auto [status, std_out, std_err] = Run("MySuite.DereferenceEmptyOptional"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: std::nullopt")); |
| ExpectTargetAbort(status, std_err); |
| #endif |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| BufferOverflowIsDetectedWithStringInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.BufferOverreadWithString"); |
| EXPECT_THAT(std_err, |
| HasSubstr("ERROR: AddressSanitizer: heap-buffer-overflow")); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| BufferOverflowIsDetectedWithStringAndLvalueStringViewRef) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.BufferOverreadWithStringAndLvalueStringViewRef"); |
| EXPECT_THAT(std_err, |
| HasSubstr("ERROR: AddressSanitizer: heap-buffer-overflow")); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| BufferOverflowIsDetectedWithStringAndRvalueStringViewRef) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.BufferOverreadWithStringAndRvalueStringViewRef"); |
| EXPECT_THAT(std_err, |
| HasSubstr("ERROR: AddressSanitizer: heap-buffer-overflow")); |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, DivByZeroTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.DivByZero"); |
| EXPECT_THAT(std_err, HasSubstr("argument 1: 0")); |
| #ifdef ADDRESS_SANITIZER |
| EXPECT_THAT(status, Ne(ExitCode(0))); |
| #else |
| EXPECT_THAT(status, Eq(Signal(SIGFPE))); |
| #endif |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, CoverageTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.Coverage"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: 'F'")); |
| EXPECT_THAT(std_err, HasSubstr("argument 1: 'u'")); |
| EXPECT_THAT(std_err, HasSubstr("argument 2: 'z'")); |
| EXPECT_THAT(std_err, HasSubstr("argument 3: 'z'")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, StringTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.String"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| StringAsciiOnlyTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.StringAsciiOnly"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, StringRegexpTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.StringRegexp"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, StringViewTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.StringView"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, StrCmpTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.StrCmp"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"Hello!")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, BitFlagsFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.BitFlags"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: 21")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, EnumTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.EnumValue"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: Color{0}")); |
| EXPECT_THAT(std_err, HasSubstr("argument 1: Color{1}")); |
| EXPECT_THAT(std_err, HasSubstr("argument 2: Color{2}")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, EnumClassTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.EnumClassValue"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ColorClass{0}")); |
| EXPECT_THAT(std_err, HasSubstr("argument 1: ColorClass{1}")); |
| EXPECT_THAT(std_err, HasSubstr("argument 2: ColorClass{2}")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, ProtoTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.Proto"); |
| EXPECT_THAT(std_err, ContainsRegex(R"(argument 0: \((.*\n.*)?b:\s+true)")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, BitvectorValueTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.BitvectorValue"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {true")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, VectorValueTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.VectorValue"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {'F'")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| FixedSizeVectorValueTestFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = Run("MySuite.FixedSizeVectorValue"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {'F'")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, GoogleTestExpectationsStopTheFuzzer) { |
| auto [status, std_out, std_err] = Run("MySuite.GoogleTestExpect"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| ExpectTargetAbort(status, std_err); |
| |
| #ifndef FUZZTEST_USE_CENTIPEDE |
| |
| // There is the repro example only on stderr. |
| EXPECT_THAT(std_out, |
| Not(HasReproducerTest("MySuite", "GoogleTestExpect", ".*"))); |
| #endif |
| EXPECT_THAT(std_err, HasReproducerTest("MySuite", "GoogleTestExpect", ".*")); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, GoogleTestAssertionsStopTheFuzzer) { |
| auto [status, std_out, std_err] = Run("MySuite.GoogleTestAssert"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| ExpectTargetAbort(status, std_err); |
| |
| #ifndef FUZZTEST_USE_CENTIPEDE |
| |
| // There is the repro example only on stderr. |
| EXPECT_THAT(std_out, |
| Not(HasReproducerTest("MySuite", "GoogleTestAssert", ".*"))); |
| |
| #endif |
| EXPECT_THAT(std_err, HasReproducerTest("MySuite", "GoogleTestAssert", ".*")); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, MappedDomainShowsMappedValue) { |
| auto [status, std_out, std_err] = Run("MySuite.Mapping"); |
| EXPECT_THAT( |
| std_err, |
| AllOf( |
| HasSubstr("argument 0: \"12 monkeys\""), |
| HasReproducerTest( |
| "MySuite", "Mapping", |
| // Account for the possibility that the symbol |
| // NumberOfAnimalsToString may be mangled. |
| R"re(.*NumberOfAnimalsToString.*\(TimesTwo\(6\), "monkey"\))re"))); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, FlatMappedDomainShowsMappedValue) { |
| auto [status, std_out, std_err] = Run("MySuite.FlatMapping"); |
| EXPECT_THAT(std_err, AllOf(HasSubstr("argument 0: {\"abc\", 2}"), |
| HasReproducerTest( |
| "MySuite", "FlatMapping", |
| // Account for the possibility that the symbol |
| // StringAndValidIndex may be mangled. |
| R"re(.*StringAndValidIndex.*\("abc"\))re"))); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, FlatMapPassesWhenCorrect) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FlatMapPassesWhenCorrect", kDefaultTargetBinary, |
| /*timeout=*/absl::Seconds(1)); |
| EXPECT_THAT(status, Eq(ExitCode(0))); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, FilterDomainShowsOnlyFilteredValues) { |
| auto [status, std_out, std_err] = Run("MySuite.Filtering"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: 8")); |
| EXPECT_THAT(std_err, HasSubstr("argument 1: 9")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, BadFilterTriggersAnAbort) { |
| auto [status, std_out, std_err] = Run("MySuite.BadFilter"); |
| EXPECT_THAT(std_err, HasSubstr("Ineffective use of Filter()")); |
| EXPECT_THAT(std_err, Not(HasSubstr("argument 0:"))); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, BadWithMinSizeTriggersAnAbort) { |
| auto [status, std_out, std_err] = Run("MySuite.BadWithMinSize"); |
| EXPECT_THAT(std_err, HasSubstr("Ineffective use of WithSize()")); |
| EXPECT_THAT(std_err, Not(HasSubstr("argument 0:"))); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, SmartPointer) { |
| auto [status, std_out, std_err] = Run("MySuite.SmartPointer"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: (1")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, UnprintableTypeRunsAndPrintsSomething) { |
| auto [status, std_out, std_err] = Run("MySuite.UsesUnprintableType"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: <unprintable value>")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, MyStructTestArbitraryCanPrint) { |
| auto [status, std_out, std_err] = Run("MySuite.MyStructArbitrary"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {0, \"X")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, MyStructTestWithDomainsCanPrint) { |
| auto [status, std_out, std_err] = Run("MySuite.MyStructWithDomains"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {0, \"X")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, ConstructorPrintsSomething) { |
| auto [status, std_out, std_err] = Run("MySuite.ConstructorWithDomains"); |
| EXPECT_THAT(std_err, HasSubstr("\"ccc\"")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, SeedInputIsUsed) { |
| auto [status, std_out, std_err] = Run("MySuite.SeedInputIsUsed"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {9439518, 21, 49153}")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| SeedInputIsUsedInProtobufsWithInternalMappings) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.SeedInputIsUsedInProtobufsWithInternalMappings"); |
| EXPECT_THAT(std_err, HasSubstr("subproto_i32: 9439518")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, SeedInputIsUsedForMutation) { |
| auto [status, std_out, std_err] = Run("MySuite.SeedInputIsUsedForMutation"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: {1979, 19, 1234, 5678}")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, UsesManualDictionary) { |
| auto [status, std_out, std_err] = Run("MySuite.StringPermutationTest"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"9876543210\"")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, UsesSeededDomain) { |
| auto [status, std_out, std_err] = Run("MySuite.StringPermutationWithSeeds"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"9876543210\"")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, UsesSeedFromSeedProvider) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.StringPermutationWithSeedProvider"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"9876543210\"")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, UsesSeedFromSeedProviderOnFixture) { |
| auto [status, std_out, std_err] = |
| Run("SeededFixture.StringPermutationWithSeedProvider"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: \"9876543210\"")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| FunctionPointerAliasFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FunctionPointerAliasesAreFuzzable"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| FunctionReferenceAliasFindsAbortInFuzzingMode) { |
| auto [status, std_out, std_err] = |
| Run("MySuite.FunctionReferenceAliasesAreFuzzable"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, FuzzTestCanFindStackOverflows) { |
| // TODO(b/302012926): Consolidate the stack overflow checking between FuzzTest |
| // and Centipede. |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() |
| << "Skipping the stack calculation tests when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = Run("MySuite.DataDependentStackOverflow"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: ")); |
| EXPECT_THAT( |
| std_err, |
| ContainsRegex("Code under test used [0-9]* bytes of stack. Configured " |
| "limit is 131072. You can change the limit by specifying " |
| "--" FUZZTEST_FLAG_PREFIX_ "stack_limit_kb flag.")); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| TEST_P(FuzzingModeCrashFindingTest, |
| StackCalculationWorksWithAlternateStackForSignalHandlers) { |
| // TODO(b/302012926): Consolidate the stack overflow checking between FuzzTest |
| // and Centipede. |
| #ifdef FUZZTEST_USE_CENTIPEDE |
| GTEST_SKIP() |
| << "Skipping the stack calculation tests when running with Centipede. " |
| "Please run with --config=fuzztest to enable these tests!"; |
| #endif |
| auto [status, std_out, std_err] = |
| Run("MySuite.StackCalculationWorksWithAlternateStackForSignalHandlers"); |
| EXPECT_THAT(std_err, HasSubstr("argument 0: 123456789")); |
| EXPECT_THAT(std_err, |
| Not(HasSubstr("You can change the limit by specifying"))); |
| ExpectTargetAbort(status, std_err); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(FuzzingModeCrashFindingTestWithExecutionModel, |
| FuzzingModeCrashFindingTest, |
| testing::ValuesIn(GetAvailableExecutionModels())); |
| |
| } // namespace |
| } // namespace fuzztest::internal |