Clean Tests
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -47,4 +47,5 @@
|
|||||||
|
|
||||||
.cache/
|
.cache/
|
||||||
.local/
|
.local/
|
||||||
Build/
|
[Bb]uild
|
||||||
|
out/
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"C_Cpp.clang_format_fallbackStyle": "Microsoft"
|
||||||
|
}
|
||||||
16
.vscode/tasks.json
vendored
16
.vscode/tasks.json
vendored
@ -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"
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
],
|
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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
108
CMakePresets.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
94
README.md
94
README.md
@ -1,2 +1,94 @@
|
|||||||
# IACore
|
# IACore (Independent Architecture Core)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
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).
|
||||||
@ -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)
|
||||||
|
|
||||||
|
|||||||
@ -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{};
|
||||||
|
|||||||
@ -20,63 +20,86 @@
|
|||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
#include <exception>
|
# include <exception>
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Macros
|
// 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
|
// 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))
|
||||||
#define IAT_CHECK_EQ(lhs, rhs) __iat_micro_test(_test_eq((lhs), (rhs), #lhs " == " #rhs))
|
# 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_NEQ(lhs, rhs) __iat_micro_test(_test_neq((lhs), (rhs), #lhs " != " #rhs))
|
||||||
|
|
||||||
// Float specific checks (Game dev essential)
|
// 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_UNIT(func) _test_unit([this]() { return this->func(); }, #func)
|
||||||
#define IAT_NAMED_UNIT(n, func) _test_unit([this](){ return this->func(); }, n)
|
# 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
|
// 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() \
|
||||||
#define IAT_ADD_TEST(name) IAT_UNIT(name)
|
public: \
|
||||||
#define IAT_END_TEST_LIST() } private:
|
VOID declareTests() OVERRIDE \
|
||||||
|
{
|
||||||
|
# define IAT_ADD_TEST(name) IAT_UNIT(name)
|
||||||
|
# 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>
|
||||||
@ -191,9 +226,9 @@ namespace ia::iatest
|
|||||||
VOID summarize();
|
VOID summarize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SIZE_T m_testCount { 0 };
|
SIZE_T m_testCount{0};
|
||||||
SIZE_T m_failCount { 0 };
|
SIZE_T m_failCount{0};
|
||||||
SIZE_T m_blockCount { 0 };
|
SIZE_T m_blockCount{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<BOOL stopOnFail, BOOL isVerbose>
|
template<BOOL stopOnFail, BOOL isVerbose>
|
||||||
@ -207,55 +242,117 @@ namespace ia::iatest
|
|||||||
|
|
||||||
printf(__CC_MAGENTA "Testing [%s]..." __CC_DEFAULT "\n", b.name());
|
printf(__CC_MAGENTA "Testing [%s]..." __CC_DEFAULT "\n", b.name());
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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}
|
||||||
|
)
|
||||||
@ -25,8 +25,8 @@ using namespace IACore;
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// We use a unique prefix to ensure we don't accidentally mess with real
|
// We use a unique prefix to ensure we don't accidentally mess with real
|
||||||
// system variables like PATH or HOME.
|
// system variables like PATH or HOME.
|
||||||
static const char* TEST_KEY = "IA_TEST_ENV_VAR_12345";
|
static const char *TEST_KEY = "IA_TEST_ENV_VAR_12345";
|
||||||
static const char* TEST_VAL = "Hello World";
|
static const char *TEST_VAL = "Hello World";
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Test Block Definition
|
// Test Block Definition
|
||||||
@ -34,11 +34,11 @@ static const char* TEST_VAL = "Hello World";
|
|||||||
|
|
||||||
IAT_BEGIN_BLOCK(Core, Environment)
|
IAT_BEGIN_BLOCK(Core, Environment)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 1. Basic Set and Get (The Happy Path)
|
// 1. Basic Set and Get (The Happy Path)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestBasicCycle()
|
BOOL TestBasicCycle()
|
||||||
{
|
{
|
||||||
// 1. Ensure clean slate
|
// 1. Ensure clean slate
|
||||||
Environment::Unset(TEST_KEY);
|
Environment::Unset(TEST_KEY);
|
||||||
IAT_CHECK_NOT(Environment::Exists(TEST_KEY));
|
IAT_CHECK_NOT(Environment::Exists(TEST_KEY));
|
||||||
@ -60,13 +60,13 @@ IAT_BEGIN_BLOCK(Core, Environment)
|
|||||||
// Cleanup
|
// Cleanup
|
||||||
Environment::Unset(TEST_KEY);
|
Environment::Unset(TEST_KEY);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 2. Overwriting Values
|
// 2. Overwriting Values
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestOverwrite()
|
BOOL TestOverwrite()
|
||||||
{
|
{
|
||||||
Environment::Set(TEST_KEY, "ValueA");
|
Environment::Set(TEST_KEY, "ValueA");
|
||||||
IAT_CHECK_EQ(Environment::Get(TEST_KEY), String("ValueA"));
|
IAT_CHECK_EQ(Environment::Get(TEST_KEY), String("ValueA"));
|
||||||
|
|
||||||
@ -76,13 +76,13 @@ IAT_BEGIN_BLOCK(Core, Environment)
|
|||||||
|
|
||||||
Environment::Unset(TEST_KEY);
|
Environment::Unset(TEST_KEY);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 3. Unset Logic
|
// 3. Unset Logic
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestUnset()
|
BOOL TestUnset()
|
||||||
{
|
{
|
||||||
Environment::Set(TEST_KEY, "To Be Deleted");
|
Environment::Set(TEST_KEY, "To Be Deleted");
|
||||||
IAT_CHECK(Environment::Exists(TEST_KEY));
|
IAT_CHECK(Environment::Exists(TEST_KEY));
|
||||||
|
|
||||||
@ -97,14 +97,14 @@ IAT_BEGIN_BLOCK(Core, Environment)
|
|||||||
IAT_CHECK_NOT(opt.has_value());
|
IAT_CHECK_NOT(opt.has_value());
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 4. Default Value Fallbacks
|
// 4. Default Value Fallbacks
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestDefaults()
|
BOOL TestDefaults()
|
||||||
{
|
{
|
||||||
const char* ghostKey = "IA_THIS_KEY_DOES_NOT_EXIST";
|
const char *ghostKey = "IA_THIS_KEY_DOES_NOT_EXIST";
|
||||||
|
|
||||||
// Ensure it really doesn't exist
|
// Ensure it really doesn't exist
|
||||||
Environment::Unset(ghostKey);
|
Environment::Unset(ghostKey);
|
||||||
@ -118,16 +118,16 @@ IAT_BEGIN_BLOCK(Core, Environment)
|
|||||||
IAT_CHECK_EQ(fallback, String("MyDefault"));
|
IAT_CHECK_EQ(fallback, String("MyDefault"));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 5. Empty Strings vs Null/Unset
|
// 5. Empty Strings vs Null/Unset
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// This is a critical edge case.
|
// This is a critical edge case.
|
||||||
// Does Set(Key, "") create an existing empty variable, or unset it?
|
// Does Set(Key, "") create an existing empty variable, or unset it?
|
||||||
// Standard POSIX/Windows API behavior is that it EXISTS, but is empty.
|
// Standard POSIX/Windows API behavior is that it EXISTS, but is empty.
|
||||||
BOOL TestEmptyValue()
|
BOOL TestEmptyValue()
|
||||||
{
|
{
|
||||||
Environment::Set(TEST_KEY, "");
|
Environment::Set(TEST_KEY, "");
|
||||||
|
|
||||||
// It should exist
|
// It should exist
|
||||||
@ -143,13 +143,13 @@ IAT_BEGIN_BLOCK(Core, Environment)
|
|||||||
IAT_CHECK_NOT(Environment::Exists(TEST_KEY));
|
IAT_CHECK_NOT(Environment::Exists(TEST_KEY));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 6. Validation / Bad Input
|
// 6. Validation / Bad Input
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestBadInput()
|
BOOL TestBadInput()
|
||||||
{
|
{
|
||||||
// Setting an empty key should fail gracefully
|
// Setting an empty key should fail gracefully
|
||||||
BOOL res = Environment::Set("", "Value");
|
BOOL res = Environment::Set("", "Value");
|
||||||
IAT_CHECK_NOT(res);
|
IAT_CHECK_NOT(res);
|
||||||
@ -159,32 +159,20 @@ IAT_BEGIN_BLOCK(Core, Environment)
|
|||||||
IAT_CHECK_NOT(resUnset);
|
IAT_CHECK_NOT(resUnset);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Registration
|
// Registration
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
IAT_BEGIN_TEST_LIST()
|
IAT_BEGIN_TEST_LIST()
|
||||||
IAT_ADD_TEST(TestBasicCycle);
|
IAT_ADD_TEST(TestBasicCycle);
|
||||||
IAT_ADD_TEST(TestOverwrite);
|
IAT_ADD_TEST(TestOverwrite);
|
||||||
IAT_ADD_TEST(TestUnset);
|
IAT_ADD_TEST(TestUnset);
|
||||||
IAT_ADD_TEST(TestDefaults);
|
IAT_ADD_TEST(TestDefaults);
|
||||||
IAT_ADD_TEST(TestEmptyValue);
|
IAT_ADD_TEST(TestEmptyValue);
|
||||||
IAT_ADD_TEST(TestBadInput);
|
IAT_ADD_TEST(TestBadInput);
|
||||||
IAT_END_TEST_LIST()
|
IAT_END_TEST_LIST()
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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
36
Tests/Unit/Main.cpp
Normal 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;
|
||||||
|
}
|
||||||
@ -24,31 +24,28 @@ using namespace IACore;
|
|||||||
// Platform Abstraction for Test Commands
|
// Platform Abstraction for Test Commands
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
#define CMD_ECHO_EXE "cmd.exe"
|
# define CMD_ECHO_EXE "cmd.exe"
|
||||||
// Windows requires /c to run a command string
|
// Windows requires /c to run a command string
|
||||||
#define CMD_ARG_PREFIX "/c"
|
# define CMD_ARG_PREFIX "/c"
|
||||||
#define NULL_DEVICE "NUL"
|
# define NULL_DEVICE "NUL"
|
||||||
#else
|
#else
|
||||||
#define CMD_ECHO_EXE "/bin/echo"
|
# define CMD_ECHO_EXE "/bin/echo"
|
||||||
#define CMD_ARG_PREFIX ""
|
# define CMD_ARG_PREFIX ""
|
||||||
#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)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestBasicRun()
|
BOOL TestBasicRun()
|
||||||
{
|
{
|
||||||
// Simple "echo hello"
|
// Simple "echo hello"
|
||||||
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
|
||||||
@ -58,26 +55,24 @@ IAT_BEGIN_BLOCK(Core, Process)
|
|||||||
IAT_CHECK(captured.find("HelloIA") != String::npos);
|
IAT_CHECK(captured.find("HelloIA") != String::npos);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 2. Argument Parsing
|
// 2. Argument Parsing
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestArguments()
|
BOOL TestArguments()
|
||||||
{
|
{
|
||||||
Vector<String> lines;
|
Vector<String> lines;
|
||||||
|
|
||||||
// Echo two distinct words.
|
// Echo two distinct words.
|
||||||
// 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);
|
||||||
@ -86,62 +81,62 @@ IAT_BEGIN_BLOCK(Core, Process)
|
|||||||
IAT_CHECK(lines[0].find("one two") != String::npos);
|
IAT_CHECK(lines[0].find("one two") != String::npos);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 3. Error / Non-Zero Exit Codes
|
// 3. Error / Non-Zero Exit Codes
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestExitCodes()
|
BOOL TestExitCodes()
|
||||||
{
|
{
|
||||||
// We need a command that returns non-zero.
|
// We need a command that returns non-zero.
|
||||||
// Windows: cmd /c exit 1
|
// Windows: cmd /c exit 1
|
||||||
// Linux: /bin/sh -c "exit 1"
|
// Linux: /bin/sh -c "exit 1"
|
||||||
|
|
||||||
String cmd, arg;
|
String cmd, arg;
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
cmd = "cmd.exe";
|
cmd = "cmd.exe";
|
||||||
arg = "/c exit 42";
|
arg = "/c exit 42";
|
||||||
#else
|
#else
|
||||||
cmd = "/bin/sh";
|
cmd = "/bin/sh";
|
||||||
arg = "-c \"exit 42\""; // quotes needed for sh -c
|
arg = "-c \"exit 42\""; // quotes needed for sh -c
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto result = ProcessOps::SpawnProcessSync(cmd, arg, [](StringView){});
|
auto result = ProcessOps::SpawnProcessSync(cmd, arg, [](StringView) {});
|
||||||
|
|
||||||
IAT_CHECK(result.has_value());
|
IAT_CHECK(result.has_value());
|
||||||
IAT_CHECK_EQ(*result, 42);
|
IAT_CHECK_EQ(*result, 42);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 4. Missing Executable Handling
|
// 4. Missing Executable Handling
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestMissingExe()
|
BOOL TestMissingExe()
|
||||||
{
|
{
|
||||||
// Try to run a random string
|
// Try to run a random string
|
||||||
auto result = ProcessOps::SpawnProcessSync("sdflkjghsdflkjg", "", [](StringView){});
|
auto result = ProcessOps::SpawnProcessSync("sdflkjghsdflkjg", "", [](StringView) {});
|
||||||
|
|
||||||
// Windows: CreateProcess usually fails -> returns unexpected
|
// Windows: CreateProcess usually fails -> returns unexpected
|
||||||
// Linux: execvp fails inside child, returns 127 via waitpid
|
// Linux: execvp fails inside child, returns 127 via waitpid
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
IAT_CHECK_NOT(result.has_value()); // Should be an error string
|
IAT_CHECK_NOT(result.has_value()); // Should be an error string
|
||||||
#else
|
#else
|
||||||
// Linux fork succeeds, but execvp fails, returning 127
|
// Linux fork succeeds, but execvp fails, returning 127
|
||||||
IAT_CHECK(result.has_value());
|
IAT_CHECK(result.has_value());
|
||||||
IAT_CHECK_EQ(*result, 127);
|
IAT_CHECK_EQ(*result, 127);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 5. Line Buffer Logic (The 4096 split test)
|
// 5. Line Buffer Logic (The 4096 split test)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestLargeOutput()
|
BOOL TestLargeOutput()
|
||||||
{
|
{
|
||||||
// We need to generate output larger than the internal 4096 buffer
|
// We need to generate output larger than the internal 4096 buffer
|
||||||
// to ensure the "partial line" logic works when a line crosses a buffer boundary.
|
// to ensure the "partial line" logic works when a line crosses a buffer boundary.
|
||||||
|
|
||||||
@ -152,25 +147,22 @@ 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;
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
cmd = "cmd.exe";
|
cmd = "cmd.exe";
|
||||||
// Windows has command line length limits (~8k), 5k is safe.
|
// Windows has command line length limits (~8k), 5k is safe.
|
||||||
arg = "/c echo " + massiveString;
|
arg = "/c echo " + massiveString;
|
||||||
#else
|
#else
|
||||||
cmd = "/bin/echo";
|
cmd = "/bin/echo";
|
||||||
arg = massiveString;
|
arg = massiveString;
|
||||||
#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);
|
||||||
@ -180,24 +172,24 @@ IAT_BEGIN_BLOCK(Core, Process)
|
|||||||
IAT_CHECK_EQ(captured.length(), massiveString.length());
|
IAT_CHECK_EQ(captured.length(), massiveString.length());
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 6. Multi-Line Handling
|
// 6. Multi-Line Handling
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestMultiLine()
|
BOOL TestMultiLine()
|
||||||
{
|
{
|
||||||
// Windows: cmd /c "echo A && echo B"
|
// Windows: cmd /c "echo A && echo B"
|
||||||
// Linux: /bin/sh -c "echo A; echo B"
|
// Linux: /bin/sh -c "echo A; echo B"
|
||||||
|
|
||||||
String cmd, arg;
|
String cmd, arg;
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
cmd = "cmd.exe";
|
cmd = "cmd.exe";
|
||||||
arg = "/c \"echo LineA && echo LineB\"";
|
arg = "/c \"echo LineA && echo LineB\"";
|
||||||
#else
|
#else
|
||||||
cmd = "/bin/sh";
|
cmd = "/bin/sh";
|
||||||
arg = "-c \"echo LineA; echo LineB\"";
|
arg = "-c \"echo LineA; echo LineB\"";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int lineCount = 0;
|
int lineCount = 0;
|
||||||
bool foundA = false;
|
bool foundA = false;
|
||||||
@ -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);
|
||||||
@ -216,32 +210,20 @@ IAT_BEGIN_BLOCK(Core, Process)
|
|||||||
IAT_CHECK(lineCount >= 2);
|
IAT_CHECK(lineCount >= 2);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Registration
|
// Registration
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
IAT_BEGIN_TEST_LIST()
|
IAT_BEGIN_TEST_LIST()
|
||||||
IAT_ADD_TEST(TestBasicRun);
|
IAT_ADD_TEST(TestBasicRun);
|
||||||
IAT_ADD_TEST(TestArguments);
|
IAT_ADD_TEST(TestArguments);
|
||||||
IAT_ADD_TEST(TestExitCodes);
|
IAT_ADD_TEST(TestExitCodes);
|
||||||
IAT_ADD_TEST(TestMissingExe);
|
IAT_ADD_TEST(TestMissingExe);
|
||||||
IAT_ADD_TEST(TestLargeOutput);
|
IAT_ADD_TEST(TestLargeOutput);
|
||||||
IAT_ADD_TEST(TestMultiLine);
|
IAT_ADD_TEST(TestMultiLine);
|
||||||
IAT_END_TEST_LIST()
|
IAT_END_TEST_LIST()
|
||||||
|
|
||||||
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
104
Tests/Unit/RingBuffer.cpp
Normal 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
182
Tests/Unit/StreamReader.cpp
Normal 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)
|
||||||
@ -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,20 +40,19 @@ 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
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
IAT_BEGIN_BLOCK(Core, Utils)
|
IAT_BEGIN_BLOCK(Core, Utils)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 1. Binary <-> Hex String Conversion
|
// 1. Binary <-> Hex String Conversion
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestHexConversion()
|
BOOL TestHexConversion()
|
||||||
{
|
{
|
||||||
// A. Binary To Hex
|
// A. Binary To Hex
|
||||||
UINT8 bin[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF };
|
UINT8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
|
||||||
String hex = IACore::Utils::BinaryToHexString(bin);
|
String hex = IACore::Utils::BinaryToHexString(bin);
|
||||||
|
|
||||||
IAT_CHECK_EQ(hex, String("DEADBEEF00FF"));
|
IAT_CHECK_EQ(hex, String("DEADBEEF00FF"));
|
||||||
@ -59,7 +60,7 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
// B. Hex To Binary (Valid Upper)
|
// B. Hex To Binary (Valid Upper)
|
||||||
auto resUpper = IACore::Utils::HexStringToBinary("DEADBEEF00FF");
|
auto resUpper = IACore::Utils::HexStringToBinary("DEADBEEF00FF");
|
||||||
IAT_CHECK(resUpper.has_value());
|
IAT_CHECK(resUpper.has_value());
|
||||||
IAT_CHECK_EQ(resUpper->size(), (SIZE_T)6);
|
IAT_CHECK_EQ(resUpper->size(), (SIZE_T) 6);
|
||||||
IAT_CHECK_EQ((*resUpper)[0], 0xDE);
|
IAT_CHECK_EQ((*resUpper)[0], 0xDE);
|
||||||
IAT_CHECK_EQ((*resUpper)[5], 0xFF);
|
IAT_CHECK_EQ((*resUpper)[5], 0xFF);
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
IAT_CHECK_EQ((*resLower)[0], 0xDE);
|
IAT_CHECK_EQ((*resLower)[0], 0xDE);
|
||||||
|
|
||||||
// D. Round Trip Integrity
|
// D. Round Trip Integrity
|
||||||
Vector<UINT8> original = { 1, 2, 3, 4, 5 };
|
Vector<UINT8> original = {1, 2, 3, 4, 5};
|
||||||
String s = IACore::Utils::BinaryToHexString(original);
|
String s = IACore::Utils::BinaryToHexString(original);
|
||||||
auto back = IACore::Utils::HexStringToBinary(s);
|
auto back = IACore::Utils::HexStringToBinary(s);
|
||||||
IAT_CHECK(back.has_value());
|
IAT_CHECK(back.has_value());
|
||||||
@ -77,13 +78,13 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
IAT_CHECK_EQ(original[2], (*back)[2]);
|
IAT_CHECK_EQ(original[2], (*back)[2]);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 2. Hex Error Handling
|
// 2. Hex Error Handling
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestHexErrors()
|
BOOL TestHexErrors()
|
||||||
{
|
{
|
||||||
// Odd Length
|
// Odd Length
|
||||||
auto odd = IACore::Utils::HexStringToBinary("ABC");
|
auto odd = IACore::Utils::HexStringToBinary("ABC");
|
||||||
IAT_CHECK_NOT(odd.has_value());
|
IAT_CHECK_NOT(odd.has_value());
|
||||||
@ -96,17 +97,17 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
// Empty string is valid (empty vector)
|
// Empty string is valid (empty vector)
|
||||||
auto empty = IACore::Utils::HexStringToBinary("");
|
auto empty = IACore::Utils::HexStringToBinary("");
|
||||||
IAT_CHECK(empty.has_value());
|
IAT_CHECK(empty.has_value());
|
||||||
IAT_CHECK_EQ(empty->size(), (SIZE_T)0);
|
IAT_CHECK_EQ(empty->size(), (SIZE_T) 0);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 3. Algorithms: Sorting
|
// 3. Algorithms: Sorting
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestSort()
|
BOOL TestSort()
|
||||||
{
|
{
|
||||||
Vector<int> nums = { 5, 1, 4, 2, 3 };
|
Vector<int> nums = {5, 1, 4, 2, 3};
|
||||||
|
|
||||||
IACore::Utils::Sort(nums);
|
IACore::Utils::Sort(nums);
|
||||||
|
|
||||||
@ -117,15 +118,15 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
IAT_CHECK_EQ(nums[4], 5);
|
IAT_CHECK_EQ(nums[4], 5);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 4. Algorithms: Binary Search (Left/Right)
|
// 4. Algorithms: Binary Search (Left/Right)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestBinarySearch()
|
BOOL TestBinarySearch()
|
||||||
{
|
{
|
||||||
// Must be sorted for Binary Search
|
// Must be sorted for Binary Search
|
||||||
Vector<int> nums = { 10, 20, 20, 20, 30 };
|
Vector<int> nums = {10, 20, 20, 20, 30};
|
||||||
|
|
||||||
// Search Left (Lower Bound) -> First element >= value
|
// Search Left (Lower Bound) -> First element >= value
|
||||||
auto itLeft = IACore::Utils::BinarySearchLeft(nums, 20);
|
auto itLeft = IACore::Utils::BinarySearchLeft(nums, 20);
|
||||||
@ -144,13 +145,13 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
IAT_CHECK(itFail == nums.end());
|
IAT_CHECK(itFail == nums.end());
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 5. Hashing Basics
|
// 5. Hashing Basics
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestHashBasics()
|
BOOL TestHashBasics()
|
||||||
{
|
{
|
||||||
UINT64 h1 = IACore::Utils::ComputeHash(10, 20.5f, "Hello");
|
UINT64 h1 = IACore::Utils::ComputeHash(10, 20.5f, "Hello");
|
||||||
UINT64 h2 = IACore::Utils::ComputeHash(10, 20.5f, "Hello");
|
UINT64 h2 = IACore::Utils::ComputeHash(10, 20.5f, "Hello");
|
||||||
UINT64 h3 = IACore::Utils::ComputeHash(10, 20.5f, "World");
|
UINT64 h3 = IACore::Utils::ComputeHash(10, 20.5f, "World");
|
||||||
@ -168,16 +169,16 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
IAT_CHECK_NEQ(orderA, orderB);
|
IAT_CHECK_NEQ(orderA, orderB);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 6. Macro Verification (IA_MAKE_HASHABLE)
|
// 6. Macro Verification (IA_MAKE_HASHABLE)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestHashMacro()
|
BOOL TestHashMacro()
|
||||||
{
|
{
|
||||||
TestVec3 v1 { 1.0f, 2.0f, 3.0f };
|
TestVec3 v1{1.0f, 2.0f, 3.0f};
|
||||||
TestVec3 v2 { 1.0f, 2.0f, 3.0f };
|
TestVec3 v2{1.0f, 2.0f, 3.0f};
|
||||||
TestVec3 v3 { 1.0f, 2.0f, 4.0f }; // Slight change
|
TestVec3 v3{1.0f, 2.0f, 4.0f}; // Slight change
|
||||||
|
|
||||||
// Instantiate the hasher manually to verify the struct specialization exists
|
// Instantiate the hasher manually to verify the struct specialization exists
|
||||||
ankerl::unordered_dense::hash<TestVec3> hasher;
|
ankerl::unordered_dense::hash<TestVec3> hasher;
|
||||||
@ -209,32 +210,20 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
IAT_CHECK_NEQ(h1, hWrapper);
|
IAT_CHECK_NEQ(h1, hWrapper);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Registration
|
// Registration
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
IAT_BEGIN_TEST_LIST()
|
IAT_BEGIN_TEST_LIST()
|
||||||
IAT_ADD_TEST(TestHexConversion);
|
IAT_ADD_TEST(TestHexConversion);
|
||||||
IAT_ADD_TEST(TestHexErrors);
|
IAT_ADD_TEST(TestHexErrors);
|
||||||
IAT_ADD_TEST(TestSort);
|
IAT_ADD_TEST(TestSort);
|
||||||
IAT_ADD_TEST(TestBinarySearch);
|
IAT_ADD_TEST(TestBinarySearch);
|
||||||
IAT_ADD_TEST(TestHashBasics);
|
IAT_ADD_TEST(TestHashBasics);
|
||||||
IAT_ADD_TEST(TestHashMacro);
|
IAT_ADD_TEST(TestHashMacro);
|
||||||
IAT_END_TEST_LIST()
|
IAT_END_TEST_LIST()
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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)
|
|
||||||
Reference in New Issue
Block a user