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

3
.gitignore vendored
View File

@ -47,4 +47,5 @@
.cache/ .cache/
.local/ .local/
Build/ [Bb]uild
out/

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"editor.formatOnSave": true,
"C_Cpp.clang_format_fallbackStyle": "Microsoft"
}

16
.vscode/tasks.json vendored
View File

@ -1,13 +1,19 @@
{ {
"version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "build", "label": "CMake Build",
"type": "shell", "type": "cmake",
"command": "python3 Tools/Builder/Build.py", "command": "build",
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
},
"presentation": {
"reveal": "silent",
"panel": "shared"
},
"detail": "Builds the currently selected CMake Preset"
} }
} ]
],
} }

View File

@ -54,16 +54,16 @@ FetchContent_Declare(
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
) )
#FetchContent_Declare( FetchContent_Declare(
# mimalloc mimalloc
# GIT_REPOSITORY https://github.com/microsoft/mimalloc.git GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
# GIT_TAG v3.0.10 GIT_TAG v3.0.10
# SYSTEM SYSTEM
# EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
# PATCH_COMMAND ${CMAKE_COMMAND} PATCH_COMMAND ${CMAKE_COMMAND}
# -DSOURCE_DIR=<SOURCE_DIR> -DSOURCE_DIR=<SOURCE_DIR>
# -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/PatchMimalloc.cmake -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/PatchMimalloc.cmake
#) )
FetchContent_Declare( FetchContent_Declare(
tl-expected tl-expected
@ -81,10 +81,10 @@ FetchContent_Declare(
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
) )
#set(MI_OVERRIDE ON CACHE BOOL "" FORCE) set(MI_OVERRIDE ON CACHE BOOL "" FORCE)
#set(MI_BUILD_STATIC ON CACHE BOOL "" FORCE) set(MI_BUILD_STATIC ON CACHE BOOL "" FORCE)
#set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE)
#set(MI_BUILD_SHARED OFF CACHE BOOL "" FORCE) set(MI_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(EXPECTED_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(EXPECTED_BUILD_TESTS OFF CACHE BOOL "" FORCE)
@ -97,7 +97,6 @@ set(ZSTD_BUILD_STATIC ON CACHE BOOL "" FORCE)
set(ZLIB_COMPAT ON CACHE BOOL "" FORCE) set(ZLIB_COMPAT ON CACHE BOOL "" FORCE)
set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "" FORCE) set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(httplib nlohmann_json glaze simdjson ZLIB zstd tl-expected unordered_dense #mimalloc FetchContent_MakeAvailable(httplib nlohmann_json glaze simdjson ZLIB zstd tl-expected unordered_dense mimalloc)
)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)

View File

@ -45,17 +45,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"
) )
endif() endif()
add_compile_definitions(-D_ITERATOR_DEBUG_LEVEL=0)
add_subdirectory(Src/) add_subdirectory(Src/)
if(IACore_BUILD_TESTS) if(IACore_BUILD_TESTS)
add_subdirectory(Tests) add_subdirectory(Tests)
endif() endif()
# -------------------------------------------------
# Local Development Sandboxes (not included in source control)
# -------------------------------------------------
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.local")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/.local")
endif()

108
CMakePresets.json Normal file
View File

