Files
rapidyaml/test/test_fuzz/test_fuzz_common.hpp
2025-12-28 19:17:22 +00:00

168 lines
5.3 KiB
C++

#pragma once
#ifndef TEST_FUZZ_COMMON_H
#define TEST_FUZZ_COMMON_H
#ifdef RYML_SINGLE_HEADER
#include <ryml_all.hpp>
#else
#include <c4/yml/std/std.hpp>
#include <c4/yml/parse.hpp>
#include <c4/yml/error.def.hpp>
#include <c4/yml/emit.hpp>
#include <c4/yml/event_handler_tree.hpp>
#include <c4/yml/parse_engine.def.hpp>
#endif
#include <testsuite/testsuite_events.hpp>
#include <c4/yml/extra/event_handler_ints.hpp>
#include <c4/yml/extra/event_handler_testsuite.hpp>
#include <cstdio>
#ifdef C4_EXCEPTIONS
#include <stdexcept>
#else
#include <csetjmp>
std::jmp_buf jmp_env = {};
c4::csubstr jmp_msg = {};
#endif
#ifdef RYML_DBG
#define _if_dbg(...) __VA_ARGS__
bool report_errors = true;
#else
#define _if_dbg(...)
bool report_errors = false;
#endif
// watchout: VS2022 requires C4_NORETURN to come before inline
[[noreturn]] void throwerr(c4::csubstr msg)
{
C4_IF_EXCEPTIONS(
throw std::runtime_error({msg.str, msg.len});
,
jmp_msg.assign(msg.str, msg.len);
std::longjmp(jmp_env, 1);
);
C4_UNREACHABLE_AFTER_ERR();
}
void dump2stderr(c4::csubstr s)
{
if(s.len)
{
fwrite(s.str, 1, s.len, stderr);
fflush(stderr);
}
}
inline c4::yml::Callbacks create_custom_callbacks()
{
c4::set_error_flags(c4::ON_ERROR_CALLBACK);
c4::set_error_callback([](const char *msg, size_t msg_len){
throwerr(c4::csubstr{msg, msg_len});
});
return c4::yml::Callbacks{}
.set_error_basic([](c4::csubstr msg, c4::yml::ErrorDataBasic const& errdata, void *){
if(report_errors)
c4::yml::err_basic_format(dump2stderr, msg, errdata);
throwerr(msg);
})
.set_error_parse([](c4::csubstr msg, c4::yml::ErrorDataParse const& errdata, void *){
if(report_errors)
c4::yml::err_parse_format(dump2stderr, msg, errdata);
throwerr(msg);
})
.set_error_visit([](c4::csubstr msg, c4::yml::ErrorDataVisit const& errdata, void *){
if(report_errors)
c4::yml::err_visit_format(dump2stderr, msg, errdata);
throwerr(msg);
});
}
namespace c4 {
namespace yml {
inline int fuzztest_parse_emit(uint32_t case_number, csubstr src)
{
C4_UNUSED(case_number);
set_callbacks(create_custom_callbacks());
Tree tree(create_custom_callbacks());
bool parse_success = false;
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
{
_RYML_ASSERT_BASIC(tree.empty());
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL));
parse_in_arena(src, &tree);
parse_success = true;
_if_dbg(print_tree("parsed tree", tree));
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL));
std::string dst = emitrs_yaml<std::string>(tree);
_if_dbg(_dbg_printf("emitted[{}]: [{}]~~~\n{}\n~~~\n", case_number, dst.size(), to_csubstr(dst)); fflush(NULL));
C4_DONT_OPTIMIZE(dst);
C4_DONT_OPTIMIZE(parse_success);
}
C4_IF_EXCEPTIONS_(catch(std::exception const&), else)
{
// if an exception leaks from here, it is likely because of a greedy noexcept
_if_dbg(if(parse_success) print_tree("parsed tree", tree));
return 1;
}
return 0;
}
inline int fuzztest_yaml_events(uint32_t case_number, csubstr src)
{
C4_UNUSED(case_number);
set_callbacks(create_custom_callbacks());
extra::EventHandlerTestSuite::EventSink sink = {};
extra::EventHandlerTestSuite handler(&sink, create_custom_callbacks());
ParseEngine<extra::EventHandlerTestSuite> parser(&handler);
std::string str(src.begin(), src.end());
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
{
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL));
parser.parse_in_place_ev("input", c4::to_substr(str));
_if_dbg(_dbg_printf("evts[{}]: ~~~\n{}\n~~~\n", case_number, sink); fflush(NULL));
C4_DONT_OPTIMIZE(sink);
}
C4_IF_EXCEPTIONS_(catch(std::exception const&), else)
{
// if an exception leaks from here, it is likely because of a greedy noexcept
_if_dbg(fprintf(stdout, "err\n"); fflush(NULL));
return 1;
}
return 0;
}
inline int fuzztest_yaml_events_ints(uint32_t case_number, csubstr src)
{
C4_UNUSED(case_number);
set_callbacks(create_custom_callbacks());
using Handler = extra::EventHandlerInts;
Handler handler{};
ParseEngine<extra::EventHandlerInts> parser(&handler);
std::string str(src.begin(), src.end());
std::vector<char> arena(str.size());
std::vector<Handler::value_type> event_ints;
event_ints.reserve(256);
handler.reset(to_substr(str), to_substr(arena), event_ints.data(), static_cast<Handler::value_type>(event_ints.size()));
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
{
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL));
parser.parse_in_place_ev("input", c4::to_substr(str));
C4_DONT_OPTIMIZE(event_ints);
}
C4_IF_EXCEPTIONS_(catch(std::exception const&), else)
{
// if an exception leaks from here, it is likely because of a greedy noexcept
_if_dbg(fprintf(stdout, "err\n"); fflush(NULL));
return 1;
}
return 0;
}
} // namespace yml
} // namespace c4
#endif /* TEST_FUZZ_COMMON_H */