osrm-backend/third_party/unordered_dense/test/fuzz/run.cpp

134 lines
3.9 KiB
C++

#include <fuzz/run.h>
#include <app/doctest.h>
#include <fmt/format.h>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
namespace fuzz::detail {
namespace {
[[nodiscard]] constexpr auto is_alpha(char c) -> bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
[[nodiscard]] constexpr auto is_digit(char c) -> bool {
return c >= '0' && c <= '9';
}
[[nodiscard]] constexpr auto is_alnum(char c) -> bool {
return is_alpha(c) || is_digit(c);
}
[[nodiscard]] constexpr auto contains(std::string_view haystack, char needle) -> bool {
return std::string_view::npos != haystack.find_first_of(needle);
}
[[nodiscard]] constexpr auto is_valid_filename(std::string_view name) -> bool {
using namespace std::literals;
for (auto c : name) {
if (!is_alnum(c) && !contains("_-+", c)) {
return false;
}
}
return true;
}
auto env(char const* varname) -> std::optional<std::string> {
#ifdef _MSC_VER
char* pValue = nullptr;
size_t len = 0;
errno_t err = _dupenv_s(&pValue, &len, varname);
if (err || nullptr == pValue) {
return {};
}
auto str = std::string(pValue);
free(pValue);
return str;
#else
char const* val = std::getenv(varname); // NOLINT(concurrency-mt-unsafe,clang-analyzer-cplusplus.StringChecker)
if (nullptr == val) {
return {};
}
return val;
#endif
}
[[nodiscard]] auto read_file(std::filesystem::path const& p) -> std::optional<std::string> {
auto f = std::ifstream(p);
if (!f) {
return {};
}
auto content = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
if (f.bad()) {
return {};
}
return content;
}
[[nodiscard]] auto find_fuzz_corpus_base_dir() -> std::optional<std::filesystem::path> {
auto corpus_base_dir = env("FUZZ_CORPUS_BASE_DIR");
if (corpus_base_dir) {
return corpus_base_dir.value();
}
auto p = std::filesystem::current_path();
while (true) {
auto const filename = p / ".fuzz-corpus-base-dir";
// INFO(fmt::format("trying '{}'", filename.string()));
if (std::filesystem::exists(filename)) {
if (auto file_content = read_file(p / ".fuzz-corpus-base-dir"); file_content) {
auto f = std::filesystem::path(file_content.value()).make_preferred();
// INFO(fmt::format("got it! p='{}, f='{}', p/f='{}'\n", p.string(), f.string(), (p / f).string()));
return p / f;
}
// could not read file
throw std::runtime_error(fmt::format("could not read '{}'", filename.string()));
}
if (p == p.root_path()) {
return {};
}
p = p.parent_path();
}
}
} // namespace
void evaluate_corpus(std::function<void(provider)> const& op) {
if (!is_valid_filename(doctest::current_test_name())) {
throw std::runtime_error("test case name needs to be a valid filename. only [a-zA-Z0-9_-+] are allowed");
}
// 2 ways
auto corpus_base_dir = find_fuzz_corpus_base_dir();
if (!corpus_base_dir) {
throw std::runtime_error("could not find corpus base dir :-(");
}
auto path = std::filesystem::path(corpus_base_dir.value()) / doctest::current_test_name();
INFO("path=\"" << path.string() << "\"");
auto num_files = size_t();
for (auto const& dir_entry : std::filesystem::directory_iterator(path)) {
++num_files;
auto const& test_file = dir_entry.path();
CAPTURE(test_file);
auto f = std::ifstream(test_file);
auto content = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
op(provider(content.data(), content.size()));
}
REQUIRE(num_files > 1);
}
} // namespace fuzz::detail