@ -0,0 +1,108 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 28
},
"configurePresets": [
{
"name": "base-common",
"hidden": true,
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "windows-base",
"hidden": true,
"inherits": "base-common",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"VCPKG_TARGET_TRIPLET": "x64-windows"
}
},
{
"name": "linux-base",
"hidden": true,
"inherits": "base-common",
"condition": {
"type": "notEquals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows-default",
"displayName": "Windows (VCPKG)",
"description": "Windows build using VCPKG",
"inherits": "windows-base"
},
{
"name": "linux-default",
"displayName": "Linux (System)",
"description": "Linux build using system compilers",
"inherits": "linux-base"
},
{
"name": "linux-ci",
"displayName": "Linux CI Build",
"description": "Linux CI Build",
"inherits": "linux-base",
"cacheVariables": {
"SDL_UNIX_CONSOLE_BUILD": "ON",
"IS_CI_BUILD": "ON"
}
}
],
"buildPresets": [
{
"name": "windows-debug",
"configurePreset": "windows-default",
"configuration": "debug",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "linux-debug",
"configurePreset": "linux-default",
"configuration": "debug",
"condition": {
"type": "notEquals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "linux-ci-debug",
"configurePreset": "linux-ci",
"configuration": "debug",
"condition": {
"type": "notEquals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "linux-ci-release",
"configurePreset": "linux-ci",
"configuration": "release",
"condition": {
"type": "notEquals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
}
]
}

View File

@ -1,2 +1,94 @@
# IACore # IACore (Independent Architecture Core)
![License](https://img.shields.io/badge/license-GPLv3-blue.svg)
![Standard](https://img.shields.io/badge/C%2B%2B-20-yellow.svg)
![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux-lightgrey.svg)
IACore is a high-performance, battery-included C++20 foundation library designed to eliminate "dependency hell." It bundles essential systems—IPC, Logging, Networking, Compression, and Async Scheduling—into a single, coherent API.
Originally developed as the internal core for IASoft (PVT) LTD., it is now open-source to provide a standardized bedrock for C++ applications.
## ✨ Features
* **🚀 High-Performance IPC:** Shared-Memory Ring Buffers with wait-free SPSC synchronization.
* **🌐 Networking:** Integrated HTTP/HTTPS client (wrapper around `cpp-httplib` with automatic Zlib/Gzip/Brotli handling).
* **🧵 Async Scheduler:** A job system with high/normal priority queues and work stealing.
* **💾 File I/O:** Memory-mapped file operations and optimized binary stream readers/writers.
* **📦 Compression:** Unified API for Zlib, Gzip, and Zstd.
* **📜 Logging:** Thread-safe, colored console and disk logging.
* **⚡ Modern C++:** Heavily uses C++20 concepts, `std::span`, and `tl::expected` for error handling.
## 🛠️ Integration
IACore is built with CMake. You can include it in your project via `FetchContent` or by adding it as a subdirectory.
### CMake Example
```cmake
add_subdirectory(IACore)
add_executable(MyApp Main.cpp)
target_link_libraries(MyApp PRIVATE IACore)
```
## 📦 Dependencies
IACore manages its own dependencies via CMake's FetchContent. You do not need to install these manually:
* nlohmann_json & glaze (JSON Parsing)
* cpp-httplib (Networking)
* zlib-ng & zstd (Compression)
* tl-expected (Error Handling)
## 💡 Usage Examples
### 1. IPC (Manager & Node)
IACore provides a manager/node architecture using shared memory.
Manager:
```C++
#include <IACore/IPC.hpp>
// Spawns a child process and connects via Shared Memory
auto nodeID = manager.SpawnNode("MyChildNodeExe");
manager.WaitTillNodeIsOnline(*nodeID);
// Send data with zero-copy overhead
String msg = "Hello Node";
manager.SendPacket(*nodeID, 100, {(PCUINT8)msg.data(), msg.size()});
```
### 2. Async Jobs
```C++
#include <IACore/AsyncOps.hpp>
// Initialize worker threads (hardware_concurrency - 2)
IACore::AsyncOps::InitializeScheduler();
// Schedule a task
IACore::AsyncOps::Schedule *mySchedule = new IACore::AsyncOps::Schedule();
IACore::AsyncOps::ScheduleTask([](auto workerID) {
printf("Running on worker %d\n", workerID);
}, 0, mySchedule);
// Wait for completion
IACore::AsyncOps::WaitForScheduleCompletion(mySchedule);
```
### 3. HTTP Request
```C++
#include <IACore/HttpClient.hpp>
IACore::HttpClient client("[https://api.example.com](https://api.example.com)");
auto res = client.JsonGet<MyResponseStruct>("/data", {});
if (res) {
std::cout << "Data: " << res->value << "\n";
} else {
std::cerr << "Error: " << res.error() << "\n";
}
```
## ⚖️ License
This project is licensed under the GNU General Public License v3 (GPLv3).

View File

@ -35,17 +35,7 @@ target_link_libraries(IACore PRIVATE
OpenSSL::Crypto OpenSSL::Crypto
) )
#if(WIN32) target_link_libraries(IACore PUBLIC mimalloc-static)
# 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_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp) target_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp)

View File

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

View File

@ -30,7 +30,8 @@
// Internal macro to handle the return logic // Internal macro to handle the return logic
# define __iat_micro_test(call) \ # define __iat_micro_test(call) \
if(!(call)) return FALSE if (!(call)) \
return FALSE
# define IAT_CHECK(v) __iat_micro_test(_test((v), #v)) # 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_NOT(v) __iat_micro_test(_test_not((v), "NOT " #v))
@ -46,35 +47,57 @@
# define IAT_BLOCK(name) class name : public ia::iatest::block # define IAT_BLOCK(name) class name : public ia::iatest::block
// Concatenation fix for macros // Concatenation fix for macros
#define IAT_BEGIN_BLOCK(_group, _name) class _group##_##_name : public ia::iatest::block { \ # define IAT_BEGIN_BLOCK(_group, _name) \
public: PCCHAR name() CONST OVERRIDE { return #_group "::" #_name; } private: 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_BEGIN_TEST_LIST() \
public: \
VOID declareTests() OVERRIDE \
{
# define IAT_ADD_TEST(name) IAT_UNIT(name) # define IAT_ADD_TEST(name) IAT_UNIT(name)
#define IAT_END_TEST_LIST() } private: # define IAT_END_TEST_LIST() \
} \
\
private:
namespace ia::iatest namespace ia::iatest
{ {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Type Printing Helper (To show WHAT failed) // Type Printing Helper (To show WHAT failed)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
template<typename T> template<typename T> std::string ToString(CONST T &value)
std::string ToString(CONST T& value) { {
if constexpr (std::is_arithmetic_v<T>) { if constexpr (std::is_arithmetic_v<T>)
{
return std::to_string(value); 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 + "\""; return std::string("\"") + value + "\"";
} else { }
else
{
return "{Object}"; // Fallback for complex types return "{Object}"; // Fallback for complex types
} }
} }
// Specialization for pointers // Specialization for pointers
template<typename T> template<typename T> std::string ToString(T *value)
std::string ToString(T* value) { {
if (value == NULLPTR) return "nullptr"; if (value == NULLPTR)
return "nullptr";
std::stringstream ss; std::stringstream ss;
ss << "ptr(" << (void *) value << ")"; ss << "ptr(" << (void *) value << ")";
return ss.str(); return ss.str();
@ -86,7 +109,8 @@ namespace ia::iatest
DEFINE_TYPE(functor_t, std::function<BOOL()>); DEFINE_TYPE(functor_t, std::function<BOOL()>);
struct unit_t { struct unit_t
{
std::string Name; std::string Name;
functor_t Functor; functor_t Functor;
}; };
@ -98,14 +122,17 @@ namespace ia::iatest
PURE_VIRTUAL(PCCHAR name() CONST); PURE_VIRTUAL(PCCHAR name() CONST);
PURE_VIRTUAL(VOID declareTests()); PURE_VIRTUAL(VOID declareTests());
std::vector<unit_t>& units() { return m_units; } std::vector<unit_t> &units()
{
return m_units;
}
protected: protected:
// Generic Equality // Generic Equality
template<typename T1, typename T2> template<typename T1, typename T2> BOOL _test_eq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
BOOL _test_eq(IN CONST T1& lhs, IN CONST T2& rhs, IN PCCHAR description) {
if (lhs != rhs)
{ {
if(lhs != rhs) {
print_fail(description, ToString(lhs), ToString(rhs)); print_fail(description, ToString(lhs), ToString(rhs));
return FALSE; return FALSE;
} }
@ -113,10 +140,10 @@ namespace ia::iatest
} }
// Generic Inequality // Generic Inequality
template<typename T1, typename T2> template<typename T1, typename T2> BOOL _test_neq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
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)); print_fail(description, ToString(lhs), "NOT " + ToString(rhs));
return FALSE; return FALSE;
} }
@ -124,12 +151,12 @@ namespace ia::iatest
} }
// Floating Point Approximation (Epsilon check) // Floating Point Approximation (Epsilon check)
template<typename T> template<typename T> BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description)
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"); static_assert(std::is_floating_point_v<T>, "Approx only works for floats/doubles");
T diff = std::abs(lhs - rhs); 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)); print_fail(description, ToString(lhs), ToString(rhs));
return FALSE; return FALSE;
} }
@ -138,7 +165,8 @@ namespace ia::iatest
BOOL _test(IN BOOL value, IN PCCHAR description) BOOL _test(IN BOOL value, IN PCCHAR description)
{ {
if(!value) { if (!value)
{
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description); printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description);
return FALSE; return FALSE;
} }
@ -147,7 +175,8 @@ namespace ia::iatest
BOOL _test_not(IN BOOL value, IN PCCHAR description) BOOL _test_not(IN BOOL value, IN PCCHAR description)
{ {
if(value) { if (value)
{
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description); printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description);
return FALSE; return FALSE;
} }
@ -160,7 +189,8 @@ namespace ia::iatest
} }
private: private:
VOID print_fail(PCCHAR desc, std::string v1, std::string v2) { VOID print_fail(PCCHAR desc, std::string v1, std::string v2)
{
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", desc); 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 " Expected: %s" __CC_DEFAULT "\n", v2.c_str());
printf(__CC_RED " Actual: %s" __CC_DEFAULT "\n", v1.c_str()); printf(__CC_RED " Actual: %s" __CC_DEFAULT "\n", v1.c_str());
@ -176,12 +206,17 @@ namespace ia::iatest
// Runner // Runner
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
template<BOOL stopOnFail = false, BOOL isVerbose = false> template<BOOL stopOnFail = false, BOOL isVerbose = false> class runner
class runner
{ {
public: public:
runner(){} runner()
~runner() { summarize(); } {
}
~runner()
{
summarize();
}
template<typename block_class> template<typename block_class>
requires valid_block_class<block_class> requires valid_block_class<block_class>
@ -210,20 +245,24 @@ namespace ia::iatest
for (auto &v : b.units()) for (auto &v : b.units())
{ {
m_testCount++; m_testCount++;
if constexpr(isVerbose) { if constexpr (isVerbose)
{
printf(__CC_YELLOW " Testing %s...\n" __CC_DEFAULT, v.Name.c_str()); printf(__CC_YELLOW " Testing %s...\n" __CC_DEFAULT, v.Name.c_str());
} }
BOOL result = FALSE; BOOL result = FALSE;
try { try
{
// Execute the test function // Execute the test function
result = v.Functor(); 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()); printf(__CC_RED " CRITICAL EXCEPTION in %s: %s\n" __CC_DEFAULT, v.Name.c_str(), e.what());
result = FALSE; result = FALSE;
} }
catch (...) { catch (...)
{
printf(__CC_RED " UNKNOWN CRITICAL EXCEPTION in %s\n" __CC_DEFAULT, v.Name.c_str()); printf(__CC_RED " UNKNOWN CRITICAL EXCEPTION in %s\n" __CC_DEFAULT, v.Name.c_str());
result = FALSE; result = FALSE;
} }
@ -231,31 +270,89 @@ namespace ia::iatest
if (!result) if (!result)
{ {
m_failCount++; m_failCount++;
if constexpr(stopOnFail) { summarize(); exit(-1); } if constexpr (stopOnFail)
{
summarize();
exit(-1);
}
} }
} }
fputs("\n", stdout); fputs("\n", stdout);
} }
template<BOOL stopOnFail, BOOL isVerbose> template<BOOL stopOnFail, BOOL isVerbose> VOID runner<stopOnFail, isVerbose>::summarize()
VOID runner<stopOnFail, isVerbose>::summarize()
{ {
printf(__CC_GREEN "\n-----------------------------------\n\t SUMMARY\n-----------------------------------\n"); printf(__CC_GREEN
"\n-----------------------------------\n\t SUMMARY\n-----------------------------------\n");
if(!m_failCount) { if (!m_failCount)
{
printf("\n\tALL TESTS PASSED!\n\n"); printf("\n\tALL TESTS PASSED!\n\n");
} else { }
FLOAT64 successRate = (100.0 * static_cast<FLOAT64>(m_testCount - m_failCount)/static_cast<FLOAT64>(m_testCount)); else
printf(__CC_RED "%zu OUT OF %zu TESTS FAILED\n" __CC_YELLOW "Success Rate: %.2f%%\n", m_failCount, m_testCount, successRate); {
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); printf(__CC_MAGENTA "Ran %zu test(s) across %zu block(s)\n" __CC_GREEN
"-----------------------------------" __CC_DEFAULT "\n",
m_testCount, m_blockCount);
} }
template<typename> template<typename> struct _valid_iatest_runner : std::false_type
struct _valid_iatest_runner : std::false_type {}; {
};
template<BOOL stopOnFail, BOOL isVerbose> 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 #endif // __cplusplus

View File

@ -1,220 +0,0 @@
// 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/>.
#include <IACore/BinaryStreamReader.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, BinaryStreamReader)
// -------------------------------------------------------------------------
// 1. Basic Primitive Reading (UINT8)
// -------------------------------------------------------------------------
BOOL TestReadUint8()
{
UINT8 data[] = { 0xAA, 0xBB, 0xCC };
BinaryStreamReader reader(data);
// Read First Byte
auto val1 = reader.Read<UINT8>();
IAT_CHECK(val1.has_value());
IAT_CHECK_EQ(*val1, 0xAA);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)1);
// Read Second Byte
auto val2 = reader.Read<UINT8>();
IAT_CHECK_EQ(*val2, 0xBB);
return TRUE;
}
// -------------------------------------------------------------------------
// 2. Multi-byte Reading (Endianness check)
// -------------------------------------------------------------------------
BOOL TestReadMultiByte()
{
// 0x04030201 in Little Endian memory layout
UINT8 data[] = { 0x01, 0x02, 0x03, 0x04 };
BinaryStreamReader reader(data);
auto val = reader.Read<UINT32>();
IAT_CHECK(val.has_value());
// Assuming standard x86/ARM Little Endian for this test
// If your engine supports Big Endian, you'd check architecture here
IAT_CHECK_EQ(*val, (UINT32)0x04030201);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)4);
IAT_CHECK(reader.IsEOF());
return TRUE;
}
// -------------------------------------------------------------------------
// 3. Floating Point (Approx check)
// -------------------------------------------------------------------------
BOOL TestReadFloat()
{
FLOAT32 pi = 3.14159f;
// Bit-cast float to bytes for setup
UINT8 data[4];
std::memcpy(data, &pi, 4);
BinaryStreamReader reader(data);
auto val = reader.Read<FLOAT32>();
IAT_CHECK(val.has_value());
IAT_CHECK_APPROX(*val, pi);
return TRUE;
}
// -------------------------------------------------------------------------
// 4. String Reading
// -------------------------------------------------------------------------
BOOL TestReadString()
{
const char* raw = "HelloIA";
BinaryStreamReader reader(std::span<const UINT8>((const UINT8*)raw, 7));
// Read "Hello" (5 bytes)
auto str = reader.ReadString(5);
IAT_CHECK(str.has_value());
IAT_CHECK_EQ(*str, String("Hello"));
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)5);
// Read remaining "IA"
auto str2 = reader.ReadString(2);
IAT_CHECK_EQ(*str2, String("IA"));
return TRUE;
}
// -------------------------------------------------------------------------
// 5. Batch Buffer Reading
// -------------------------------------------------------------------------
BOOL TestReadBuffer()
{
UINT8 src[] = { 1, 2, 3, 4, 5 };
UINT8 dst[3] = { 0 };
BinaryStreamReader reader(src);
// Read 3 bytes into dst
auto res = reader.Read(dst, 3);
IAT_CHECK(res.has_value());
// Verify dst content
IAT_CHECK_EQ(dst[0], 1);
IAT_CHECK_EQ(dst[1], 2);
IAT_CHECK_EQ(dst[2], 3);
// Verify cursor
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)3);
return TRUE;
}
// -------------------------------------------------------------------------
// 6. Navigation (Seek, Skip, Remaining)
// -------------------------------------------------------------------------
BOOL TestNavigation()
{
UINT8 data[10] = { 0 }; // Zero init
BinaryStreamReader reader(data);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)10);
// Skip
reader.Skip(5);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)5);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)5);
// Skip clamping
reader.Skip(100); // Should clamp to 10
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)10);
IAT_CHECK(reader.IsEOF());
// Seek
reader.Seek(2);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)2);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)8);
IAT_CHECK_NOT(reader.IsEOF());
return TRUE;
}
// -------------------------------------------------------------------------
// 7. Error Handling (EOF Protection)
// -------------------------------------------------------------------------
BOOL TestBoundaryChecks()
{
UINT8 data[] = { 0x00, 0x00 };
BinaryStreamReader reader(data);
// Valid read
UNUSED(reader.Read<UINT16>());
IAT_CHECK(reader.IsEOF());
// Invalid Read Primitive
auto val = reader.Read<UINT8>();
IAT_CHECK_NOT(val.has_value()); // Should be unexpected
// Invalid Read String
auto str = reader.ReadString(1);
IAT_CHECK_NOT(str.has_value());
// Invalid Batch Read
UINT8 buf[1];
auto batch = reader.Read(buf, 1);
IAT_CHECK_NOT(batch.has_value());
return TRUE;
}
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(TestReadUint8);
IAT_ADD_TEST(TestReadMultiByte);
IAT_ADD_TEST(TestReadFloat);
IAT_ADD_TEST(TestReadString);
IAT_ADD_TEST(TestReadBuffer);
IAT_ADD_TEST(TestNavigation);
IAT_ADD_TEST(TestBoundaryChecks);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
int main(int argc, char* argv[])
{
UNUSED(argc);
UNUSED(argv);
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner<false, true> testRunner;
// Run the BinaryStreamReader block
testRunner.testBlock<Core_BinaryReader>();
return 0;
}

