Skip to content

Commit

Permalink
Add a version of ReadFilesFromDirectory that allows filtering by file…
Browse files Browse the repository at this point in the history
… name.

Based on #1520.

PiperOrigin-RevId: 719678199
  • Loading branch information
lszekeres authored and copybara-github committed Jan 30, 2025
1 parent f96372c commit d31aba9
Show file tree
Hide file tree
Showing 17 changed files with 284 additions and 162 deletions.
11 changes: 11 additions & 0 deletions common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,17 @@ cc_library(
],
)

cc_library(
name = "temp_dir",
srcs = ["temp_dir.cc"],
hdrs = ["temp_dir.h"],
deps = [
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
],
)

### Tests

cc_test(
Expand Down
12 changes: 12 additions & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ fuzztest_cc_library(
TESTONLY
)

fuzztest_cc_library(
NAME
temp_dir
HDRS
"temp_dir.h"
SRCS
"temp_dir.cc"
DEPS
absl::check
TESTONLY
)

### Tests

fuzztest_cc_test(
Expand Down
29 changes: 29 additions & 0 deletions common/temp_dir.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "./common/temp_dir.h"

#include <filesystem> // NOLINT
#include <system_error>

#include "absl/log/check.h"

namespace fuzztest::internal {

namespace fs = std::filesystem;

TempDir::TempDir() {
std::error_code error;
const fs::path path_template =
std::filesystem::temp_directory_path(error) / "temp_dir_XXXXXX";
CHECK(!error) << "Failed to get the root temp directory path: "
<< error.message();
path_ = mkdtemp(path_template.string().data());
CHECK(std::filesystem::is_directory(path_));
}

TempDir::~TempDir() {
std::error_code error;
std::filesystem::remove_all(path_, error);
CHECK(!error) << "Unable to clean up temporary dir " << path_ << ": "
<< error.message();
}

} // namespace fuzztest::internal
26 changes: 26 additions & 0 deletions common/temp_dir.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef FUZZTEST_COMMON_TEMP_DIR_H_
#define FUZZTEST_COMMON_TEMP_DIR_H_

#include <filesystem> // NOLINT

namespace fuzztest::internal {

// A helper class for creating a temporary directory. Removes the directory
// when it goes out of scope.
class TempDir {
public:
explicit TempDir();
~TempDir();

TempDir(const TempDir& other) = delete;
TempDir& operator=(const TempDir& other) = delete;

const std::filesystem::path& path() const { return path_; }

private:
std::filesystem::path path_;
};

} // namespace fuzztest::internal

#endif // FUZZTEST_COMMON_TEMP_DIR_H_
2 changes: 2 additions & 0 deletions e2e_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ cc_test(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@com_google_fuzztest//centipede:weak_sancov_stubs",
"@com_google_fuzztest//common:temp_dir",
"@com_google_fuzztest//domain_tests:domain_testing",
"@com_google_fuzztest//fuzztest:io",
"@com_google_fuzztest//fuzztest:logging",
Expand Down Expand Up @@ -124,6 +125,7 @@ cc_test(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_fuzztest//centipede:weak_sancov_stubs",
"@com_google_fuzztest//common:temp_dir",
"@com_google_fuzztest//fuzztest:io",
"@com_google_fuzztest//fuzztest:logging",
"@com_google_fuzztest//fuzztest:subprocess",
Expand Down
1 change: 1 addition & 0 deletions e2e_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target_link_libraries(
fuzztest_subprocess
fuzztest_test_binary_util
fuzztest_type_support
fuzztest_temp_dir
re2
absl::flat_hash_map
absl::strings
Expand Down
9 changes: 5 additions & 4 deletions e2e_tests/corpus_database_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "./common/temp_dir.h"
#include "./e2e_tests/test_binary_util.h"
#include "./fuzztest/internal/io.h"
#include "./fuzztest/internal/logging.h"
Expand Down Expand Up @@ -97,7 +98,7 @@ class UpdateCorpusDatabaseTest

static std::string GetCorpusDatabasePath() {
RunUpdateCorpusDatabase();
return std::filesystem::path(temp_dir_->dirname()) / "corpus_database";
return temp_dir_->path() / "corpus_database";
}

static absl::string_view GetUpdateCorpusDatabaseStdErr() {
Expand Down Expand Up @@ -170,7 +171,7 @@ TEST_P(UpdateCorpusDatabaseTest, ResumedFuzzTestRunsForRemainingTime) {
// 1st run that gets interrupted.
RunOptions fst_run_options;
fst_run_options.fuzztest_flags = {
{"corpus_database", corpus_database.dirname()},
{"corpus_database", corpus_database.path()},
{"fuzz_for", "300s"},
};
fst_run_options.timeout = absl::Seconds(10);
Expand All @@ -179,14 +180,14 @@ TEST_P(UpdateCorpusDatabaseTest, ResumedFuzzTestRunsForRemainingTime) {

// Adjust the fuzzing time so that only 1s remains.
const absl::StatusOr<std::string> fuzzing_time_file =
FindFile(corpus_database.dirname(), "fuzzing_time");
FindFile(corpus_database.path().c_str(), "fuzzing_time");
ASSERT_TRUE(fuzzing_time_file.ok()) << fst_std_err;
ASSERT_TRUE(WriteFile(*fuzzing_time_file, "299s"));

// 2nd run that resumes the fuzzing.
RunOptions snd_run_options;
snd_run_options.fuzztest_flags = {
{"corpus_database", corpus_database.dirname()},
{"corpus_database", corpus_database.path()},
{"fuzz_for", "300s"},
};
snd_run_options.timeout = absl::Seconds(10);
Expand Down
65 changes: 31 additions & 34 deletions e2e_tests/functional_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <cstdio>
#include <cstdlib>
#include <filesystem> // NOLINT
#include <optional>
#include <string>
#include <system_error>
#include <tuple>
Expand All @@ -34,6 +33,7 @@
#include "absl/strings/substitute.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "./common/temp_dir.h"
#include "./domain_tests/domain_testing.h"
#include "./e2e_tests/test_binary_util.h"
#include "./fuzztest/internal/io.h"
Expand Down Expand Up @@ -789,11 +789,11 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, ReproducerIsDumpedWhenEnvVarIsSet) {

auto [status, std_out, std_err] =
RunWith({{"fuzz", "MySuite.StringFast"}},
{{"FUZZTEST_REPRODUCERS_OUT_DIR", out_dir.dirname()}});
{{"FUZZTEST_REPRODUCERS_OUT_DIR", out_dir.path()}});
EXPECT_THAT(std_err, HasSubstr("argument 0: \"Fuzz"));
ExpectTargetAbort(status, std_err);

auto replay_files = ReadFileOrDirectory(out_dir.dirname());
auto replay_files = ReadFileOrDirectory(out_dir.path().c_str());
ASSERT_EQ(replay_files.size(), 1) << std_err;
auto parsed = IRObject::FromString(replay_files[0].data);
ASSERT_TRUE(parsed) << std_err;
Expand All @@ -815,9 +815,9 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, SavesCorpusWhenEnvVarIsSet) {
// find the crash without saving some corpus.
auto [status, std_out, std_err] =
RunWith({{"fuzz", "MySuite.String"}},
{{"FUZZTEST_TESTSUITE_OUT_DIR", out_dir.dirname()}});
{{"FUZZTEST_TESTSUITE_OUT_DIR", out_dir.path()}});

auto corpus_files = ReadFileOrDirectory(out_dir.dirname());
auto corpus_files = ReadFileOrDirectory(out_dir.path().c_str());
EXPECT_THAT(corpus_files, Not(IsEmpty())) << std_err;
}

Expand Down Expand Up @@ -857,14 +857,14 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, RestoresCorpusWhenEnvVarIsSet) {
// find the crash without saving some corpus.
auto [producer_status, producer_std_out, producer_std_err] =
RunWith({{"fuzz", "MySuite.String"}, {"fuzz_for", "10s"}},
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}});
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.path()}});

