Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions e2e_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ cc_test(
"@com_google_fuzztest//centipede:centipede_uninstrumented",
"@com_google_fuzztest//e2e_tests/testdata:data",
"@com_google_fuzztest//e2e_tests/testdata:dynamically_registered_fuzz_tests.stripped",
"@com_google_fuzztest//e2e_tests/testdata:fuzz_test_without_init_fuzztest.stripped",
"@com_google_fuzztest//e2e_tests/testdata:fuzz_tests_for_functional_testing.stripped",
"@com_google_fuzztest//e2e_tests/testdata:fuzz_tests_with_invalid_seeds.stripped",
"@com_google_fuzztest//e2e_tests/testdata:llvm_fuzzer_with_custom_mutator.stripped",
Expand Down
13 changes: 13 additions & 0 deletions e2e_tests/functional_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,19 @@ TEST_F(UnitTestModeTest, PassingTestPassesInUnitTestingMode) {
EXPECT_THAT(status, Eq(ExitCode(0)));
}

TEST_F(UnitTestModeTest, FailsLoudlyWhenInitFuzzTestIsNotCalled) {
auto [status, std_out, std_err] =
Run(/*test_filter=*/"*",
/*target_binary=*/"testdata/fuzz_test_without_init_fuzztest");
EXPECT_THAT(status, Ne(ExitCode(0)));
const std::string output = absl::StrCat(std_out, std_err);
EXPECT_THAT_LOG(output, HasSubstr("FuzzTest was not initialized!"));
EXPECT_THAT_LOG(output,
HasSubstr("FUZZ_TEST was registered, but InitFuzzTest was "
"never called in main()."));
EXPECT_THAT_LOG(output, HasSubstr("InitFuzzTest"));
}

TEST_F(UnitTestModeTest, InvalidSeedsAreSkippedAndReported) {
auto [status, std_out, std_err] =
Run(/*test_filter=*/"*",
Expand Down
10 changes: 10 additions & 0 deletions e2e_tests/testdata/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,13 @@ cc_binary(
"@com_google_fuzztest//fuzztest:llvm_fuzzer_wrapper",
],
)

cc_binary(
name = "fuzz_test_without_init_fuzztest",
testonly = 1,
srcs = ["fuzz_test_without_init_fuzztest.cc"],
deps = [
"@com_google_fuzztest//fuzztest",
"@googletest//:gtest",
],
)
17 changes: 17 additions & 0 deletions e2e_tests/testdata/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,20 @@ set_target_properties(
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/_main/e2e_tests/testdata"
)

add_executable(
fuzz_test_without_init_fuzztest.stripped
fuzz_test_without_init_fuzztest.cc
)
target_link_libraries(
fuzz_test_without_init_fuzztest.stripped
PUBLIC
fuzztest::fuzztest
GTest::gtest
)
set_target_properties(
fuzz_test_without_init_fuzztest.stripped
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/_main/e2e_tests/testdata"
)

28 changes: 28 additions & 0 deletions e2e_tests/testdata/fuzz_test_without_init_fuzztest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 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
//
// https://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 "gtest/gtest.h"
#include "./fuzztest/fuzztest.h"

namespace {

void MyFuzzTest(int x) {}
FUZZ_TEST(MySuite, MyFuzzTest);

} // namespace

int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
18 changes: 17 additions & 1 deletion fuzztest/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ config_setting(

cc_library(
name = "fuzztest",
hdrs = ["fuzztest.h"],
testonly = True,
hdrs = [
"fuzztest.h",
],
deps = [
":domain",
":fuzztest_macros",
":init_fuzztest_checker",
],
)

Expand Down Expand Up @@ -142,6 +146,18 @@ cc_library(
alwayslink = True,
)

cc_library(
name = "init_fuzztest_checker",
testonly = True,
hdrs = ["init_fuzztest_checker.h"],
deps = [
"@abseil-cpp//absl/strings:string_view",
"@com_google_fuzztest//fuzztest/internal:registry",
"@com_google_fuzztest//fuzztest/internal:runtime",
"@googletest//:gtest",
],
)

# TODO(hadi88): Add an e2e test for llvm_fuzzer_wrapper.
cc_library(
name = "llvm_fuzzer_main",
Expand Down
13 changes: 13 additions & 0 deletions fuzztest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ fuzztest_cc_test(
GTest::gmock_main
)

fuzztest_cc_library(
NAME
init_fuzztest_checker
HDRS
"init_fuzztest_checker.h"
DEPS
GTest::gtest
absl::string_view
fuzztest::registry
fuzztest::runtime
)

fuzztest_cc_library(
NAME
fuzztest
Expand All @@ -92,6 +104,7 @@ fuzztest_cc_library(
DEPS
fuzztest::domain
fuzztest::fuzztest_macros
fuzztest::init_fuzztest_checker
)

fuzztest_cc_library(
Expand Down
1 change: 1 addition & 0 deletions fuzztest/fuzztest.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// IWYU pragma: begin_exports
#include "./fuzztest/domain.h"
#include "./fuzztest/fuzztest_macros.h"
#include "./fuzztest/init_fuzztest_checker.h"
// IWYU pragma: end_exports

#endif // FUZZTEST_FUZZTEST_FUZZTEST_H_
1 change: 1 addition & 0 deletions fuzztest/init_fuzztest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ void RunSpecifiedFuzzTest(std::string_view name, std::string_view binary_id) {

void InitFuzzTest(int* argc, char*** argv, std::string_view binary_id) {
auto& runtime = internal::Runtime::instance();
runtime.SetInitFuzzTestCalled(true);
const bool is_listing = absl::GetFlag(FUZZTEST_FLAG(list_fuzz_tests));
if (is_listing) {
for (const auto& name : ListRegisteredTests()) {
Expand Down
69 changes: 69 additions & 0 deletions fuzztest/init_fuzztest_checker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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
//
// https://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.

#ifndef FUZZTEST_FUZZTEST_INIT_FUZZTEST_CHECKER_H_
#define FUZZTEST_FUZZTEST_INIT_FUZZTEST_CHECKER_H_

#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "./fuzztest/internal/registry.h"
#include "./fuzztest/internal/runtime.h"

namespace fuzztest {

__attribute__((weak)) void InitFuzzTest(int* argc, char*** argv,
std::string_view binary_id);

namespace internal {

inline constexpr absl::string_view kInitFuzzTestFailureMessage =
"FuzzTest was not initialized! "
"FUZZ_TEST was registered, but InitFuzzTest was never "
"called in main(). "
"If you are using a custom main(), please call "
"fuzztest::InitFuzzTest(&argc, &argv)"
" before RUN_ALL_TESTS().";

inline void CheckFuzzTestInitialization() {
if (HasRegisteredFuzzTests() &&
(InitFuzzTest == nullptr ||
!Runtime::instance().init_fuzztest_called())) {
ADD_FAILURE() << kInitFuzzTestFailureMessage;
}
}

class FuzzTestInitVerificationListener
: public ::testing::EmptyTestEventListener {
public:
void OnTestIterationStart(const ::testing::UnitTest&, int) override {
CheckFuzzTestInitialization();
}
};

inline bool RegisterFuzzTestInitVerification() {
static bool registered = [] {
::testing::UnitTest::GetInstance()->listeners().Append(
new FuzzTestInitVerificationListener);
return true;
}();
return registered;
}

[[maybe_unused]] inline const bool g_fuzztest_init_checker_registered =
RegisterFuzzTestInitVerification();

} // namespace internal
} // namespace fuzztest

#endif // FUZZTEST_FUZZTEST_INIT_FUZZTEST_CHECKER_H_
2 changes: 2 additions & 0 deletions fuzztest/internal/registry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ void ForEachTest(absl::FunctionRef<void(FuzzTest&)> func) {
for (auto& t : Regs()) func(t);
}

bool HasRegisteredFuzzTests() { return !Regs().empty(); }

void RegisterImpl(BasicTestInfo test_info, FuzzTestFuzzerFactory factory) {
Regs().emplace_back(std::move(test_info), std::move(factory));
}
Expand Down
2 changes: 2 additions & 0 deletions fuzztest/internal/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ namespace internal {

void RegisterImpl(BasicTestInfo test_info, FuzzTestFuzzerFactory factory);

bool HasRegisteredFuzzTests();

void ForEachTest(absl::FunctionRef<void(FuzzTest&)> func);

using SetUpTearDownTestSuiteFunction = void (*)();
Expand Down
5 changes: 5 additions & 0 deletions fuzztest/internal/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ class Runtime {
// and aborts the process. Otherwise, does nothing.
void HandleUnexpectedExit();

bool init_fuzztest_called() const { return init_fuzztest_called_; }
void SetInitFuzzTestCalled(bool b) { init_fuzztest_called_ = b; }

class Watchdog;
// Returns a watchdog that periodically checks the time and memory limits in a
// separate thread. The watchdog handles the logic of starting and joining the
Expand Down Expand Up @@ -289,6 +292,8 @@ class Runtime {
std::vector<CrashMetadataListener> crash_metadata_listeners_;
// In case of a crash, contains the crash type.
std::optional<std::string> crash_type_;

bool init_fuzztest_called_ = false;
};

struct ReproducerOutputLocation {
Expand Down
Loading