View File

@ -1,222 +0,0 @@
// 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/>.
#include <IACore/BinaryStreamWriter.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, BinaryStreamWriter)
// -------------------------------------------------------------------------
// 1. Vector Mode (Dynamic Growth)
// -------------------------------------------------------------------------
BOOL TestVectorGrowth()
{
std::vector<UINT8> buffer;
// Start empty
BinaryStreamWriter writer(buffer);
// Write 1 Byte
writer.Write<UINT8>(0xAA);
IAT_CHECK_EQ(buffer.size(), (SIZE_T)1);
IAT_CHECK_EQ(buffer[0], 0xAA);
// Write 4 Bytes (UINT32)
// 0xDDCCBBAA -> Little Endian: AA BB CC DD
writer.Write<UINT32>(0xDDCCBBAA);
IAT_CHECK_EQ(buffer.size(), (SIZE_T)5);
// Verify Memory Layout
IAT_CHECK_EQ(buffer[1], 0xAA);
IAT_CHECK_EQ(buffer[2], 0xBB);
IAT_CHECK_EQ(buffer[3], 0xCC);
IAT_CHECK_EQ(buffer[4], 0xDD);
return TRUE;
}
// -------------------------------------------------------------------------
// 2. Vector Mode (Appending to Existing Data)
// -------------------------------------------------------------------------
BOOL TestVectorAppend()
{
// Vector starts with existing data
std::vector<UINT8> buffer = { 0x01, 0x02 };
BinaryStreamWriter writer(buffer);
// Should append to end, not overwrite 0x01
writer.Write<UINT8>(0x03);
IAT_CHECK_EQ(buffer.size(), (SIZE_T)3);
IAT_CHECK_EQ(buffer[0], 0x01);
IAT_CHECK_EQ(buffer[2], 0x03);
return TRUE;
}
// -------------------------------------------------------------------------
// 3. Fixed Mode (Span / No Allocation)
// -------------------------------------------------------------------------
BOOL TestFixedBuffer()
{
UINT8 rawData[10];
// Initialize with zeros
std::memset(rawData, 0, 10);
BinaryStreamWriter writer(rawData); // Implicit conversion to span
// Write UINT8
writer.Write<UINT8>(0xFF);
IAT_CHECK_EQ(rawData[0], 0xFF);
// Write UINT16
writer.Write<UINT16>(0x2211);
IAT_CHECK_EQ(rawData[1], 0x11);
IAT_CHECK_EQ(rawData[2], 0x22);
// Verify we haven't touched the rest
IAT_CHECK_EQ(rawData[3], 0x00);
return TRUE;
}
// -------------------------------------------------------------------------
// 4. WriteBytes (Bulk Write)
// -------------------------------------------------------------------------
BOOL TestWriteBytes()
{
std::vector<UINT8> buffer;
BinaryStreamWriter writer(buffer);
const char* msg = "IA";
writer.WriteBytes((PVOID)msg, 2);
writer.Write<UINT8>(0x00); // Null term
IAT_CHECK_EQ(buffer.size(), (SIZE_T)3);
IAT_CHECK_EQ(buffer[0], 'I');
IAT_CHECK_EQ(buffer[1], 'A');
IAT_CHECK_EQ(buffer[2], 0x00);
return TRUE;
}
// -------------------------------------------------------------------------
// 5. Random Access (WriteAt)
// -------------------------------------------------------------------------
BOOL TestRandomAccess()
{
std::vector<UINT8> buffer;
BinaryStreamWriter writer(buffer);
// Fill with placeholders
writer.Write<UINT32>(0xFFFFFFFF);
writer.Write<UINT32>(0xFFFFFFFF);
// Overwrite the first integer with 0x00000000
writer.WriteAt<UINT32>(0, 0);
// Overwrite the byte at index 4 (start of second int)
writer.WriteAt<UINT8>(4, 0xAA);
IAT_CHECK_EQ(buffer[0], 0x00);
IAT_CHECK_EQ(buffer[3], 0x00);
IAT_CHECK_EQ(buffer[4], 0xAA); // Modified
IAT_CHECK_EQ(buffer[5], 0xFF); // Unmodified
return TRUE;
}
// -------------------------------------------------------------------------
// 6. Floating Point Writing
// -------------------------------------------------------------------------
BOOL TestWriteFloat()
{
std::vector<UINT8> buffer;
BinaryStreamWriter writer(buffer);
FLOAT32 val = 1.234f;
writer.Write<FLOAT32>(val);
IAT_CHECK_EQ(buffer.size(), (SIZE_T)4);
// Read it back via memcpy to verify
FLOAT32 readBack;
std::memcpy(&readBack, buffer.data(), 4);
IAT_CHECK_APPROX(readBack, val);
return TRUE;
}
// -------------------------------------------------------------------------
// 7. GetPtrAt (Pointer Arithmetic Check)
// -------------------------------------------------------------------------
BOOL TestGetPtr()
{
std::vector<UINT8> buffer = { 0x10, 0x20, 0x30 };
BinaryStreamWriter writer(buffer);
PUINT8 p1 = writer.GetPtrAt(1);
IAT_CHECK(p1 != nullptr);
IAT_CHECK_EQ(*p1, 0x20);
// Bounds check (Valid boundary)
PUINT8 pLast = writer.GetPtrAt(2);
IAT_CHECK(pLast != nullptr);
// Bounds check (Invalid)
// Note: We don't test Panic here, but we check if it returns valid ptrs
PUINT8 pInvalid = writer.GetPtrAt(3);
IAT_CHECK(pInvalid == nullptr);
return TRUE;
}
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(TestVectorGrowth);
IAT_ADD_TEST(TestVectorAppend);
IAT_ADD_TEST(TestFixedBuffer);
IAT_ADD_TEST(TestWriteBytes);
IAT_ADD_TEST(TestRandomAccess);
IAT_ADD_TEST(TestWriteFloat);
IAT_ADD_TEST(TestGetPtr);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
int main(int argc, char* argv[])
{
UNUSED(argc);
UNUSED(argv);
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner<false, true> testRunner;
// Run the BinaryReader block
testRunner.testBlock<Core_BinaryStreamWriter>();
return 0;
}

