2025-11-04 18:50:59 +03:30
|
|
|
export module test.registry;
|
|
|
|
|
|
2026-02-02 13:56:25 +03:30
|
|
|
import logger;
|
2026-01-20 09:58:35 +03:30
|
|
|
import preliminary;
|
2025-11-04 18:50:59 +03:30
|
|
|
import test.expects;
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////
|
|
|
|
|
// ----------* INTERFACE *--------- //
|
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
namespace lt::test {
|
|
|
|
|
|
|
|
|
|
export class Registry
|
|
|
|
|
{
|
|
|
|
|
public:
|
2026-01-20 09:58:35 +03:30
|
|
|
enum class ExecutionPolicy : u8
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
normal,
|
|
|
|
|
stats,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Options
|
|
|
|
|
{
|
|
|
|
|
bool stop_on_fail = false;
|
|
|
|
|
|
|
|
|
|
ExecutionPolicy execution_policy = ExecutionPolicy::normal;
|
|
|
|
|
|
|
|
|
|
std::string suite_regex;
|
|
|
|
|
|
|
|
|
|
std::string case_regex;
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
using FuzzFunction = i32 (*)(const u8 *, size_t);
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
using SuiteFunction = void (*)();
|
|
|
|
|
|
|
|
|
|
static void register_suite(SuiteFunction suite);
|
|
|
|
|
|
|
|
|
|
static void register_fuzz_harness(FuzzFunction suite);
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
static auto run_all(Options options) -> i32;
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
static auto process_fuzz_input(const u8 *data, size_t size) -> i32;
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
static void set_last_suite_name(const char *name);
|
|
|
|
|
|
|
|
|
|
static void increment_total_suite_count();
|
|
|
|
|
|
|
|
|
|
static void increment_matched_suite_count();
|
|
|
|
|
|
|
|
|
|
static void increment_skipped_suite_count();
|
|
|
|
|
|
|
|
|
|
static void increment_passed_suite_count();
|
|
|
|
|
|
|
|
|
|
static void increment_failed_suite_count();
|
|
|
|
|
|
|
|
|
|
static void increment_total_case_count();
|
|
|
|
|
|
|
|
|
|
static void increment_matched_case_count();
|
|
|
|
|
|
|
|
|
|
static void increment_skipped_case_count();
|
|
|
|
|
|
|
|
|
|
static void increment_passed_case_count();
|
|
|
|
|
|
|
|
|
|
static void increment_failed_case_count();
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static auto should_return_on_failure() -> bool;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static auto get_options() -> const Options &;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static auto get_case_regex() -> const std::regex &;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Registry();
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] static auto instance() -> Registry &;
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
auto run_all_impl() -> i32;
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
void print_options();
|
|
|
|
|
|
|
|
|
|
Options m_options {};
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<SuiteFunction, const char *>> m_suites;
|
|
|
|
|
|
|
|
|
|
FuzzFunction m_fuzz_harness {};
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_total_case_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_passed_case_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_failed_case_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_matched_case_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_skipped_case_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_total_suite_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_passed_suite_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_failed_suite_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_matched_suite_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
i32 m_skipped_suite_count {};
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
std::regex m_case_regex;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace lt::test
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////
|
|
|
|
|
// -------* IMPLEMENTATION *------- //
|
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
module :private;
|
2025-11-16 07:05:55 +03:30
|
|
|
namespace lt::test {
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
/* static */ void Registry::register_suite(SuiteFunction suite)
|
|
|
|
|
{
|
|
|
|
|
instance().m_suites.emplace_back(suite, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::register_fuzz_harness(FuzzFunction suite)
|
|
|
|
|
{
|
|
|
|
|
if (instance().m_fuzz_harness)
|
|
|
|
|
{
|
|
|
|
|
throw std::logic_error {
|
|
|
|
|
"Attempting to register fuzz harness while one is already registered",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instance().m_fuzz_harness = suite;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
/* static */ auto Registry::run_all(Options options) -> i32
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
instance().m_options = std::move(options);
|
|
|
|
|
return instance().run_all_impl();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
/* static */ auto Registry::process_fuzz_input(const u8 *data, size_t size) -> i32
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
if (!instance().m_fuzz_harness)
|
|
|
|
|
{
|
|
|
|
|
throw std::logic_error {
|
|
|
|
|
"Attempting to process fuzz input with no active harness",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return instance().m_fuzz_harness(data, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::set_last_suite_name(const char *name)
|
|
|
|
|
{
|
|
|
|
|
instance().m_suites.back().second = name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_total_suite_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_total_suite_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_matched_suite_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_matched_suite_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_skipped_suite_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_skipped_suite_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_passed_suite_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_passed_suite_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_failed_suite_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_failed_suite_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_total_case_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_total_case_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_matched_case_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_matched_case_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_skipped_case_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_skipped_case_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_passed_case_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_passed_case_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */ void Registry::increment_failed_case_count()
|
|
|
|
|
{
|
|
|
|
|
++instance().m_failed_case_count;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 13:56:25 +03:30
|
|
|
/* static */ [[nodiscard]] auto Registry::should_return_on_failure() -> bool
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
return instance().m_options.stop_on_fail;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 13:56:25 +03:30
|
|
|
/* static */ [[nodiscard]] auto Registry::get_options() -> const Options &
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
return instance().m_options;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 13:56:25 +03:30
|
|
|
/* static */ [[nodiscard]] auto Registry::get_case_regex() -> const std::regex &
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
return instance().m_case_regex;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 09:58:35 +03:30
|
|
|
auto Registry::run_all_impl() -> i32
|
2025-11-04 18:50:59 +03:30
|
|
|
{
|
|
|
|
|
print_options();
|
|
|
|
|
m_case_regex = std::regex(m_options.case_regex);
|
|
|
|
|
|
|
|
|
|
const auto regex = std::regex(m_options.suite_regex);
|
|
|
|
|
for (auto &[suite, name] : m_suites)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (std::regex_search(name, regex))
|
|
|
|
|
{
|
2026-02-02 13:56:25 +03:30
|
|
|
auto padding_left = std::string {};
|
|
|
|
|
padding_left.resize((79 - std::strlen(name)) / 2u - 1u);
|
|
|
|
|
for (auto &ch : padding_left)
|
|
|
|
|
{
|
|
|
|
|
ch = '-';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto padding_right = std::string {};
|
|
|
|
|
padding_right.resize((79 - std::strlen(name)) / 2u);
|
|
|
|
|
if (std::strlen(name) % 2 == 0)
|
|
|
|
|
{
|
|
|
|
|
padding_right.resize(padding_right.size() + 1);
|
|
|
|
|
}
|
|
|
|
|
for (auto &ch : padding_right)
|
|
|
|
|
{
|
|
|
|
|
ch = '-';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log::test(
|
|
|
|
|
"\033[1;33m*{}{}{}-*\033[0m",
|
|
|
|
|
std::string { padding_left },
|
|
|
|
|
std::string_view { name },
|
|
|
|
|
std::string { padding_right }
|
|
|
|
|
);
|
2025-11-04 18:50:59 +03:30
|
|
|
suite();
|
|
|
|
|
increment_matched_suite_count();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
increment_skipped_suite_count();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
increment_total_suite_count();
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception &exp)
|
|
|
|
|
{
|
|
|
|
|
if (m_options.stop_on_fail)
|
|
|
|
|
{
|
2026-02-02 13:56:25 +03:30
|
|
|
log::info("Quitting due to options.stop_on_fail == true");
|
2025-11-04 18:50:59 +03:30
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 13:56:25 +03:30
|
|
|
log::test("Uncaught exception when running suite:");
|
|
|
|
|
log::test("\twhat: {}", exp.what());
|
2025-11-04 18:50:59 +03:30
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (m_options.execution_policy)
|
|
|
|
|
{
|
|
|
|
|
case ExecutionPolicy::normal:
|
|
|
|
|
{
|
2026-02-02 13:56:25 +03:30
|
|
|
// log::test("[-------STATS------]");
|
|
|
|
|
//
|
|
|
|
|
// log::test("suites:");
|
|
|
|
|
// log::test("\ttotal: {}", (i32)m_total_suite_count);
|
|
|
|
|
// log::test("\tpassed: {}", (i32)m_passed_suite_count);
|
|
|
|
|
// log::test("\tfailed: {}", (i32)m_failed_suite_count);
|
|
|
|
|
// log::test("\tmatched: {}", (i32)m_matched_suite_count);
|
|
|
|
|
// log::test("\tskipped: {}", (i32)m_skipped_suite_count);
|
|
|
|
|
//
|
|
|
|
|
// log::test("tests:");
|
|
|
|
|
// log::test("\ttotal: {}", (i32)m_total_case_count);
|
|
|
|
|
// log::test("\tpassed: {}", (i32)m_passed_case_count);
|
|
|
|
|
// log::test("\tfailed: {}", (i32)m_failed_case_count);
|
|
|
|
|
// log::test("\tmatched: {}", (i32)m_matched_case_count);
|
|
|
|
|
// log::test("\tskipped: {}", (i32)m_skipped_case_count);
|
|
|
|
|
|
|
|
|
|
// log::test("________________________________________________________________");
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
return m_failed_case_count;
|
|
|
|
|
}
|
|
|
|
|
case ExecutionPolicy::stats:
|
|
|
|
|
{
|
2026-02-02 13:56:25 +03:30
|
|
|
log::test("[-------STATS------]");
|
|
|
|
|
log::test("Total suite count: {}", (i32)m_total_suite_count);
|
|
|
|
|
log::test("Total test count: {}", (i32)m_total_case_count);
|
|
|
|
|
log::test("________________________________________________________________");
|
2025-11-04 18:50:59 +03:30
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-15 20:59:52 +03:30
|
|
|
|
|
|
|
|
std::unreachable();
|
2025-11-04 18:50:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Registry::print_options()
|
|
|
|
|
{
|
2026-02-02 13:56:25 +03:30
|
|
|
// log::info("stop-on-failure: {}", static_cast<bool>(m_options.stop_on_fail));
|
2025-11-04 18:50:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Registry::Registry()
|
|
|
|
|
{
|
2026-02-02 13:56:25 +03:30
|
|
|
// log::info("________________________________________________________________");
|
2025-11-04 18:50:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] /* static */ auto Registry::instance() -> Registry &
|
|
|
|
|
{
|
|
|
|
|
static auto registry = Registry {};
|
|
|
|
|
return registry;
|
|
|
|
|
}
|
2025-11-16 07:05:55 +03:30
|
|
|
|
|
|
|
|
} // namespace lt::test
|