src/testlib/testlib.cpp
#if defined(TACKLE_TESTLIB) || defined(UNIT_TESTS) || defined(BENCH_TESTS)
#include "testlib.hpp"
#if defined(UTILITY_PLATFORM_WINDOWS)
// windows includes must be ordered here!
#include <windef.h>
#include <winbase.h>
#include <winnt.h>
#include <wincon.h>
#endif
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
# include <fmt/format.h>
#else
# include <tacklelib/utility/utility.hpp>
#endif
#include <cstdarg>
#include <vector>
#include <algorithm>
#define TEST_IMPL_DEFINE_ENV_VAR(class_name, var_name) \
tackle::path_string class_name::UTILITY_PP_CONCAT(s_, var_name); \
bool class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = false; \
bool class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _disabled) = false
#if defined(UTILITY_PLATFORM_WINDOWS)
#define TEST_BASE_INIT_ENV_VAR(class_name, var_name) \
if_break(1) { \
if (!class_name::UTILITY_PP_CONCAT(s_, var_name).empty()) { \
if(utility::is_path_exists(class_name::UTILITY_PP_CONCAT(s_, var_name), true)) { \
class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = true; \
} \
break; \
} \
size_t var_size = 0; \
char buf[4096] = {0}; \
if (!getenv_s(&var_size, buf, UTILITY_CONSTEXPR_SIZE(buf), UTILITY_PP_STRINGIZE(var_name))) { \
class_name::UTILITY_PP_CONCAT(s_, var_name) = buf; \
if (!class_name::UTILITY_PP_CONCAT(s_, var_name).empty() && utility::is_path_exists(class_name::UTILITY_PP_CONCAT(s_, var_name), true)) \
class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = true; \
} \
} (void)0
#elif defined(UTILITY_PLATFORM_POSIX)
#define TEST_BASE_INIT_ENV_VAR(class_name, var_name) \
if_break(1) { \
if (!class_name::UTILITY_PP_CONCAT(s_, var_name).empty()) { \
if(utility::is_path_exists(class_name::UTILITY_PP_CONCAT(s_, var_name), true)) { \
class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = true; \
} \
break; \
} \
const char * var_str = getenv(UTILITY_PP_STRINGIZE(var_name)); \
if (var_str) { \
class_name::UTILITY_PP_CONCAT(s_, var_name) = var_str; \
if (!class_name::UTILITY_PP_CONCAT(s_, var_name).empty() && utility::is_path_exists(class_name::UTILITY_PP_CONCAT(s_, var_name), true)) \
class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = true; \
} \
} (void)0
#else
#error platform is not implemented
#endif
#define TEST_INIT_ENV_VAR(base_name, class_name, var_name, base_var_name, suffix_path) \
if (class_name::UTILITY_PP_CONCAT(s_, var_name).empty()) { \
class_name::UTILITY_PP_CONCAT(s_, var_name) = base_name::UTILITY_PP_CONCAT(s_, base_var_name) / suffix_path; \
if (!utility::is_path_exists(class_name::UTILITY_PP_CONCAT(s_, var_name), true)) { \
utility::create_directory(class_name::UTILITY_PP_CONCAT(s_, var_name), true); \
class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = true; \
} \
else { \
class_name::UTILITY_PP_CONCAT(UTILITY_PP_CONCAT(s_is_, var_name), _exists) = utility::is_directory_path(class_name::UTILITY_PP_CONCAT(s_, var_name), true); \
} \
} else (void)0
extern std::vector<test::TestCase> g_test_cases;
template <typename Function>
struct TestCaseFuncRef
{
using func_t = bool();
TestCaseFuncRef(Function * func_) :
func(func_), refs(0)
{
if (func) {
refs++;
}
}
TestCaseFuncRef(const TestCaseFuncRef &) = default;
Function * func;
int refs; // number of calls
};
template <typename Ret>
FORCE_INLINE bool operator ==(const TestCaseFuncRef<Ret()> & l, const TestCaseFuncRef<Ret()> & r)
{
return l.func == r.func;
}
using TestCaseInitFuncRef = TestCaseFuncRef<bool()>;
using TestCaseUninitFuncRef = TestCaseFuncRef<void()>;
std::vector<TestCaseInitFuncRef> g_test_cases_inits;
std::vector<TestCaseUninitFuncRef> g_test_cases_uninits;
TEST_IMPL_DEFINE_ENV_VAR(TestCaseStaticBase, TESTS_DATA_IN_ROOT);
TEST_IMPL_DEFINE_ENV_VAR(TestCaseStaticBase, TESTS_DATA_OUT_ROOT);
TEST_IMPL_DEFINE_ENV_VAR(TestCaseWithDataReference, TESTS_REF_DIR);
TEST_IMPL_DEFINE_ENV_VAR(TestCaseWithDataGenerator, TESTS_GEN_DIR);
TEST_IMPL_DEFINE_ENV_VAR(TestCaseWithDataOutput, TESTS_OUT_DIR);
bool TestCaseStaticBase::s_enable_all_tests = false;
bool TestCaseStaticBase::s_enable_interactive_tests = false;
bool TestCaseStaticBase::s_enable_only_interactive_tests = false; // overrides all enable_*_tests flags
bool TestCaseStaticBase::s_enable_combinator_tests = false;
namespace test
{
namespace
{
inline const char * _get_test_case_msg_prefix_str(size_t index)
{
static const char * test_case_msg_prefix_str[] = {
"[ SKIP ] ", "[ DEBUG ] ", "[ CAUTION ] ", "[ WARNING ] ", "[ INFO ] ", "[ INFO ] ", "[ WARNING ] "
};
return test_case_msg_prefix_str[index];
}
inline const char * _get_global_init_msg_prefix_str(size_t index)
{
static const char * global_init_msg_prefix_str[] = {
"skip: ", "debug: ", "caution: ", "warning: ", "info: ", "info: ", "warning: "
};
return global_init_msg_prefix_str[index];
}
#if defined(UTILITY_PLATFORM_WINDOWS)
inline WORD _get_log_out_console_attrs(size_t index)
{
static const WORD console_attrs[] = { \
FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN, \
FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE, \
FOREGROUND_INTENSITY | FOREGROUND_RED, \
FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN, \
FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE, \
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, \
FOREGROUND_RED | FOREGROUND_GREEN \
};
return console_attrs[index];
}
#elif defined(UTILITY_PLATFORM_POSIX)
inline const char * _get_console_color_ansi_sequence(size_t index)
{
static const char * console_color_ansi_sequence[] = {
"\033[1;33m", // yellow
"\033[1;35m", // light purple
"\033[1;31m", // light red
"\033[1;33m", // yellow
"\033[1;36m", // light cyan
"\033[0;37m", // gray
}; \
return console_color_ansi_sequence[index];
}
#endif
}
std::string get_test_case_filter_token(const TestCase & test_case)
{
const std::string prefix_str = (test_case.prefix_str ? test_case.prefix_str : "");
const std::string test_case_name = (test_case.test_case_name ? test_case.test_case_name : "");
const std::string test_name = (test_case.test_name ? test_case.test_name : "");
if (test_case.flags & test::TCF_IS_PARAMETERIZED) {
return prefix_str + (!prefix_str.empty() && !test_case_name.empty() ? "/" : "") + test_case_name +
(!test_case_name.empty() && !test_name.empty() ? "/" : "") + (!test_name.empty() ? test_name : "*") + "/*";
}
return prefix_str + (!prefix_str.empty() && !test_case_name.empty() ? "/" : "") + test_case_name +
(!test_case_name.empty() && !test_name.empty() ? "/" : "") + (!test_name.empty() ? test_name : "*");
}
std::string get_test_case_name_token(const TestCase & test_case)
{
const std::string prefix_str = (test_case.prefix_str ? test_case.prefix_str : "");
const std::string test_case_name = (test_case.test_case_name ? test_case.test_case_name : "");
return prefix_str + (!prefix_str.empty() && !test_case_name.empty() ? "/" : "") + test_case_name;
}
std::string get_test_case_name_filter_token(const TestCase & test_case)
{
return get_test_case_name_token(test_case) + ".*";
}
std::string get_test_case_name_filter_token(const testing::TestCase * test_case)
{
const std::string test_cast_name = DEBUG_VERIFY_TRUE(test_case)->name();
return test_cast_name + ".*";
}
void global_preinit(std::string & gtest_exclude_filter)
{
TEST_BASE_INIT_ENV_VAR(TestCaseStaticBase, TESTS_DATA_IN_ROOT);
TEST_BASE_INIT_ENV_VAR(TestCaseStaticBase, TESTS_DATA_OUT_ROOT);
// defaults
if (TestCaseStaticBase::s_TESTS_DATA_IN_ROOT.empty()) {
TestCaseStaticBase::s_TESTS_DATA_IN_ROOT = "./tests_data";
TestCaseStaticBase::s_is_TESTS_DATA_IN_ROOT_exists = true;
}
if (TestCaseStaticBase::s_TESTS_DATA_OUT_ROOT.empty()) {
TestCaseStaticBase::s_TESTS_DATA_OUT_ROOT = "./tests_data_out";
TestCaseStaticBase::s_is_TESTS_DATA_OUT_ROOT_exists = true;
}
TEST_BASE_INIT_ENV_VAR(TestCaseWithDataReference, TESTS_REF_DIR);
TEST_BASE_INIT_ENV_VAR(TestCaseWithDataGenerator, TESTS_GEN_DIR);
TEST_BASE_INIT_ENV_VAR(TestCaseWithDataOutput, TESTS_OUT_DIR);
TEST_INIT_ENV_VAR(TestCaseStaticBase, TestCaseWithDataReference, TESTS_REF_DIR, TESTS_DATA_IN_ROOT, "");
TEST_INIT_ENV_VAR(TestCaseStaticBase, TestCaseWithDataGenerator, TESTS_GEN_DIR, TESTS_DATA_OUT_ROOT, "");
TEST_INIT_ENV_VAR(TestCaseStaticBase, TestCaseWithDataOutput, TESTS_OUT_DIR, TESTS_DATA_OUT_ROOT, "");
// skip tests if conditions has met
bool has_TESTS_REF_DIR_exclude_warning = false;
bool has_TESTS_GEN_DIR_exclude_warning = false;
bool has_TESTS_OUT_DIR_exclude_warning = false;
bool has_interactive_exclude_warning = false;
bool has_only_interactive_tests_warning = false;
bool has_combinator_exclude_warning = false;
std::string gtest_inner_exclude_filter;
// hierarchy: `/test_case/<ref|gen|out>`
for(auto & v : g_test_cases) {
bool do_exclude = false;
const std::string test_case_name_token = get_test_case_name_token(v);
if (v.flags & test::TCF_HAS_DATA_REF) {
if (TestCaseWithDataReference::s_is_TESTS_REF_DIR_disabled || !TestCaseWithDataReference::s_is_TESTS_REF_DIR_exists) {
if (!has_TESTS_REF_DIR_exclude_warning) {
has_TESTS_REF_DIR_exclude_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"tests which required TESTS_REF_DIR (--data_ref_dir) or TESTS_DATA_IN_ROOT (--data_in_root) directory path will be %s.",
TestCaseWithDataReference::s_is_TESTS_REF_DIR_disabled ? "disabled" : "skipped");
}
do_exclude = true;
}
else if (v.data_in_subdir && !std::string(v.data_in_subdir).empty()) { // filter by subdir
// test on existence, disable test case if not found
const tackle::path_string data_in_subdir = TestCaseWithDataReference::s_TESTS_REF_DIR / v.data_in_subdir;
if (!utility::is_path_exists(data_in_subdir, true) || !utility::is_directory_path(data_in_subdir, true)) {
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"tests input data subdirectory is not found: \"%s\".\n", data_in_subdir.c_str());
do_exclude = true;
}
}
} else if ((TestCaseWithDataGenerator::s_is_TESTS_GEN_DIR_disabled || !TestCaseWithDataGenerator::s_is_TESTS_GEN_DIR_exists) && (v.flags & test::TCF_HAS_DATA_GEN)) {
if (!has_TESTS_GEN_DIR_exclude_warning) {
has_TESTS_GEN_DIR_exclude_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"tests which required TESTS_GEN_DIR (--data_gen_dir) or TESTS_DATA_IN_ROOT (--data_in_root) directory path will be %s.",
TestCaseWithDataGenerator::s_is_TESTS_GEN_DIR_disabled ? "disabled" : "skipped");
}
do_exclude = true;
} else if ((TestCaseWithDataOutput::s_is_TESTS_OUT_DIR_disabled || !TestCaseWithDataOutput::s_is_TESTS_OUT_DIR_exists) && (v.flags & test::TCF_HAS_DATA_OUT)) {
if (!has_TESTS_OUT_DIR_exclude_warning) {
has_TESTS_OUT_DIR_exclude_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"tests which required TESTS_OUT_DIR (--data_out_dir) or TESTS_DATA_IN_ROOT (--data_in_root) directory path will be %s.",
TestCaseWithDataOutput::s_is_TESTS_OUT_DIR_disabled ? "disabled" : "skipped");
}
do_exclude = true;
}
if (!do_exclude) {
if (TestCaseStaticBase::s_enable_only_interactive_tests && !(v.flags & test::TCF_IS_INTERACTIVE)) {
if (!has_only_interactive_tests_warning) {
has_only_interactive_tests_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"only interactive tests are enabled, all other tests will be skipped.");
}
do_exclude = true;
}
else if (!TestCaseStaticBase::s_enable_interactive_tests && (v.flags & test::TCF_IS_INTERACTIVE)) {
if (!has_interactive_exclude_warning) {
has_interactive_exclude_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"interactive tests are disabled by default (use `--enable_interactive_tests` to enable interactive tests).");
}
do_exclude = true;
}
else if (!TestCaseStaticBase::s_enable_combinator_tests && (v.flags & test::TCF_IS_COMBINATOR)) {
if (!has_combinator_exclude_warning) {
has_combinator_exclude_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"long and heavy combinator tests are disabled by default (use `--enable_combinator_tests` to enable combinator tests).");
}
do_exclude = true;
}
}
#ifndef UNIT_TESTS_ENABLE_INTERACTIVE_TESTS
if (!do_exclude) {
if (TestCaseStaticBase::s_enable_interactive_tests && (v.flags & test::TCF_IS_INTERACTIVE)) {
if (!has_interactive_exclude_warning) {
has_interactive_exclude_warning = true;
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"UNIT_TESTS_ENABLE_INTERACTIVE_TESTS macro is not defined, interactive tests will be skipped.");
}
do_exclude = true;
}
}
#endif
if (!do_exclude) {
// test case global initialization
if (v.init_func) {
auto test_case_inits_it = std::find(g_test_cases_inits.begin(), g_test_cases_inits.end(), TestCaseInitFuncRef{ v.init_func });
if (test_case_inits_it != g_test_cases_inits.end()) {
test_case_inits_it->refs++;
}
else {
try {
if (v.init_func()) {
g_test_cases_inits.push_back(TestCaseInitFuncRef{ v.init_func });
v.flags |= TCF_PRIVATE_FLAGS_MASK & 0x00020000; // globally initialized
}
else {
do_exclude = true;
v.flags |= TCF_PRIVATE_FLAGS_MASK & 0x00010000; // excluded
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"%s: tests group failed of global initialization (returned).",
test_case_name_token.c_str());
}
}
catch (const std::exception & ex) {
do_exclude = true;
v.flags |= TCF_PRIVATE_FLAGS_MASK & 0x00010000; // excluded
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"%s: tests group failed of global initialization (std exception: \"%s\").",
test_case_name_token.c_str(), ex.what());
}
catch (...) {
do_exclude = true;
v.flags |= TCF_PRIVATE_FLAGS_MASK & 0x00010000; // excluded
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"%s: tests group failed of global initialization (unknown exception).",
test_case_name_token.c_str());
}
}
}
}
if (do_exclude) {
if (!gtest_inner_exclude_filter.empty()) {
gtest_inner_exclude_filter += ":";
}
const std::string test_case_filter_token = get_test_case_filter_token(v);
gtest_inner_exclude_filter += test_case_filter_token;
}
}
if (!gtest_exclude_filter.empty())
gtest_exclude_filter += ":";
if (!gtest_inner_exclude_filter.empty() &&
std::string::npos == gtest_exclude_filter.find("-", 0))
gtest_exclude_filter += "-";
gtest_exclude_filter += gtest_inner_exclude_filter;
}
void global_postinit(std::string & gtest_exclude_filter)
{
bool negated_filter = false;
// reread the filter
std::string gtest_external_filter = ::testing::GTEST_FLAG(filter);
if (!gtest_external_filter.empty() &&
std::string::npos != gtest_external_filter.find("-", 0)) {
negated_filter = true;
}
if (!gtest_exclude_filter.empty() &&
std::string::npos != gtest_exclude_filter.find("-", 0)) {
negated_filter = true;
}
// generate disabled tests filter string for not declared tests
int test_case_index = 0;
const auto * test_case = ::testing::UnitTest::GetInstance()->GetTestCase(test_case_index);
std::string test_case_name_filter_token;
while (test_case) {
bool is_declared = false;
bool is_excluded = false;
const auto built_test_case_name = test_case->name();
int test_case_flags = 0;
for (const auto & v : g_test_cases) {
const std::string test_case_name_token = get_test_case_name_token(v);
if (built_test_case_name == test_case_name_token) {
test_case_flags = v.flags;
if (test_case_flags & 0x00010000) { // is excluded?
is_excluded = true;
}
else {
is_declared = true;
}
break;
}
}
if (!is_declared) {
// generate test case disable filter
if (!gtest_exclude_filter.empty())
gtest_exclude_filter += ":";
if (!negated_filter) {
gtest_exclude_filter += "-";
negated_filter = true;
}
gtest_exclude_filter += get_test_case_name_filter_token(test_case);
if (!is_excluded) {
TEST_LOG_OUT(FROM_GLOBAL_INIT | SKIP,
"%s: tests group built but not declared for execution, will be skipped.",
built_test_case_name);
}
else {
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"%s: tests group built, declared for execution, but will be excluded as not been properly initialized: flags=0x%08X",
built_test_case_name, test_case_flags);
}
}
test_case = ::testing::UnitTest::GetInstance()->GetTestCase(++test_case_index);
}
if (!gtest_exclude_filter.empty()) {
// update the filter
if (!::testing::GTEST_FLAG(filter).empty())
::testing::GTEST_FLAG(filter) += ":";
::testing::GTEST_FLAG(filter) += gtest_exclude_filter;
}
}
void global_uninit()
{
// generate disabled tests filter string for not declared tests
int test_case_index = 0;
const auto * test_case = ::testing::UnitTest::GetInstance()->GetTestCase(test_case_index);
while (test_case) {
const auto built_test_case_name = test_case->name();
for (auto & v : g_test_cases) {
const std::string test_case_name_token = get_test_case_name_token(v);
if (built_test_case_name == test_case_name_token) {
if (!(v.flags & 0x00010000)) {
// test case global uninitialization
if (v.uninit_func) {
auto test_case_uninits_it = std::find(g_test_cases_uninits.begin(), g_test_cases_uninits.end(), TestCaseUninitFuncRef{ v.uninit_func });
if (test_case_uninits_it != g_test_cases_uninits.end()) {
test_case_uninits_it->refs++;
}
else {
try {
v.uninit_func();
g_test_cases_uninits.push_back(TestCaseUninitFuncRef{ v.uninit_func });
v.flags |= TCF_PRIVATE_FLAGS_MASK & 0x00040000; // globally uninitialized
}
catch (const std::exception & ex) {
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"%s: tests group failed of global uninitialization (std exception: \"%s\").",
test_case_name_token.c_str(), ex.what());
}
catch (...) {
TEST_LOG_OUT(FROM_GLOBAL_INIT | WARNING,
"%s: tests group failed of global uninitialization (unknown exception).",
test_case_name_token.c_str());
}
}
}
}
break;
}
}
test_case = ::testing::UnitTest::GetInstance()->GetTestCase(++test_case_index);
}
}
tackle::path_string get_data_in_subdir(const tackle::path_string & test_case_name, const tackle::path_string & test_name)
{
for (const auto & v : g_test_cases) {
if (test_case_name == v.test_case_name && (test_name.empty() || test_name == "*" || test_name == v.test_name)) {
if (v.data_in_subdir && !std::string(v.data_in_subdir).empty()) {
return v.data_in_subdir;
}
return tackle::path_string{};
}
}
return tackle::path_string{};
}
tackle::path_string get_data_out_subdir(const tackle::path_string & test_case_name, const tackle::path_string & test_name)
{
for (const auto & v : g_test_cases) {
if (test_case_name == v.test_case_name && (test_name.empty() || test_name == "*" || test_name == v.test_name)) {
if (v.data_out_subdir && !std::string(v.data_out_subdir).empty()) {
return v.data_out_subdir;
}
return tackle::path_string{};
}
}
return tackle::path_string{};
}
tackle::path_string get_ref_in_subdir(const tackle::path_string & test_case_name, const tackle::path_string & test_name)
{
for (const auto & v : g_test_cases) {
if (test_case_name == v.test_case_name && (test_name.empty() || test_name == "*" || test_name == v.test_name)) {
if (v.ref_in_subdir && !std::string(v.ref_in_subdir).empty()) {
return v.ref_in_subdir;
}
return tackle::path_string{};
}
}
return tackle::path_string{};
}
void interrupt_test()
{
TEST_LOG_OUT(SKIP, "User interrupted.");
}
void log_out_va(int lvl, const char * fmt, va_list vl)
{
size_t lvl_offset = (lvl) & PREFIX_OFFSET_MASK;
if (lvl_offset < MIN_LVL || lvl_offset > MAX_LVL) lvl_offset = MIN_LVL;
lvl_offset -= MIN_LVL;
#if defined(UTILITY_PLATFORM_WINDOWS)
HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
SetConsoleTextAttribute(hConsole, _get_log_out_console_attrs(lvl_offset));
const bool is_global_init = !(lvl & FROM_GLOBAL_INIT);
if (is_global_init) {
fprintf(stderr, "%s", _get_test_case_msg_prefix_str(lvl_offset));
}
else {
fprintf(stdout, "%s", _get_global_init_msg_prefix_str(lvl_offset));
}
vfprintf(stderr, fmt, vl);
fputs("\n", stderr);
SetConsoleTextAttribute(hConsole, ConsoleInfo.wAttributes);
#elif defined(UTILITY_PLATFORM_POSIX)
const bool is_global_init = !(lvl & FROM_GLOBAL_INIT); \
if (is_global_init) {
fprintf(stderr, "%s%s", _get_console_color_ansi_sequence(lvl_offset), _get_test_case_msg_prefix_str(lvl_offset));
}
else {
fprintf(stdout, "%s%s", _get_console_color_ansi_sequence(lvl_offset), _get_global_init_msg_prefix_str(lvl_offset));
}
vfprintf(stderr, fmt, vl);
fputs("\033[0m\n", stderr);
#else
#error platform is not implemented
#endif
}
void log_out(int lvl, const char * fmt, ...)
{
va_list vl;
va_start(vl, fmt);
log_out_va(lvl, fmt, vl);
va_end(vl);
}
void log_out_predicate_va(const log_out_predicate_func_t & functor, int lvl, const char * fmt, va_list vl)
{
log_out_va(lvl, fmt, vl);
functor(lvl, fmt, vl);
}
void log_out_predicate(const log_out_predicate_func_t & functor, int lvl, const char * fmt, ...)
{
va_list vl;
va_start(vl, fmt);
log_out_predicate_va(functor, lvl, fmt, vl);
va_end(vl);
}
}
//TestCaseStaticBase
TestCaseStaticBase::TestCaseStaticBase()
{
}
const tackle::path_string & TestCaseStaticBase::get_data_in_var(const char * error_msg_prefix)
{
if (s_TESTS_DATA_IN_ROOT.empty() || !utility::is_directory_path(s_TESTS_DATA_IN_ROOT, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: s_TESTS_DATA_IN_ROOT directory does not exist: \"{:s}\"",
error_msg_prefix, s_TESTS_DATA_IN_ROOT)
#else
utility::string_format(256, "%s: s_TESTS_DATA_IN_ROOT directory does not exist: \"%s\"",
error_msg_prefix, s_TESTS_DATA_IN_ROOT)
#endif
);
}
return s_TESTS_DATA_IN_ROOT;
}
const tackle::path_string & TestCaseStaticBase::get_data_out_var(const char * error_msg_prefix)
{
if (s_TESTS_DATA_OUT_ROOT.empty() || !utility::is_directory_path(s_TESTS_DATA_OUT_ROOT, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: s_TESTS_DATA_OUT_ROOT directory does not exist: \"{:s}\"",
error_msg_prefix, s_TESTS_DATA_OUT_ROOT)
#else
utility::string_format(256, "%s: s_TESTS_DATA_OUT_ROOT directory does not exist: \"%s\"",
error_msg_prefix, s_TESTS_DATA_OUT_ROOT)
#endif
);
}
return s_TESTS_DATA_OUT_ROOT;
}
tackle::path_string TestCaseStaticBase::get_data_in_root(const char * error_msg_prefix, const tackle::path_string & test_case_name, const tackle::path_string & test_name)
{
const tackle::path_string & data_in_subdir = test::get_data_in_subdir(test_case_name, test_name);
return get_data_in_var(error_msg_prefix) / data_in_subdir;
}
tackle::path_string TestCaseStaticBase::get_data_out_root(const char * error_msg_prefix, const tackle::path_string & test_case_name, const tackle::path_string & test_name)
{
const tackle::path_string & data_out_subdir = test::get_data_out_subdir(test_case_name, test_name);
return get_data_out_var(error_msg_prefix) / data_out_subdir;
}
tackle::path_string TestCaseStaticBase::get_data_in_dir_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_data_in_var(error_msg_prefix) / path_suffix;
if (!::utility::is_directory_path(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: directory does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: directory does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
tackle::path_string TestCaseStaticBase::get_data_out_dir_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_data_out_var(error_msg_prefix) / path_suffix;
if (!::utility::is_directory_path(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: directory does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: directory does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
tackle::path_string TestCaseStaticBase::get_data_in_file_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_data_in_var(error_msg_prefix) / path_suffix;
if (!::utility::is_regular_file(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: file path does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
tackle::path_string TestCaseStaticBase::get_data_out_file_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_data_out_var(error_msg_prefix) / path_suffix;
if (!::utility::is_regular_file(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: file path does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
//TestCaseWithDataReference
TestCaseWithDataReference::TestCaseWithDataReference()
{
}
const tackle::path_string & TestCaseWithDataReference::get_ref_var(const char * error_msg_prefix)
{
if (s_TESTS_REF_DIR.empty() || !utility::is_directory_path(s_TESTS_REF_DIR, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: s_TESTS_REF_DIR directory does not exist: \"{:s}\"",
error_msg_prefix, s_TESTS_REF_DIR)
#else
utility::string_format(256, "%s: s_TESTS_REF_DIR directory does not exist: \"{:s}\"",
error_msg_prefix, s_TESTS_REF_DIR)
#endif
);
}
return s_TESTS_REF_DIR;
}
tackle::path_string TestCaseWithDataReference::get_ref_dir(const char * error_msg_prefix, const char * prefix_dir, const char * suffix_dir)
{
return get_ref_dir(error_msg_prefix,
tackle::path_string((prefix_dir && *prefix_dir != '\0') ? prefix_dir : ""),
tackle::path_string((suffix_dir && *suffix_dir != '\0') ? suffix_dir : ""));
}
tackle::path_string TestCaseWithDataReference::get_ref_dir(const char * error_msg_prefix, const tackle::path_string & prefix_dir, const tackle::path_string & suffix_dir)
{
const tackle::path_string & root_dir_var = get_ref_var(error_msg_prefix);
const bool has_prefix_dir = !prefix_dir.empty();
const tackle::path_string ref_dir = root_dir_var / prefix_dir / (has_prefix_dir ? "ref" : "") / suffix_dir;
// reference directory must already exist at first request
if (!utility::is_directory_path(ref_dir, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: test reference directory does not exist: \"{:s}\"",
error_msg_prefix, ref_dir)
#else
utility::string_format(256, "%s: test reference directory does not exist: \"%s\"",
error_msg_prefix, ref_dir)
#endif
);
}
return ref_dir;
}
tackle::path_string TestCaseWithDataReference::get_ref_dir_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_ref_var(error_msg_prefix) / path_suffix;
if (!::utility::is_directory_path(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: directory does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: directory does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
tackle::path_string TestCaseWithDataReference::get_ref_file_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_ref_var(error_msg_prefix) / path_suffix;
if (!::utility::is_regular_file(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
//TestCaseWithDataGenerator
TestCaseWithDataGenerator::TestCaseWithDataGenerator()
{
}
const tackle::path_string & TestCaseWithDataGenerator::get_gen_var(const char * error_msg_prefix)
{
if (s_TESTS_GEN_DIR.empty() || !utility::is_directory_path(s_TESTS_GEN_DIR, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: s_TESTS_GEN_DIR directory does not exist: \"%s\"",
error_msg_prefix, s_TESTS_GEN_DIR)
#else
utility::string_format(256, "%s: s_TESTS_GEN_DIR directory does not exist: \"%s\"",
error_msg_prefix, s_TESTS_GEN_DIR)
#endif
);
}
return s_TESTS_GEN_DIR;
}
tackle::path_string TestCaseWithDataGenerator::get_gen_dir(const char * error_msg_prefix, const char * prefix_dir, const char * suffix_dir)
{
return get_gen_dir(error_msg_prefix,
tackle::path_string((prefix_dir && *prefix_dir != '\0') ? prefix_dir : ""),
tackle::path_string((suffix_dir && *suffix_dir != '\0') ? suffix_dir : ""));
}
tackle::path_string TestCaseWithDataGenerator::get_gen_dir(const char * error_msg_prefix, const tackle::path_string & prefix_dir, const tackle::path_string & suffix_dir)
{
const tackle::path_string & root_dir_var = get_gen_var(error_msg_prefix);
const bool has_prefix_dir = !prefix_dir.empty();
const tackle::path_string gen_dir = root_dir_var / prefix_dir / (has_prefix_dir ? "gen" : "") / suffix_dir;
// generated direcory must be created if not done yet
if (!utility::is_path_exists(gen_dir, true)) {
utility::create_directories(gen_dir, true);
}
return gen_dir;
}
tackle::path_string TestCaseWithDataGenerator::get_gen_dir_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_gen_var(error_msg_prefix) / path_suffix;
if (!::utility::is_directory_path(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: directory path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: directory path does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
tackle::path_string TestCaseWithDataGenerator::get_gen_file_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_gen_var(error_msg_prefix) / path_suffix;
if (!::utility::is_regular_file(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
//TestCaseWithDataOutput
TestCaseWithDataOutput::TestCaseWithDataOutput()
{
}
const tackle::path_string & TestCaseWithDataOutput::get_out_var(const char * error_msg_prefix)
{
if (s_TESTS_OUT_DIR.empty() || !utility::is_directory_path(s_TESTS_OUT_DIR, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: s_TESTS_OUT_DIR does not exist: \"{:s}\"",
error_msg_prefix, s_TESTS_OUT_DIR)
#else
utility::string_format(256, "%s: s_TESTS_OUT_DIR does not exist: \"%s\"",
error_msg_prefix, s_TESTS_OUT_DIR)
#endif
);
}
return s_TESTS_OUT_DIR;
}
tackle::path_string TestCaseWithDataOutput::get_out_dir(const char * error_msg_prefix, const char * prefix_dir, const char * suffix_dir)
{
return get_out_dir(error_msg_prefix,
tackle::path_string((prefix_dir && *prefix_dir != '\0') ? prefix_dir : ""),
tackle::path_string((suffix_dir && *suffix_dir != '\0') ? suffix_dir : ""));
}
tackle::path_string TestCaseWithDataOutput::get_out_dir(const char * error_msg_prefix, const tackle::path_string & prefix_dir, const tackle::path_string & suffix_dir)
{
const tackle::path_string & root_dir_var = get_out_var(error_msg_prefix);
const bool has_prefix_dir = !prefix_dir.empty();
const tackle::path_string out_dir = root_dir_var / prefix_dir / (has_prefix_dir ? "out" : "") / suffix_dir;
// output direcory must be created if not done yet
if (!utility::is_path_exists(out_dir, true)) {
utility::create_directories(out_dir, true);
}
return out_dir;
}
tackle::path_string TestCaseWithDataOutput::get_out_dir_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_out_var(error_msg_prefix) / path_suffix;
if (!::utility::is_directory_path(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: directory path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: directory path does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
tackle::path_string TestCaseWithDataOutput::get_out_file_path(const char * error_msg_prefix, const tackle::path_string & path_suffix)
{
const tackle::path_string & path = get_out_var(error_msg_prefix) / path_suffix;
if (!::utility::is_regular_file(path, true)) {
DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
fmt::format("{:s}: file path does not exist: \"{:s}\"",
error_msg_prefix, path)
#else
utility::string_format(256, "%s: file path does not exist: \"%s\"",
error_msg_prefix, path)
#endif
);
}
return path;
}
#else
namespace {
enum dummy { dummy_ = 1 }; // to suppress compiler warnings around an empty translation unit
}
#endif