View File

@ -1,62 +1,37 @@
set(TEST_NAME_PREFIX "IACore_Test_Unit_")
# ------------------------------------------------ # ------------------------------------------------
# C Compile Test # C Compile Test (Keep separate to verify C ABI)
# ------------------------------------------------ # ------------------------------------------------
add_executable(${TEST_NAME_PREFIX}CCompile "CCompile.c") add_executable(IACore_Test_C_ABI "CCompile.c")
set_target_properties(IACore_Test_C_ABI PROPERTIES
set_target_properties(${TEST_NAME_PREFIX}CCompile PROPERTIES
C_STANDARD 99 C_STANDARD 99
C_STANDARD_REQUIRED ON C_STANDARD_REQUIRED ON
LINKER_LANGUAGE C LINKER_LANGUAGE C
) )
target_link_libraries(IACore_Test_C_ABI PRIVATE IACore)
target_link_libraries(${TEST_NAME_PREFIX}CCompile PRIVATE IACore)
## ------------------------------------------------
## Unit: BinaryReader
## ------------------------------------------------
#add_executable(${TEST_NAME_PREFIX}BinaryReader "BinaryReader.cpp")
#target_link_libraries(${TEST_NAME_PREFIX}BinaryReader PRIVATE IACore)
#target_compile_options(${TEST_NAME_PREFIX}BinaryReader PRIVATE -fexceptions)
#set_target_properties(${TEST_NAME_PREFIX}BinaryReader PROPERTIES USE_EXCEPTIONS ON)
#
## ------------------------------------------------
## Unit: BinaryWriter
## ------------------------------------------------
#add_executable(${TEST_NAME_PREFIX}BinaryWriter "BinaryWriter.cpp")
#target_link_libraries(${TEST_NAME_PREFIX}BinaryWriter PRIVATE IACore)
#target_compile_options(${TEST_NAME_PREFIX}BinaryWriter PRIVATE -fexceptions)
#set_target_properties(${TEST_NAME_PREFIX}BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
# ------------------------------------------------ # ------------------------------------------------
# Unit: Environment # The Unified C++ Test Suite
# ------------------------------------------------ # ------------------------------------------------
add_executable(${TEST_NAME_PREFIX}Environment "Environment.cpp") set(TEST_SOURCES
target_link_libraries(${TEST_NAME_PREFIX}Environment PRIVATE IACore) Main.cpp
target_compile_options(${TEST_NAME_PREFIX}Environment PRIVATE -fexceptions) Utils.cpp
set_target_properties(${TEST_NAME_PREFIX}Environment PROPERTIES USE_EXCEPTIONS ON) Environment.cpp
ProcessOps.cpp
StreamReader.cpp
RingBuffer.cpp
)
# ------------------------------------------------ add_executable(IACore_Test_Suite ${TEST_SOURCES})
# Unit: FileOps
# ------------------------------------------------
#add_executable(${TEST_NAME_PREFIX}FileOps "FileOps.cpp")
#target_link_libraries(${TEST_NAME_PREFIX}FileOps PRIVATE IACore)
#target_compile_options(${TEST_NAME_PREFIX}FileOps PRIVATE -fexceptions)
#set_target_properties(${TEST_NAME_PREFIX}FileOps PROPERTIES USE_EXCEPTIONS ON)
# ------------------------------------------------ # Enable exceptions for testing framework (even if Core is no-except)
# Unit: ProcessOps target_compile_options(IACore_Test_Suite PRIVATE -fexceptions)
# ------------------------------------------------ set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON)
add_executable(${TEST_NAME_PREFIX}ProcessOps "ProcessOps.cpp")
target_link_libraries(${TEST_NAME_PREFIX}ProcessOps PRIVATE IACore)
target_compile_options(${TEST_NAME_PREFIX}ProcessOps PRIVATE -fexceptions)
set_target_properties(${TEST_NAME_PREFIX}ProcessOps PROPERTIES USE_EXCEPTIONS ON)
# ------------------------------------------------ target_link_libraries(IACore_Test_Suite PRIVATE IACore)
# Unit: Utils
# ------------------------------------------------ # Copy necessary runtime assets if you have them (like the LongProcess test subject)
add_executable(${TEST_NAME_PREFIX}Utils "Utils.cpp") add_custom_command(TARGET IACore_Test_Suite POST_BUILD
target_link_libraries(${TEST_NAME_PREFIX}Utils PRIVATE IACore) COMMAND ${CMAKE_COMMAND} -E copy_if_different
target_compile_options(${TEST_NAME_PREFIX}Utils PRIVATE -fexceptions) $<TARGET_FILE:LongProcess>
set_target_properties(${TEST_NAME_PREFIX}Utils PROPERTIES USE_EXCEPTIONS ON) $<TARGET_FILE_DIR:IACore_Test_Suite>/LongProcess${CMAKE_EXECUTABLE_SUFFIX}
)