auto corpus_files = ReadFileOrDirectory(corpus_dir.dirname());
auto corpus_files = ReadFileOrDirectory(corpus_dir.path().c_str());
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()},
{{"FUZZTEST_TESTSUITE_IN_DIR", corpus_dir.path()},
{"FUZZTEST_MAX_FUZZING_RUNS", "0"}});
ExpectCorpusInputMessageInLogs(consumer_std_err, corpus_files.size());
}
Expand All @@ -876,9 +876,9 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizesCorpusWhenEnvVarIsSet) {
// find the crash without saving some corpus.
auto [producer_status, producer_std_out, producer_std_err] =
RunWith({{"fuzz", "MySuite.String"}, {"fuzz_for", "10s"}},
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}});
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.path()}});

auto corpus_files = ReadFileOrDirectory(corpus_dir.dirname());
auto corpus_files = ReadFileOrDirectory(corpus_dir.path().c_str());
ASSERT_THAT(corpus_files, Not(IsEmpty())) << producer_std_err;
std::vector<std::string> corpus_data;
for (const FilePathAndData& corpus_file : corpus_files) {
Expand All @@ -887,11 +887,11 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizesCorpusWhenEnvVarIsSet) {

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()}});
{{"FUZZTEST_MINIMIZE_TESTSUITE_DIR", corpus_dir.path()},
{"FUZZTEST_TESTSUITE_OUT_DIR", minimized_corpus_dir.path()}});

