Clean Tests

This commit is contained in:
2025-12-15 21:20:30 +05:30
parent 71d83859f5
commit 1b24256653
21 changed files with 1224 additions and 1396 deletions

View File

@ -35,17 +35,7 @@ target_link_libraries(IACore PRIVATE
OpenSSL::Crypto
)
#if(WIN32)
# target_link_libraries(IACore PUBLIC mimalloc-static)
#
# if(MSVC)
# target_link_options(IACore PUBLIC "/INCLUDE:mi_version")
# else()
# target_link_options(IACore PUBLIC "")
# endif()
#else()
# target_link_libraries(IACore PUBLIC mimalloc)
#endif()
target_link_libraries(IACore PUBLIC mimalloc-static)
target_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp)

View File

@ -17,8 +17,6 @@
#include <IACore/IACore.hpp>
#include <IACore/Logger.hpp>
// #include <mimalloc-new-delete.h>
namespace IACore
{
HighResTimePoint g_startTime{};

View File

@ -1,16 +1,16 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
@ -20,63 +20,86 @@
#ifdef __cplusplus
#include <exception>
# include <exception>
// -----------------------------------------------------------------------------
// Macros
// -----------------------------------------------------------------------------
#define valid_iatest_runner(type) iatest::_valid_iatest_runner<type>::value_type
# define valid_iatest_runner(type) iatest::_valid_iatest_runner<type>::value_type
// Internal macro to handle the return logic
#define __iat_micro_test(call) \
if(!(call)) return FALSE
# define __iat_micro_test(call) \
if (!(call)) \
return FALSE
#define IAT_CHECK(v) __iat_micro_test(_test((v), #v))
#define IAT_CHECK_NOT(v) __iat_micro_test(_test_not((v), "NOT " #v))
#define IAT_CHECK_EQ(lhs, rhs) __iat_micro_test(_test_eq((lhs), (rhs), #lhs " == " #rhs))
#define IAT_CHECK_NEQ(lhs, rhs) __iat_micro_test(_test_neq((lhs), (rhs), #lhs " != " #rhs))
# define IAT_CHECK(v) __iat_micro_test(_test((v), #v))
# define IAT_CHECK_NOT(v) __iat_micro_test(_test_not((v), "NOT " #v))
# define IAT_CHECK_EQ(lhs, rhs) __iat_micro_test(_test_eq((lhs), (rhs), #lhs " == " #rhs))
# define IAT_CHECK_NEQ(lhs, rhs) __iat_micro_test(_test_neq((lhs), (rhs), #lhs " != " #rhs))
// Float specific checks (Game dev essential)
#define IAT_CHECK_APPROX(lhs, rhs) __iat_micro_test(_test_approx((lhs), (rhs), #lhs " ~= " #rhs))
# define IAT_CHECK_APPROX(lhs, rhs) __iat_micro_test(_test_approx((lhs), (rhs), #lhs " ~= " #rhs))
#define IAT_UNIT(func) _test_unit([this](){ return this->func(); }, # func)
#define IAT_NAMED_UNIT(n, func) _test_unit([this](){ return this->func(); }, n)
# define IAT_UNIT(func) _test_unit([this]() { return this->func(); }, #func)
# define IAT_NAMED_UNIT(n, func) _test_unit([this]() { return this->func(); }, n)
#define IAT_BLOCK(name) class name: public ia::iatest::block
# define IAT_BLOCK(name) class name : public ia::iatest::block
// Concatenation fix for macros
#define IAT_BEGIN_BLOCK(_group, _name) class _group##_##_name : public ia::iatest::block { \
public: PCCHAR name() CONST OVERRIDE { return #_group "::" #_name; } private:
# define IAT_BEGIN_BLOCK(_group, _name) \
class _group##_##_name : public ia::iatest::block \
{ \
public: \
PCCHAR name() CONST OVERRIDE \
{ \
return #_group "::" #_name; \
} \
\
private:
#define IAT_END_BLOCK() };
# define IAT_END_BLOCK() \
} \
;
#define IAT_BEGIN_TEST_LIST() public: VOID declareTests() OVERRIDE {
#define IAT_ADD_TEST(name) IAT_UNIT(name)
#define IAT_END_TEST_LIST() } private:
# define IAT_BEGIN_TEST_LIST() \
public: \
VOID declareTests() OVERRIDE \
{
# define IAT_ADD_TEST(name) IAT_UNIT(name)
# define IAT_END_TEST_LIST() \
} \
\
private:
namespace ia::iatest
{
// -------------------------------------------------------------------------
// Type Printing Helper (To show WHAT failed)
// -------------------------------------------------------------------------
template<typename T>
std::string ToString(CONST T& value) {
if constexpr (std::is_arithmetic_v<T>) {
template<typename T> std::string ToString(CONST T &value)
{
if constexpr (std::is_arithmetic_v<T>)
{
return std::to_string(value);
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const char*>) {
}
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const char *>)
{
return std::string("\"") + value + "\"";
} else {
}
else
{
return "{Object}"; // Fallback for complex types
}
}
// Specialization for pointers
template<typename T>
std::string ToString(T* value) {
if (value == NULLPTR) return "nullptr";
template<typename T> std::string ToString(T *value)
{
if (value == NULLPTR)
return "nullptr";
std::stringstream ss;
ss << "ptr(" << (void*)value << ")";
ss << "ptr(" << (void *) value << ")";
return ss.str();
}
@ -86,26 +109,30 @@ namespace ia::iatest
DEFINE_TYPE(functor_t, std::function<BOOL()>);
struct unit_t {
struct unit_t
{
std::string Name;
functor_t Functor;
functor_t Functor;
};
class block
{
public:
public:
virtual ~block() = default;
PURE_VIRTUAL(PCCHAR name() CONST);
PURE_VIRTUAL(VOID declareTests());
std::vector<unit_t>& units() { return m_units; }
protected:
// Generic Equality
template<typename T1, typename T2>
BOOL _test_eq(IN CONST T1& lhs, IN CONST T2& rhs, IN PCCHAR description)
std::vector<unit_t> &units()
{
if(lhs != rhs) {
return m_units;
}
protected:
// Generic Equality
template<typename T1, typename T2> BOOL _test_eq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
{
if (lhs != rhs)
{
print_fail(description, ToString(lhs), ToString(rhs));
return FALSE;
}
@ -113,10 +140,10 @@ namespace ia::iatest
}
// Generic Inequality
template<typename T1, typename T2>
BOOL _test_neq(IN CONST T1& lhs, IN CONST T2& rhs, IN PCCHAR description)
template<typename T1, typename T2> BOOL _test_neq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
{
if(lhs == rhs) {
if (lhs == rhs)
{
print_fail(description, ToString(lhs), "NOT " + ToString(rhs));
return FALSE;
}
@ -124,12 +151,12 @@ namespace ia::iatest
}
// Floating Point Approximation (Epsilon check)
template<typename T>
BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description)
template<typename T> BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description)
{
static_assert(std::is_floating_point_v<T>, "Approx only works for floats/doubles");
T diff = std::abs(lhs - rhs);
if (diff > static_cast<T>(0.0001)) { // Default epsilon
if (diff > static_cast<T>(0.0001))
{ // Default epsilon
print_fail(description, ToString(lhs), ToString(rhs));
return FALSE;
}
@ -138,7 +165,8 @@ namespace ia::iatest
BOOL _test(IN BOOL value, IN PCCHAR description)
{
if(!value) {
if (!value)
{
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description);
return FALSE;
}
@ -147,7 +175,8 @@ namespace ia::iatest
BOOL _test_not(IN BOOL value, IN PCCHAR description)
{
if(value) {
if (value)
{
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description);
return FALSE;
}
@ -159,8 +188,9 @@ namespace ia::iatest
m_units.push_back({name, functor});
}
private:
VOID print_fail(PCCHAR desc, std::string v1, std::string v2) {
private:
VOID print_fail(PCCHAR desc, std::string v1, std::string v2)
{
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", desc);
printf(__CC_RED " Expected: %s" __CC_DEFAULT "\n", v2.c_str());
printf(__CC_RED " Actual: %s" __CC_DEFAULT "\n", v1.c_str());
@ -171,91 +201,158 @@ namespace ia::iatest
template<typename block_class>
concept valid_block_class = std::derived_from<block_class, block>;
// -------------------------------------------------------------------------
// Runner
// -------------------------------------------------------------------------
template<BOOL stopOnFail = false, BOOL isVerbose = false>
class runner
template<BOOL stopOnFail = false, BOOL isVerbose = false> class runner
{
public:
runner(){}
~runner() { summarize(); }
public:
runner()
{
}
~runner()
{
summarize();
}
template<typename block_class>
requires valid_block_class<block_class>
requires valid_block_class<block_class>
VOID testBlock();
private:
private:
VOID summarize();
private:
SIZE_T m_testCount { 0 };
SIZE_T m_failCount { 0 };
SIZE_T m_blockCount { 0 };
private:
SIZE_T m_testCount{0};
SIZE_T m_failCount{0};
SIZE_T m_blockCount{0};
};
template<BOOL stopOnFail, BOOL isVerbose>
template<typename block_class>
requires valid_block_class<block_class>
requires valid_block_class<block_class>
VOID runner<stopOnFail, isVerbose>::testBlock()
{
m_blockCount++;
block_class b;
b.declareTests();
printf(__CC_MAGENTA "Testing [%s]..." __CC_DEFAULT "\n", b.name());
for(auto& v: b.units())
for (auto &v : b.units())
{
m_testCount++;
if constexpr(isVerbose) {
if constexpr (isVerbose)
{
printf(__CC_YELLOW " Testing %s...\n" __CC_DEFAULT, v.Name.c_str());
}
BOOL result = FALSE;
try {
try
{
// Execute the test function
result = v.Functor();
}
catch (const std::exception& e) {
catch (const std::exception &e)
{
printf(__CC_RED " CRITICAL EXCEPTION in %s: %s\n" __CC_DEFAULT, v.Name.c_str(), e.what());
result = FALSE;
}
catch (...) {
catch (...)
{
printf(__CC_RED " UNKNOWN CRITICAL EXCEPTION in %s\n" __CC_DEFAULT, v.Name.c_str());
result = FALSE;
}
if(!result)
if (!result)
{
m_failCount++;
if constexpr(stopOnFail) { summarize(); exit(-1); }
if constexpr (stopOnFail)
{
summarize();
exit(-1);
}
}
}
fputs("\n", stdout);
}
template<BOOL stopOnFail, BOOL isVerbose>
VOID runner<stopOnFail, isVerbose>::summarize()
template<BOOL stopOnFail, BOOL isVerbose> VOID runner<stopOnFail, isVerbose>::summarize()
{
printf(__CC_GREEN "\n-----------------------------------\n\t SUMMARY\n-----------------------------------\n");
if(!m_failCount) {
printf(__CC_GREEN
"\n-----------------------------------\n\t SUMMARY\n-----------------------------------\n");
if (!m_failCount)
{
printf("\n\tALL TESTS PASSED!\n\n");
} else {
FLOAT64 successRate = (100.0 * static_cast<FLOAT64>(m_testCount - m_failCount)/static_cast<FLOAT64>(m_testCount));
printf(__CC_RED "%zu OUT OF %zu TESTS FAILED\n" __CC_YELLOW "Success Rate: %.2f%%\n", m_failCount, m_testCount, successRate);
}
printf(__CC_MAGENTA "Ran %zu test(s) across %zu block(s)\n" __CC_GREEN "-----------------------------------" __CC_DEFAULT "\n", m_testCount, m_blockCount);
else
{
FLOAT64 successRate =
(100.0 * static_cast<FLOAT64>(m_testCount - m_failCount) / static_cast<FLOAT64>(m_testCount));
printf(__CC_RED "%zu OUT OF %zu TESTS FAILED\n" __CC_YELLOW "Success Rate: %.2f%%\n", m_failCount,
m_testCount, successRate);
}
printf(__CC_MAGENTA "Ran %zu test(s) across %zu block(s)\n" __CC_GREEN
"-----------------------------------" __CC_DEFAULT "\n",
m_testCount, m_blockCount);
}
template<typename>
struct _valid_iatest_runner : std::false_type {};
template<typename> struct _valid_iatest_runner : std::false_type
{
};
template<BOOL stopOnFail, BOOL isVerbose>
struct _valid_iatest_runner<runner<stopOnFail,isVerbose>> : std::true_type {};
}
struct _valid_iatest_runner<runner<stopOnFail, isVerbose>> : std::true_type
{
};
// -------------------------------------------------------------------------
// Global Test Registry
// -------------------------------------------------------------------------
// Standard runner configuration for the single executable
using DefaultRunner = runner<false, true>;
class TestRegistry
{
public:
using TestEntry = std::function<void(DefaultRunner &)>;
static std::vector<TestEntry> &GetEntries()
{
static std::vector<TestEntry> entries;
return entries;
}
static int RunAll()
{
DefaultRunner r;
auto &entries = GetEntries();
printf(__CC_CYAN "[IATest] Discovered %zu Test Blocks\n\n" __CC_DEFAULT, entries.size());
for (auto &entry : entries)
{
entry(r);
}
// The destructor of 'r' will automatically print the summary
return 0;
}
};
template<typename BlockType> struct AutoRegister
{
AutoRegister()
{
TestRegistry::GetEntries().push_back([](DefaultRunner &r) { r.testBlock<BlockType>(); });
}
};
} // namespace ia::iatest
// Usage: IAT_REGISTER_ENTRY(Core, Utils)
# define IAT_REGISTER_ENTRY(Group, Name) static ia::iatest::AutoRegister<Group##_##Name> _iat_reg_##Group##_##Name;
#endif // __cplusplus