View File

@ -175,16 +175,4 @@ IAT_BEGIN_BLOCK(Core, Environment)
IAT_END_BLOCK() IAT_END_BLOCK()
int main(int argc, char* argv[]) IAT_REGISTER_ENTRY(Core, Environment)
{
UNUSED(argc);
UNUSED(argv);
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner<false, true> testRunner;
// Run the BinaryReader block
testRunner.testBlock<Core_Environment>();
return 0;
}

View File

@ -1,246 +0,0 @@
// 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/>.
#include <IACore/FileOps.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, File)
// Helper to generate a temp path for testing
String GetTempPath(const String& filename) {
auto path = std::filesystem::temp_directory_path() / filename;
return path.string();
}
// Helper to write raw data for setup (bypassing the class we are testing)
void CreateDummyFile(const String& path, const String& content) {
std::ofstream out(path);
out << content;
out.close();
}
// -------------------------------------------------------------------------
// 1. Path Utilities (Pure Logic)
// -------------------------------------------------------------------------
BOOL TestPathUtils()
{
// We use forward slashes as std::filesystem handles them cross-platform
String complexPath = "assets/textures/hero_diffuse.png";
// Test ExtractDirectory
String dir = File::ExtractDirectory(complexPath);
// Note: On Windows this might return "assets\textures", check specific impl if strict
// But std::filesystem usually normalizes based on the input string format.
IAT_CHECK(dir.find("textures") != String::npos);
// Test ExtractFilename (With Extension)
String nameWithExt = File::ExtractFilename<true>(complexPath);
IAT_CHECK_EQ(nameWithExt, String("hero_diffuse.png"));
// Test ExtractFilename (No Extension / Stem)
String nameNoExt = File::ExtractFilename<false>(complexPath);
IAT_CHECK_EQ(nameNoExt, String("hero_diffuse"));
return TRUE;
}
// -------------------------------------------------------------------------
// 2. Static Read String
// -------------------------------------------------------------------------
BOOL TestStaticReadString()
{
String path = GetTempPath("ia_test_text.txt");
String content = "Hello IA Engine";
// Arrange
CreateDummyFile(path, content);
// Act
auto result = File::ReadToString(path);
// Assert
IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, content);
// Cleanup
std::filesystem::remove(path);
return TRUE;
}
// -------------------------------------------------------------------------
// 3. Static Read Binary (Vector)
// -------------------------------------------------------------------------
BOOL TestStaticReadVector()
{
String path = GetTempPath("ia_test_bin.dat");
// Arrange: Create a binary file manually
std::ofstream out(path, std::ios::binary);
UINT8 rawBytes[] = { 0xDE, 0xAD, 0xBE, 0xEF };
out.write((char*)rawBytes, 4);
out.close();
// Act
auto result = File::ReadToVector(path);
// Assert
IAT_CHECK(result.has_value());
Vector<UINT8>& vec = *result;
IAT_CHECK_EQ(vec.size(), (SIZE_T)4);
IAT_CHECK_EQ(vec[0], 0xDE);
IAT_CHECK_EQ(vec[3], 0xEF);
// Cleanup
std::filesystem::remove(path);
return TRUE;
}
// -------------------------------------------------------------------------
// 4. Instance Write & Read Loop
// -------------------------------------------------------------------------
BOOL TestInstanceWriteRead()
{
String path = GetTempPath("ia_instance_io.bin");
UINT32 flagsWrite = (UINT32)File::EOpenFlags::Write |
(UINT32)File::EOpenFlags::Binary |
(UINT32)File::EOpenFlags::Trunc;
// 1. Write
{
File f;
auto res = f.Open(path, (File::EOpenFlags)flagsWrite);
IAT_CHECK(res.has_value());
IAT_CHECK(f.IsOpen());
UINT32 magic = 12345;
f.Write(&magic, sizeof(magic));
f.Close();
IAT_CHECK_NOT(f.IsOpen());
}
// 2. Read Back
{
UINT32 flagsRead = (UINT32)File::EOpenFlags::Read |
(UINT32)File::EOpenFlags::Binary;
File f(path, (File::EOpenFlags)flagsRead); // Test RAII constructor
IAT_CHECK(f.IsOpen());
UINT32 magicRead = 0;
SIZE_T bytesRead = f.Read(&magicRead, sizeof(magicRead));
IAT_CHECK_EQ(bytesRead, sizeof(UINT32));
IAT_CHECK_EQ(magicRead, (UINT32)12345);
}
std::filesystem::remove(path);
return TRUE;
}
// -------------------------------------------------------------------------
// 5. Append Mode
// -------------------------------------------------------------------------
BOOL TestAppendMode()
{
String path = GetTempPath("ia_append.txt");
UINT32 flagsAppend = (UINT32)File::EOpenFlags::Write | (UINT32)File::EOpenFlags::Append;
// Create initial file
CreateDummyFile(path, "A");
// Open in append mode
File f;
const auto openResult = f.Open(path, (File::EOpenFlags)flagsAppend);
if(!openResult)
{
IA_PANIC(openResult.error().c_str())
return FALSE;
}
char c = 'B';
f.Write(&c, 1);
f.Close();
// Verify content is "AB"
auto content = File::ReadToString(path);
IAT_CHECK(content.has_value());
IAT_CHECK_EQ(*content, String("AB"));
std::filesystem::remove(path);
return TRUE;
}
// -------------------------------------------------------------------------
// 6. Error Handling
// -------------------------------------------------------------------------
BOOL TestNonExistentFile()
{
String ghostPath = GetTempPath("this_does_not_exist.ghost");
// Ensure it really doesn't exist
if(File::Exists(ghostPath)) std::filesystem::remove(ghostPath);
// Test Static
auto resStatic = File::ReadToString(ghostPath);
IAT_CHECK_NOT(resStatic.has_value());
// Optional: Check error message content
// IAT_CHECK(resStatic.error().find("not found") != String::npos);
// Test Instance
File f;
auto resInstance = f.Open(ghostPath, File::EOpenFlags::Read);
IAT_CHECK_NOT(resInstance.has_value());
IAT_CHECK_NOT(f.IsOpen());
return TRUE;
}
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(TestPathUtils);
IAT_ADD_TEST(TestStaticReadString);
IAT_ADD_TEST(TestStaticReadVector);
IAT_ADD_TEST(TestInstanceWriteRead);
IAT_ADD_TEST(TestAppendMode);
IAT_ADD_TEST(TestNonExistentFile);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
int main(int argc, char* argv[])
{
UNUSED(argc);
UNUSED(argv);
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner<false, true> testRunner;
// Run the BinaryReader block
testRunner.testBlock<Core_File>();
return 0;
}