auto minimized_corpus_files =
ReadFileOrDirectory(minimized_corpus_dir.dirname());
ReadFileOrDirectory(minimized_corpus_dir.path().c_str());
EXPECT_THAT(minimized_corpus_files,
AllOf(Not(IsEmpty()), SizeIs(Le(corpus_files.size()))))
<< minimizer_std_err;
Expand All @@ -916,21 +916,21 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizesDuplicatedCorpus) {
// find the crash without saving some corpus.
auto [producer_status, producer_std_out, producer_std_err] =
RunWith({{"fuzz", "MySuite.String"}, {"fuzz_for", "10s"}},
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}});
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.path()}});

auto corpus_files = ReadFileOrDirectory(corpus_dir.dirname());
auto corpus_files = ReadFileOrDirectory(corpus_dir.path().c_str());
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()}});
{{"FUZZTEST_MINIMIZE_TESTSUITE_DIR", corpus_dir.path()},
{"FUZZTEST_TESTSUITE_OUT_DIR", minimized_corpus_dir.path()}});

auto minimized_corpus_files =
ReadFileOrDirectory(minimized_corpus_dir.dirname());
ReadFileOrDirectory(minimized_corpus_dir.path().c_str());
EXPECT_THAT(minimized_corpus_files,
AllOf(Not(IsEmpty()), SizeIs(Le(corpus_files.size()))))
<< minimizer_std_err;
Expand All @@ -948,7 +948,7 @@ class ReplayFile {
public:
template <typename T>
ReplayFile(std::in_place_t, const T& corpus) {
filename_ = absl::StrCat(dir_.dirname(), "/replay_file");
filename_ = dir_.path() / "replay_file";
WriteFile(filename_, internal::IRObject::FromCorpus(corpus).ToString());
}

Expand Down Expand Up @@ -996,11 +996,11 @@ TEST_F(FuzzingModeCommandLineInterfaceTest,

auto [status, std_out, std_err] =
RunWith({{"fuzz", "MySuite.WithDomainClass"}},
{{"FUZZTEST_REPRODUCERS_OUT_DIR", out_dir.dirname()}});
{{"FUZZTEST_REPRODUCERS_OUT_DIR", out_dir.path()}});
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());
auto replay_files = ReadFileOrDirectory(out_dir.path().c_str());
ASSERT_EQ(replay_files.size(), 1) << std_err;
auto parsed = IRObject::FromString(replay_files[0].data);
ASSERT_TRUE(parsed) << std_err;
Expand Down Expand Up @@ -1033,14 +1033,14 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, MinimizerFindsSmallerInput) {
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();
env["FUZZTEST_REPRODUCERS_OUT_DIR"] = out_dir.path();

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());
auto replay_files = ReadFileOrDirectory(out_dir.path().c_str());
ASSERT_EQ(replay_files.size(), 1) << std_err;
auto parsed = IRObject::FromString(replay_files[0].data);
ASSERT_TRUE(parsed) << std_err;
Expand Down Expand Up @@ -1205,13 +1205,13 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, CorpusDoesNotContainSkippedInputs) {
// find the crash without saving some corpus.
auto [producer_status, producer_std_out, producer_std_err] =
RunWith({{"fuzz", "MySuite.SkipInputs"}, {"fuzz_for", "10s"}},
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.dirname()}});
{{"FUZZTEST_TESTSUITE_OUT_DIR", corpus_dir.path()}});