36
Tests/Unit/Main.cpp Normal file
View File

@ -0,0 +1,36 @@
// 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/>.
#include <IACore/IACore.hpp>
#include <IACore/IATest.hpp>
int main(int argc, char *argv[])
{
UNUSED(argc);
UNUSED(argv);
printf(__CC_GREEN "\n===============================================================\n");
printf(" IACore (Independent Architecture Core) - Unit Test Suite\n");
printf("===============================================================\n" __CC_DEFAULT "\n");
IACore::Initialize();
int result = ia::iatest::TestRegistry::RunAll();
IACore::Terminate();
return result;
}

View File

@ -34,7 +34,7 @@ using namespace IACore;
# define NULL_DEVICE "/dev/null" # define NULL_DEVICE "/dev/null"
#endif #endif
IAT_BEGIN_BLOCK(Core, Process) IAT_BEGIN_BLOCK(Core, ProcessOps)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 1. Basic Execution (Exit Code 0) // 1. Basic Execution (Exit Code 0)
@ -45,10 +45,7 @@ IAT_BEGIN_BLOCK(Core, Process)
String captured; String captured;
auto result = ProcessOps::SpawnProcessSync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA", auto result = ProcessOps::SpawnProcessSync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
[&](StringView line) { [&](StringView line) { captured = line; });
captured = line;
}
);
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0); // Exit code 0 IAT_CHECK_EQ(*result, 0); // Exit code 0
@ -71,13 +68,11 @@ IAT_BEGIN_BLOCK(Core, Process)
// Windows: cmd.exe /c echo one two // Windows: cmd.exe /c echo one two
// Linux: /bin/echo one two // Linux: /bin/echo one two
String args = String(CMD_ARG_PREFIX) + " one two"; String args = String(CMD_ARG_PREFIX) + " one two";
if(args[0] == ' ') args.erase(0, 1); // cleanup space if prefix empty if (args[0] == ' ')
args.erase(0, 1); // cleanup space if prefix empty
auto result = ProcessOps::SpawnProcessSync(CMD_ECHO_EXE, args, auto result =
[&](StringView line) { ProcessOps::SpawnProcessSync(CMD_ECHO_EXE, args, [&](StringView line) { lines.push_back(String(line)); });
lines.push_back(String(line));
}
);
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
IAT_CHECK(lines.size() > 0); IAT_CHECK(lines.size() > 0);
@ -152,7 +147,8 @@ IAT_BEGIN_BLOCK(Core, Process)
String massiveString; String massiveString;
massiveString.reserve(5000); massiveString.reserve(5000);
for(int i=0; i<500; ++i) massiveString += "1234567890"; // 5000 chars for (int i = 0; i < 500; ++i)
massiveString += "1234567890"; // 5000 chars
String cmd, arg; String cmd, arg;
@ -166,11 +162,7 @@ IAT_BEGIN_BLOCK(Core, Process)
#endif #endif
String captured; String captured;
auto result = ProcessOps::SpawnProcessSync(cmd, arg, auto result = ProcessOps::SpawnProcessSync(cmd, arg, [&](StringView line) { captured += line; });
[&](StringView line) {
captured += line;
}
);
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
@ -205,8 +197,10 @@ IAT_BEGIN_BLOCK(Core, Process)
UNUSED(ProcessOps::SpawnProcessSync(cmd, arg, [&](StringView line) { UNUSED(ProcessOps::SpawnProcessSync(cmd, arg, [&](StringView line) {
lineCount++; lineCount++;
if (line.find("LineA") != String::npos) foundA = true; if (line.find("LineA") != String::npos)
if (line.find("LineB") != String::npos) foundB = true; foundA = true;
if (line.find("LineB") != String::npos)
foundB = true;
})); }));
IAT_CHECK(foundA); IAT_CHECK(foundA);
@ -232,16 +226,4 @@ IAT_BEGIN_BLOCK(Core, Process)
IAT_END_BLOCK() IAT_END_BLOCK()
int main(int argc, char* argv[]) IAT_REGISTER_ENTRY(Core, ProcessOps)
{
UNUSED(argc);
UNUSED(argv);
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner<false, true> testRunner;
// Run the BinaryReader block
testRunner.testBlock<Core_Process>();
return 0;
}

104
Tests/Unit/RingBuffer.cpp Normal file
View File

@ -0,0 +1,104 @@
// 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/>.
#include <IACore/ADT/RingBuffer.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, RingBuffer)
// 1. Basic Push Pop
BOOL TestPushPop()
{
// Allocate raw memory for the ring buffer
// ControlBlock (128 bytes) + Data
std::vector<UINT8> memory(sizeof(RingBufferView::ControlBlock) + 1024);
// Initialize as OWNER (Producer)
RingBufferView producer(std::span<UINT8>(memory), TRUE);
// Initialize as CONSUMER (Pointer to same memory)
RingBufferView consumer(std::span<UINT8>(memory), FALSE);
// Data to send
String msg = "Hello RingBuffer";
BOOL pushed = producer.Push(1, {(const UINT8 *) msg.data(), msg.size()});
IAT_CHECK(pushed);
// Read back
RingBufferView::PacketHeader header;
UINT8 readBuf[128];
INT32 bytesRead = consumer.Pop(header, std::span<UINT8>(readBuf, 128));
IAT_CHECK_EQ(header.ID, (UINT16) 1);
IAT_CHECK_EQ(bytesRead, (INT32) msg.size());
String readMsg((char *) readBuf, bytesRead);
IAT_CHECK_EQ(readMsg, msg);
return TRUE;
}
// 2. Wrap Around (The hardest logic to get right)
BOOL TestWrapAround()
{
// Small buffer to force wrapping quickly
// Capacity will be 100 bytes
std::vector<UINT8> memory(sizeof(RingBufferView::ControlBlock) + 100);
RingBufferView rb(std::span<UINT8>(memory), TRUE);
// Fill buffer to near end
// Push 80 bytes
std::vector<UINT8> junk(80, 0xFF);
rb.Push(1, junk);
// Pop them to advance READ cursor
RingBufferView::PacketHeader header;
UINT8 outBuf[100];
rb.Pop(header, outBuf);
// Now READ and WRITE are near index 80.
// Pushing 40 bytes should trigger a wrap-around (split write)
std::vector<UINT8> wrapData(40, 0xAA);
BOOL pushed = rb.Push(2, wrapData);
IAT_CHECK(pushed);
// Pop and verify integrity
INT32 popSize = rb.Pop(header, outBuf);
IAT_CHECK_EQ(popSize, 40);
// Check if data is intact
BOOL match = TRUE;
for (int i = 0; i < 40; i++)
{
if (outBuf[i] != 0xAA)
match = FALSE;
}
IAT_CHECK(match);
return TRUE;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(TestPushPop);
IAT_ADD_TEST(TestWrapAround);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, RingBuffer)