ASSERT_THAT(producer_std_err, HasSubstr("Skipped input"));

auto [replayer_status, replayer_std_out, replayer_std_err] =
RunWith({{"fuzz", "MySuite.SkipInputs"}},
{{"FUZZTEST_REPLAY", corpus_dir.dirname()}});
{{"FUZZTEST_REPLAY", corpus_dir.path()}});

EXPECT_THAT(replayer_std_err, Not(HasSubstr("Skipped input")));
}
Expand All @@ -1224,7 +1224,7 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, UsesCentipedeBinaryWhenEnvIsSet) {
auto [status, std_out, std_err] = RunWith(
{
{"fuzz_for", "1s"},
{"corpus_database", temp_dir.dirname()},
{"corpus_database", temp_dir.path()},
},
{{"FUZZTEST_CENTIPEDE_BINARY", CentipedePath()}},
/*timeout=*/absl::Minutes(1), "testdata/unit_test_and_fuzz_tests");
Expand Down Expand Up @@ -1287,7 +1287,7 @@ class FuzzingModeFixtureTest
run_options.flags = {
{"print_runner_log", "true"},
{"exit_on_crash", "true"},
{"workdir", workdir.dirname()},
{"workdir", workdir.path()},
{"binary", absl::StrCat(BinaryPath(kDefaultTargetBinary), " ",
CreateFuzzTestFlag("fuzz", test_name))},
{"num_runs", absl::StrCat(iterations)}};
Expand Down Expand Up @@ -1426,7 +1426,7 @@ class FuzzingModeCrashFindingTest
{"exit_on_crash", "true"},
{"timeout_per_input", "0"},
{"stop_at", absl::StrCat(absl::Now() + timeout)},
{"workdir", workdir.dirname()},
{"workdir", workdir.path()},
{"binary", absl::StrCat(BinaryPath(target_binary), " ",
CreateFuzzTestFlag("fuzz", test_name))}};
run_options.env = std::move(env);
Expand Down Expand Up @@ -1790,8 +1790,7 @@ TEST_P(FuzzingModeCrashFindingTest, InputsAreSkippedWhenRequestedInTests) {

TEST_P(FuzzingModeCrashFindingTest, AsanCrashMetadataIsDumpedIfEnvVarIsSet) {
TempDir out_dir;
const std::string crash_metadata_path =
std::filesystem::path(out_dir.dirname()) / "crash_metadata";
const std::string crash_metadata_path = out_dir.path() / "crash_metadata";
auto [status, std_out, std_err] =
Run("MySuite.BufferOverreadWithString", kDefaultTargetBinary,
{{"FUZZTEST_CRASH_METADATA_PATH", crash_metadata_path}});
Expand All @@ -1802,8 +1801,7 @@ TEST_P(FuzzingModeCrashFindingTest, AsanCrashMetadataIsDumpedIfEnvVarIsSet) {

TEST_P(FuzzingModeCrashFindingTest, SignalCrashMetadataIsDumpedIfEnvVarIsSet) {
TempDir out_dir;
const std::string crash_metadata_path =
std::filesystem::path(out_dir.dirname()) / "crash_metadata";
const std::string crash_metadata_path = out_dir.path() / "crash_metadata";
auto [status, std_out, std_err] =
Run("MySuite.Aborts", kDefaultTargetBinary,
{{"FUZZTEST_CRASH_METADATA_PATH", crash_metadata_path}});
Expand All @@ -1813,8 +1811,7 @@ TEST_P(FuzzingModeCrashFindingTest, SignalCrashMetadataIsDumpedIfEnvVarIsSet) {

TEST_P(FuzzingModeCrashFindingTest, GTestCrashMetadataIsDumpedIfEnvVarIsSet) {
TempDir out_dir;
const std::string crash_metadata_path =
std::filesystem::path(out_dir.dirname()) / "crash_metadata";
const std::string crash_metadata_path = out_dir.path() / "crash_metadata";
auto [status, std_out, std_err] =
Run("MySuite.GoogleTestExpect", kDefaultTargetBinary,
{{"FUZZTEST_CRASH_METADATA_PATH", crash_metadata_path}});
Expand Down
Loading

0 comments on commit d31aba9

Please sign in to comment.