182
Tests/Unit/StreamReader.cpp Normal file
View File

@ -0,0 +1,182 @@
// 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/>.
#include <IACore/StreamReader.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, StreamReader)
// -------------------------------------------------------------------------
// 1. Basic Primitive Reading (UINT8)
// -------------------------------------------------------------------------
BOOL TestReadUint8()
{
UINT8 data[] = {0xAA, 0xBB, 0xCC};
StreamReader reader(data);
// Read First Byte
auto val1 = reader.Read<UINT8>();
IAT_CHECK(val1.has_value());
IAT_CHECK_EQ(*val1, 0xAA);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 1);
// Read Second Byte
auto val2 = reader.Read<UINT8>();
IAT_CHECK_EQ(*val2, 0xBB);
return TRUE;
}
// -------------------------------------------------------------------------
// 2. Multi-byte Reading (Endianness check)
// -------------------------------------------------------------------------
BOOL TestReadMultiByte()
{
// 0x04030201 in Little Endian memory layout
UINT8 data[] = {0x01, 0x02, 0x03, 0x04};
StreamReader reader(data);
auto val = reader.Read<UINT32>();
IAT_CHECK(val.has_value());
// Assuming standard x86/ARM Little Endian for this test
// If your engine supports Big Endian, you'd check architecture here
IAT_CHECK_EQ(*val, (UINT32) 0x04030201);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 4);
IAT_CHECK(reader.IsEOF());
return TRUE;
}
// -------------------------------------------------------------------------
// 3. Floating Point (Approx check)
// -------------------------------------------------------------------------
BOOL TestReadFloat()
{
FLOAT32 pi = 3.14159f;
// Bit-cast float to bytes for setup
UINT8 data[4];
std::memcpy(data, &pi, 4);
StreamReader reader(data);
auto val = reader.Read<FLOAT32>();
IAT_CHECK(val.has_value());
IAT_CHECK_APPROX(*val, pi);
return TRUE;
}
// -------------------------------------------------------------------------
// 4. Batch Buffer Reading
// -------------------------------------------------------------------------
BOOL TestReadBuffer()
{
UINT8 src[] = {1, 2, 3, 4, 5};
UINT8 dst[3] = {0};
StreamReader reader(src);
// Read 3 bytes into dst
auto res = reader.Read(dst, 3);
IAT_CHECK(res.has_value());
// Verify dst content
IAT_CHECK_EQ(dst[0], 1);
IAT_CHECK_EQ(dst[1], 2);
IAT_CHECK_EQ(dst[2], 3);
// Verify cursor
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 3);
return TRUE;
}
// -------------------------------------------------------------------------
// 5. Navigation (Seek, Skip, Remaining)
// -------------------------------------------------------------------------
BOOL TestNavigation()
{
UINT8 data[10] = {0}; // Zero init
StreamReader reader(data);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T) 10);
// Skip
reader.Skip(5);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 5);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T) 5);
// Skip clamping
reader.Skip(100); // Should clamp to 10
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 10);
IAT_CHECK(reader.IsEOF());
// Seek
reader.Seek(2);
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 2);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T) 8);
IAT_CHECK_NOT(reader.IsEOF());
return TRUE;
}
// -------------------------------------------------------------------------
// 6. Error Handling (EOF Protection)
// -------------------------------------------------------------------------
BOOL TestBoundaryChecks()
{
UINT8 data[] = {0x00, 0x00};
StreamReader reader(data);
// Valid read
UNUSED(reader.Read<UINT16>());
IAT_CHECK(reader.IsEOF());
// Invalid Read Primitive
auto val = reader.Read<UINT8>();
IAT_CHECK_NOT(val.has_value()); // Should be unexpected
// Invalid Batch Read
UINT8 buf[1];
auto batch = reader.Read(buf, 1);
IAT_CHECK_NOT(batch.has_value());
return TRUE;
}
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(TestReadUint8);
IAT_ADD_TEST(TestReadMultiByte);
IAT_ADD_TEST(TestReadFloat);
IAT_ADD_TEST(TestReadBuffer);
IAT_ADD_TEST(TestNavigation);
IAT_ADD_TEST(TestBoundaryChecks);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, StreamReader)

View File

@ -24,12 +24,14 @@ using namespace IACore;
// Test Structs for Hashing (Must be defined at Global Scope) // Test Structs for Hashing (Must be defined at Global Scope)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
struct TestVec3 { struct TestVec3
{
FLOAT32 x, y, z; FLOAT32 x, y, z;
// Equality operator required for hash maps, though strictly // Equality operator required for hash maps, though strictly
// the hash function itself doesn't need it, it's good practice to test both. // the hash function itself doesn't need it, it's good practice to test both.
bool operator==(const TestVec3& other) const { bool operator==(const TestVec3 &other) const
{
return x == other.x && y == other.y && z == other.z; return x == other.x && y == other.y && z == other.z;
} }
}; };
@ -38,7 +40,6 @@ struct TestVec3 {
// This proves the macro works structurally // This proves the macro works structurally
IA_MAKE_HASHABLE(TestVec3, &TestVec3::x, &TestVec3::y, &TestVec3::z); IA_MAKE_HASHABLE(TestVec3, &TestVec3::x, &TestVec3::y, &TestVec3::z);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Test Block Definition // Test Block Definition
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -225,16 +226,4 @@ IAT_BEGIN_BLOCK(Core, Utils)
IAT_END_BLOCK() IAT_END_BLOCK()
int main(int argc, char* argv[]) IAT_REGISTER_ENTRY(Core, Utils)
{
UNUSED(argc);
UNUSED(argv);
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner<false, true> testRunner;
// Run the BinaryReader block
testRunner.testBlock<Core_Utils>();
return 0;
}

View File

@ -1,26 +0,0 @@
#!/bin/bash
# IACore-OSS; The Core Library for All IA Open Source Projects
# Copyright (C) 20245IAS (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/>.
import os
import sys
def main(args: list[str]):
os.system("cmake -S. -B./Build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug")
os.system("cmake --build ./Build")
main(sys.argv)