Fixes
This commit is contained in:
@ -23,8 +23,6 @@ message(STATUS "Configured IACore for Multi-Config (Debug/Release rules generate
|
|||||||
message(STATUS "Detected Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
|
message(STATUS "Detected Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
|
||||||
# Check if the compiler is MSVC (cl.exe), but allow Clang acting like MSVC (clang-cl)
|
# Check if the compiler is MSVC (cl.exe), but allow Clang acting like MSVC (clang-cl)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
# Note: clang-cl usually reports itself as "Clang" in newer CMake versions,
|
|
||||||
# but if it reports MSVC with a Clang simulation, we want to allow it.
|
|
||||||
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"\n\n"
|
"\n\n"
|
||||||
@ -40,7 +38,6 @@ endif()
|
|||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Wall -Wextra -Wpedantic
|
-Wall -Wextra -Wpedantic
|
||||||
# Suppress warning for the statement expression macro if -pedantic is on
|
|
||||||
-Wno-language-extension-token
|
-Wno-language-extension-token
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
39
README.md
39
README.md
@ -130,6 +130,45 @@ if (res) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
We welcome contributions from the community! However, to maintain the architectural integrity and licensing flexibility of the project, we have specific guidelines for Pull Requests.
|
||||||
|
|
||||||
|
### What we accept immediately:
|
||||||
|
* **📚 Documentation:** Improvements to comments, the README, or external docs.
|
||||||
|
* **🧪 Tests:** New unit tests (in `Tests/`) to improve coverage or reproduce bugs.
|
||||||
|
* **💡 Examples:** New usage examples or sample projects.
|
||||||
|
* **🐛 Bug Reports:** detailed issues describing reproduction steps are highly valued.
|
||||||
|
|
||||||
|
### Core Library Policy (`Src/` Directory)
|
||||||
|
Currently, **we are not accepting Pull Requests that modify the core source code (`Src/`)**.
|
||||||
|
|
||||||
|
**Why?**
|
||||||
|
IACore is a dual-licensed product. To offer commercial licenses to proprietary software vendors in the future, **IASoft (PVT) LTD.** must retain 100% copyright ownership of the core library.
|
||||||
|
|
||||||
|
We are currently establishing a **Contributor License Agreement (CLA)** process. Once that is in place, we will open the core library for contributions, provided the contributor signs the CLA to assign copyright or grant an unlimited license to the project maintainers.
|
||||||
|
|
||||||
|
If you find a critical bug in `Src/`, please open an **Issue** rather than a PR, and the core team will implement the fix to ensure legal compliance.
|
||||||
|
|
||||||
|
## 🤝 Credits & Acknowledgements
|
||||||
|
**IACore** is an architectural effort by **IASoft (PVT) LTD.**, designed and maintained by its lead developers.
|
||||||
|
|
||||||
|
While the core architecture, API design, and final logic verification were strictly human-led, this project leveraged **Google Gemini 3** as an advanced AI thought partner to accelerate development.
|
||||||
|
|
||||||
|
#### AI contributions include:
|
||||||
|
|
||||||
|
* **Boilerplate Generation:** Rapid prototyping of standard C++20 structures and repetitive implementations.
|
||||||
|
|
||||||
|
* **Research & Selection:** Analyzing and comparing open-source libraries (e.g., `glaze` vs. `nlohmann`, `zstd` vs. `lz4`) to select the best-in-class dependencies.
|
||||||
|
|
||||||
|
* **Design Analysis:** Researching and contrasting design patterns (e.g., SPSC Ring Buffers vs. Mutex Queues) for optimal performance.
|
||||||
|
|
||||||
|
* **Documentation:** Drafting comprehensive documentation and this README.
|
||||||
|
|
||||||
|
* **Code Review:** Automated logic checking and static analysis support.
|
||||||
|
|
||||||
|
**Methodology:** Every line of code, whether written by hand or generated by AI, has been manually reviewed, tested, and verified by human engineers to ensure zero hallucinations and maximum reliability. *Trust, but verify.*
|
||||||
|
|
||||||
## ⚖️ License
|
## ⚖️ License
|
||||||
|
|
||||||
This project is licensed under the GNU General Public License v3 (GPLv3).
|
This project is licensed under the GNU General Public License v3 (GPLv3).
|
||||||
@ -111,7 +111,6 @@ namespace IACore
|
|||||||
|
|
||||||
// Check for ZLIB Magic Number (starts with 0x78)
|
// Check for ZLIB Magic Number (starts with 0x78)
|
||||||
// 0x78 = Deflate compression with 32k window size
|
// 0x78 = Deflate compression with 32k window size
|
||||||
// Valid second bytes: 0x01 (Fastest), 0x9C (Default), 0xDA (Best)
|
|
||||||
if (data[0] == 0x78 && (data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA))
|
if (data[0] == 0x78 && (data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA))
|
||||||
return CompressionType::Zlib;
|
return CompressionType::Zlib;
|
||||||
|
|
||||||
@ -134,7 +133,6 @@ namespace IACore
|
|||||||
|
|
||||||
Vector<UINT8> outBuffer;
|
Vector<UINT8> outBuffer;
|
||||||
// Start with 2x input size.
|
// Start with 2x input size.
|
||||||
// Small packets compress well, so maybe 4x for very small inputs.
|
|
||||||
size_t guessSize = data.size() < 1024 ? data.size() * 4 : data.size() * 2;
|
size_t guessSize = data.size() < 1024 ? data.size() * 4 : data.size() * 2;
|
||||||
outBuffer.resize(guessSize);
|
outBuffer.resize(guessSize);
|
||||||
|
|
||||||
@ -282,7 +280,7 @@ namespace IACore
|
|||||||
zs.zfree = Z_NULL;
|
zs.zfree = Z_NULL;
|
||||||
zs.opaque = Z_NULL;
|
zs.opaque = Z_NULL;
|
||||||
|
|
||||||
// WindowBits = 15 + 16 (31) -> This forces GZIP encoding
|
// WindowBits = 15 + 16 (31) = Enforce GZIP encoding
|
||||||
// MemLevel = 8 (default)
|
// MemLevel = 8 (default)
|
||||||
// Strategy = Z_DEFAULT_STRATEGY
|
// Strategy = Z_DEFAULT_STRATEGY
|
||||||
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
|
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||||
|
|||||||
@ -182,7 +182,7 @@ namespace IACore
|
|||||||
|
|
||||||
IPC_Manager::IPC_Manager()
|
IPC_Manager::IPC_Manager()
|
||||||
{
|
{
|
||||||
// SocketOps is smart enough to handle multiple inits
|
// SocketOps is smart enough to track multiple inits
|
||||||
SocketOps::Initialize();
|
SocketOps::Initialize();
|
||||||
|
|
||||||
m_recieveBuffer.resize(UINT16_MAX + 1);
|
m_recieveBuffer.resize(UINT16_MAX + 1);
|
||||||
@ -208,7 +208,7 @@ namespace IACore
|
|||||||
}
|
}
|
||||||
m_pendingSessions.clear();
|
m_pendingSessions.clear();
|
||||||
|
|
||||||
// SocketOps is smart enough to handle multiple terminates
|
// SocketOps is smart enough to track multiple terminates
|
||||||
SocketOps::Terminate();
|
SocketOps::Terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ namespace IACore
|
|||||||
|
|
||||||
BOOL success = CreateProcessA(NULL, commandLine.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
|
BOOL success = CreateProcessA(NULL, commandLine.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
|
||||||
|
|
||||||
// Important: Close write end in parent, otherwise ReadFile never returns EOF!
|
// Close write end in parent, otherwise ReadFile never returns EOF!
|
||||||
CloseHandle(hWrite);
|
CloseHandle(hWrite);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -196,7 +196,6 @@ namespace IACore
|
|||||||
dup2(pipefd[1], STDERR_FILENO);
|
dup2(pipefd[1], STDERR_FILENO);
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
|
|
||||||
// --- ARGUMENT PARSING START ---
|
|
||||||
std::vector<std::string> argStorage; // To keep strings alive
|
std::vector<std::string> argStorage; // To keep strings alive
|
||||||
std::vector<char *> argv;
|
std::vector<char *> argv;
|
||||||
|
|
||||||
@ -258,7 +257,6 @@ namespace IACore
|
|||||||
argv.push_back(s.data());
|
argv.push_back(s.data());
|
||||||
}
|
}
|
||||||
argv.push_back(nullptr);
|
argv.push_back(nullptr);
|
||||||
// --- ARGUMENT PARSING END ---
|
|
||||||
|
|
||||||
execvp(argv[0], argv.data());
|
execvp(argv[0], argv.data());
|
||||||
_exit(127);
|
_exit(127);
|
||||||
@ -312,7 +310,6 @@ namespace IACore
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Zero copy optimization for pure lines in one chunk
|
|
||||||
if (i > start)
|
if (i > start)
|
||||||
Callback(StringView(data + start, i - start));
|
Callback(StringView(data + start, i - start));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
@ -19,30 +19,31 @@
|
|||||||
#include <IACore/PCH.hpp>
|
#include <IACore/PCH.hpp>
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
#include <libloaderapi.h>
|
# include <libloaderapi.h>
|
||||||
#include <errhandlingapi.h>
|
# include <errhandlingapi.h>
|
||||||
#else
|
#else
|
||||||
#include <dlfcn.h>
|
# include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace IACore {
|
namespace IACore
|
||||||
|
{
|
||||||
|
|
||||||
class DynamicLib {
|
class DynamicLib
|
||||||
public:
|
{
|
||||||
// ---------------------------------------------------------------------
|
public:
|
||||||
// Constructors / Destructors (Move Only)
|
DynamicLib() : m_handle(nullptr)
|
||||||
// ---------------------------------------------------------------------
|
{
|
||||||
|
}
|
||||||
DynamicLib() : m_handle(nullptr) {}
|
|
||||||
|
|
||||||
// Move Constructor: Steal ownership, nullify source
|
DynamicLib(DynamicLib &&other) NOEXCEPT : m_handle(other.m_handle)
|
||||||
DynamicLib(DynamicLib&& other) NOEXCEPT : m_handle(other.m_handle) {
|
{
|
||||||
other.m_handle = nullptr;
|
other.m_handle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move Assignment
|
DynamicLib &operator=(DynamicLib &&other) NOEXCEPT
|
||||||
DynamicLib& operator=(DynamicLib&& other) NOEXCEPT {
|
{
|
||||||
if (this != &other) {
|
if (this != &other)
|
||||||
|
{
|
||||||
Unload(); // Free current if exists
|
Unload(); // Free current if exists
|
||||||
m_handle = other.m_handle;
|
m_handle = other.m_handle;
|
||||||
other.m_handle = nullptr;
|
other.m_handle = nullptr;
|
||||||
@ -50,135 +51,129 @@ namespace IACore {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No Copying allowed (Library handles are unique resources)
|
DynamicLib(CONST DynamicLib &) = delete;
|
||||||
DynamicLib(CONST DynamicLib&) = delete;
|
DynamicLib &operator=(CONST DynamicLib &) = delete;
|
||||||
DynamicLib& operator=(CONST DynamicLib&) = delete;
|
|
||||||
|
|
||||||
~DynamicLib() {
|
~DynamicLib()
|
||||||
|
{
|
||||||
Unload();
|
Unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Static Loader
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Automatically detects extension (.dll/.so) if not provided
|
// Automatically detects extension (.dll/.so) if not provided
|
||||||
NO_DISCARD("Check for load errors")
|
NO_DISCARD("Check for load errors")
|
||||||
STATIC tl::expected<DynamicLib, String> Load(CONST String& searchPath, CONST String& name) {
|
|
||||||
|
STATIC tl::expected<DynamicLib, String> Load(CONST String &searchPath, CONST String &name)
|
||||||
|
{
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
// 1. Build Path safely
|
|
||||||
fs::path fullPath = fs::path(searchPath) / name;
|
fs::path fullPath = fs::path(searchPath) / name;
|
||||||
|
|
||||||
// 2. Auto-append extension if missing
|
if (!fullPath.has_extension())
|
||||||
if (!fullPath.has_extension()) {
|
{
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
fullPath += ".dll";
|
fullPath += ".dll";
|
||||||
#elif IA_PLATFORM_MAC
|
#elif IA_PLATFORM_MAC
|
||||||
fullPath += ".dylib";
|
fullPath += ".dylib";
|
||||||
#else
|
#else
|
||||||
fullPath += ".so";
|
fullPath += ".so";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicLib lib;
|
DynamicLib lib;
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
// Use LoadLibraryA (ANSI/UTF-8) assuming manifest is set for UTF-8
|
HMODULE h = LoadLibraryA(fullPath.string().c_str());
|
||||||
HMODULE h = LoadLibraryA(fullPath.string().c_str());
|
if (!h)
|
||||||
if (!h) {
|
{
|
||||||
return tl::make_unexpected(GetWindowsError());
|
return tl::make_unexpected(GetWindowsError());
|
||||||
}
|
}
|
||||||
lib.m_handle = CAST(h, PVOID);
|
lib.m_handle = CAST(h, PVOID);
|
||||||
#else
|
#else
|
||||||
// RTLD_LAZY: Resolve symbols only as code executes (Standard for plugins)
|
// RTLD_LAZY: Resolve symbols only as code executes (Standard for plugins)
|
||||||
// RTLD_NOW: Resolve all immediately (Safer for strict engines)
|
void *h = dlopen(fullPath.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||||
void* h = dlopen(fullPath.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
if (!h)
|
||||||
if (!h) {
|
{
|
||||||
// dlerror returns a string describing the last error
|
// dlerror returns a string describing the last error
|
||||||
const char* err = dlerror();
|
const char *err = dlerror();
|
||||||
return tl::make_unexpected(String(err ? err : "Unknown dlopen error"));
|
return tl::make_unexpected(String(err ? err : "Unknown dlopen error"));
|
||||||
}
|
}
|
||||||
lib.m_handle = h;
|
lib.m_handle = h;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return IA_MOVE(lib);
|
return IA_MOVE(lib);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Symbol Access
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
NO_DISCARD("Check if symbol exists")
|
NO_DISCARD("Check if symbol exists")
|
||||||
tl::expected<PVOID, String> GetSymbol(CONST String& name) CONST {
|
|
||||||
if (!m_handle) return tl::make_unexpected(String("Library not loaded"));
|
tl::expected<PVOID, String> GetSymbol(CONST String &name) CONST
|
||||||
|
{
|
||||||
|
if (!m_handle)
|
||||||
|
return tl::make_unexpected(String("Library not loaded"));
|
||||||
|
|
||||||
PVOID sym = nullptr;
|
PVOID sym = nullptr;
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
sym = CAST(GetProcAddress(CAST(m_handle, HMODULE), name.c_str()), PVOID);
|
sym = CAST(GetProcAddress(CAST(m_handle, HMODULE), name.c_str()), PVOID);
|
||||||
if (!sym) return tl::make_unexpected(GetWindowsError());
|
if (!sym)
|
||||||
#else
|
return tl::make_unexpected(GetWindowsError());
|
||||||
// Clear any previous error
|
#else
|
||||||
dlerror();
|
// Clear any previous error
|
||||||
sym = dlsym(m_handle, name.c_str());
|
dlerror();
|
||||||
const char* err = dlerror();
|
sym = dlsym(m_handle, name.c_str());
|
||||||
if (err) return tl::make_unexpected(String(err));
|
const char *err = dlerror();
|
||||||
#endif
|
if (err)
|
||||||
|
return tl::make_unexpected(String(err));
|
||||||
|
#endif
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template helper for casting
|
// Template helper for casting
|
||||||
template<typename FuncT>
|
template<typename FuncT> tl::expected<FuncT, String> GetFunction(CONST String &name) CONST
|
||||||
tl::expected<FuncT, String> GetFunction(CONST String& name) CONST {
|
{
|
||||||
auto res = GetSymbol(name);
|
auto res = GetSymbol(name);
|
||||||
if (!res) return tl::make_unexpected(res.error());
|
if (!res)
|
||||||
|
return tl::make_unexpected(res.error());
|
||||||
return REINTERPRET(*res, FuncT);
|
return REINTERPRET(*res, FuncT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
VOID Unload()
|
||||||
// State Management
|
{
|
||||||
// ---------------------------------------------------------------------
|
if (m_handle)
|
||||||
|
{
|
||||||
VOID Unload() {
|
#if IA_PLATFORM_WINDOWS
|
||||||
if (m_handle) {
|
FreeLibrary(CAST(m_handle, HMODULE));
|
||||||
#if IA_PLATFORM_WINDOWS
|
#else
|
||||||
FreeLibrary(CAST(m_handle, HMODULE));
|
dlclose(m_handle);
|
||||||
#else
|
#endif
|
||||||
dlclose(m_handle);
|
|
||||||
#endif
|
|
||||||
m_handle = nullptr;
|
m_handle = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL IsLoaded() CONST {
|
BOOL IsLoaded() CONST
|
||||||
|
{
|
||||||
return m_handle != nullptr;
|
return m_handle != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PVOID m_handle;
|
PVOID m_handle;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
#if IA_PLATFORM_WINDOWS
|
||||||
// Private Helpers
|
STATIC String GetWindowsError()
|
||||||
// ---------------------------------------------------------------------
|
{
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
|
||||||
STATIC String GetWindowsError() {
|
|
||||||
DWORD errorID = ::GetLastError();
|
DWORD errorID = ::GetLastError();
|
||||||
if(errorID == 0) return String();
|
if (errorID == 0)
|
||||||
|
return String();
|
||||||
|
|
||||||
LPSTR messageBuffer = nullptr;
|
LPSTR messageBuffer = nullptr;
|
||||||
size_t size = FormatMessageA(
|
size_t size = FormatMessageA(
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
|
||||||
NULL, errorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
errorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL);
|
||||||
(LPSTR)&messageBuffer, 0, NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
String message(messageBuffer, size);
|
String message(messageBuffer, size);
|
||||||
LocalFree(messageBuffer);
|
LocalFree(messageBuffer);
|
||||||
return String("Win32 Error: ") + message;
|
return String("Win32 Error: ") + message;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
}
|
} // namespace IACore
|
||||||
@ -23,20 +23,14 @@ namespace IACore
|
|||||||
class Environment
|
class Environment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// ---------------------------------------------------------------------
|
STATIC Optional<String> Find(CONST String &name)
|
||||||
// Getters
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Modern approach: Returns nullopt if variable doesn't exist
|
|
||||||
STATIC std::optional<String> Find(CONST String &name)
|
|
||||||
{
|
{
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
// 1. Get required size (result includes null terminator if buffer is null/too small)
|
|
||||||
DWORD bufferSize = GetEnvironmentVariableA(name.c_str(), nullptr, 0);
|
DWORD bufferSize = GetEnvironmentVariableA(name.c_str(), nullptr, 0);
|
||||||
|
|
||||||
if (bufferSize == 0)
|
if (bufferSize == 0)
|
||||||
{
|
{
|
||||||
// DWORD 0 means failed (usually ERROR_ENVVAR_NOT_FOUND)
|
// DWORD 0 means failed
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +39,7 @@ namespace IACore
|
|||||||
result.resize(bufferSize);
|
result.resize(bufferSize);
|
||||||
|
|
||||||
// 3. Fetch
|
// 3. Fetch
|
||||||
// Returns num chars written EXCLUDING null terminator
|
// Returns num chars written excluding null terminator
|
||||||
DWORD actualSize = GetEnvironmentVariableA(name.c_str(), result.data(), bufferSize);
|
DWORD actualSize = GetEnvironmentVariableA(name.c_str(), result.data(), bufferSize);
|
||||||
|
|
||||||
if (actualSize == 0 || actualSize > bufferSize)
|
if (actualSize == 0 || actualSize > bufferSize)
|
||||||
@ -58,8 +52,7 @@ namespace IACore
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// POSIX (Linux/Mac)
|
// getenv returns a pointer to the environment area
|
||||||
// getenv returns a pointer to the environment area. Do NOT free it.
|
|
||||||
const char *val = std::getenv(name.c_str());
|
const char *val = std::getenv(name.c_str());
|
||||||
if (val == nullptr)
|
if (val == nullptr)
|
||||||
{
|
{
|
||||||
@ -69,17 +62,11 @@ namespace IACore
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classic approach: Returns value or default (defaulting to "")
|
|
||||||
// Matches your old API behavior but is safer
|
|
||||||
STATIC String Get(CONST String &name, CONST String &defaultValue = "")
|
STATIC String Get(CONST String &name, CONST String &defaultValue = "")
|
||||||
{
|
{
|
||||||
return Find(name).value_or(defaultValue);
|
return Find(name).value_or(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Setters
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
STATIC BOOL Set(CONST String &name, CONST String &value)
|
STATIC BOOL Set(CONST String &name, CONST String &value)
|
||||||
{
|
{
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
@ -88,7 +75,6 @@ namespace IACore
|
|||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
return SetEnvironmentVariableA(name.c_str(), value.c_str()) != 0;
|
return SetEnvironmentVariableA(name.c_str(), value.c_str()) != 0;
|
||||||
#else
|
#else
|
||||||
// setenv(name, value, overwrite)
|
|
||||||
// Returns 0 on success, -1 on error
|
// Returns 0 on success, -1 on error
|
||||||
return setenv(name.c_str(), value.c_str(), 1) == 0;
|
return setenv(name.c_str(), value.c_str(), 1) == 0;
|
||||||
#endif
|
#endif
|
||||||
@ -100,17 +86,12 @@ namespace IACore
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
// Windows unsets a variable by setting it to NULL
|
|
||||||
return SetEnvironmentVariableA(name.c_str(), nullptr) != 0;
|
return SetEnvironmentVariableA(name.c_str(), nullptr) != 0;
|
||||||
#else
|
#else
|
||||||
return unsetenv(name.c_str()) == 0;
|
return unsetenv(name.c_str()) == 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Utilities
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
STATIC BOOL Exists(CONST String &name)
|
STATIC BOOL Exists(CONST String &name)
|
||||||
{
|
{
|
||||||
#if IA_PLATFORM_WINDOWS
|
#if IA_PLATFORM_WINDOWS
|
||||||
|
|||||||
@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
# define valid_iatest_runner(type) iatest::_valid_iatest_runner<type>::value_type
|
# define valid_iatest_runner(type) iatest::_valid_iatest_runner<type>::value_type
|
||||||
|
|
||||||
// Internal macro to handle the return logic
|
|
||||||
# define __iat_micro_test(call) \
|
# define __iat_micro_test(call) \
|
||||||
if (!(call)) \
|
if (!(call)) \
|
||||||
return FALSE
|
return FALSE
|
||||||
@ -38,7 +37,6 @@
|
|||||||
# 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)
|
|
||||||
# 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)
|
||||||
@ -46,7 +44,6 @@
|
|||||||
|
|
||||||
# define IAT_BLOCK(name) class name : public ia::iatest::block
|
# define IAT_BLOCK(name) class name : public ia::iatest::block
|
||||||
|
|
||||||
// Concatenation fix for macros
|
|
||||||
# define IAT_BEGIN_BLOCK(_group, _name) \
|
# define IAT_BEGIN_BLOCK(_group, _name) \
|
||||||
class _group##_##_name : public ia::iatest::block \
|
class _group##_##_name : public ia::iatest::block \
|
||||||
{ \
|
{ \
|
||||||
@ -74,9 +71,6 @@
|
|||||||
|
|
||||||
namespace ia::iatest
|
namespace ia::iatest
|
||||||
{
|
{
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Type Printing Helper (To show WHAT failed)
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
template<typename T> std::string ToString(CONST T &value)
|
template<typename T> std::string ToString(CONST T &value)
|
||||||
{
|
{
|
||||||
if constexpr (std::is_arithmetic_v<T>)
|
if constexpr (std::is_arithmetic_v<T>)
|
||||||
@ -93,7 +87,6 @@ namespace ia::iatest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specialization for pointers
|
|
||||||
template<typename T> std::string ToString(T *value)
|
template<typename T> std::string ToString(T *value)
|
||||||
{
|
{
|
||||||
if (value == NULLPTR)
|
if (value == NULLPTR)
|
||||||
@ -103,10 +96,6 @@ namespace ia::iatest
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Core Structures
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
DEFINE_TYPE(functor_t, std::function<BOOL()>);
|
DEFINE_TYPE(functor_t, std::function<BOOL()>);
|
||||||
|
|
||||||
struct unit_t
|
struct unit_t
|
||||||
@ -128,7 +117,6 @@ namespace ia::iatest
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Generic Equality
|
|
||||||
template<typename T1, typename T2> BOOL _test_eq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
|
template<typename T1, typename T2> BOOL _test_eq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
|
||||||
{
|
{
|
||||||
if (lhs != rhs)
|
if (lhs != rhs)
|
||||||
@ -139,7 +127,6 @@ namespace ia::iatest
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic Inequality
|
|
||||||
template<typename T1, typename T2> BOOL _test_neq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
|
template<typename T1, typename T2> BOOL _test_neq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description)
|
||||||
{
|
{
|
||||||
if (lhs == rhs)
|
if (lhs == rhs)
|
||||||
@ -150,13 +137,12 @@ namespace ia::iatest
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Floating Point Approximation (Epsilon check)
|
|
||||||
template<typename T> BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description)
|
template<typename T> BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description)
|
||||||
{
|
{
|
||||||
static_assert(std::is_floating_point_v<T>, "Approx only works for floats/doubles");
|
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))
|
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;
|
||||||
}
|
}
|
||||||
@ -202,10 +188,6 @@ namespace ia::iatest
|
|||||||
template<typename block_class>
|
template<typename block_class>
|
||||||
concept valid_block_class = std::derived_from<block_class, block>;
|
concept valid_block_class = std::derived_from<block_class, block>;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Runner
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<BOOL stopOnFail = false, BOOL isVerbose = false> class runner
|
template<BOOL stopOnFail = false, BOOL isVerbose = false> class runner
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -253,7 +235,6 @@ namespace ia::iatest
|
|||||||
BOOL result = FALSE;
|
BOOL result = FALSE;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Execute the test function
|
|
||||||
result = v.Functor();
|
result = v.Functor();
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
@ -311,10 +292,6 @@ namespace ia::iatest
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Global Test Registry
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Standard runner configuration for the single executable
|
|
||||||
using DefaultRunner = runner<false, true>;
|
using DefaultRunner = runner<false, true>;
|
||||||
|
|
||||||
class TestRegistry
|
class TestRegistry
|
||||||
@ -352,7 +329,6 @@ namespace ia::iatest
|
|||||||
};
|
};
|
||||||
} // namespace ia::iatest
|
} // 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;
|
# define IAT_REGISTER_ENTRY(Group, Name) static ia::iatest::AutoRegister<Group##_##Name> _iat_reg_##Group##_##Name;
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
@ -43,7 +43,7 @@ namespace IACore
|
|||||||
// SECTION 2: RING BUFFER CONTROL BLOCKS
|
// SECTION 2: RING BUFFER CONTROL BLOCKS
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
// RingBufferView::ControlBlock is already 64-byte aligned internally.
|
// RingBufferView ControlBlock is already 64-byte aligned internally.
|
||||||
RingBufferView::ControlBlock MONI_Control;
|
RingBufferView::ControlBlock MONI_Control;
|
||||||
RingBufferView::ControlBlock MINO_Control;
|
RingBufferView::ControlBlock MINO_Control;
|
||||||
|
|
||||||
|
|||||||
@ -54,8 +54,8 @@
|
|||||||
# define NOMINMAX
|
# define NOMINMAX
|
||||||
# endif
|
# endif
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#undef VOID
|
# undef VOID
|
||||||
#undef ERROR
|
# undef ERROR
|
||||||
#elif IA_PLATFORM_UNIX
|
#elif IA_PLATFORM_UNIX
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
@ -238,7 +238,6 @@
|
|||||||
|
|
||||||
#define ALIGN(a) __attribute__((aligned(a)))
|
#define ALIGN(a) __attribute__((aligned(a)))
|
||||||
|
|
||||||
// Mark every explicit assembly instruction as volatile
|
|
||||||
#define ASM(...) __asm__ volatile(__VA_ARGS__)
|
#define ASM(...) __asm__ volatile(__VA_ARGS__)
|
||||||
|
|
||||||
#ifndef NULL
|
#ifndef NULL
|
||||||
@ -539,7 +538,7 @@ STATIC CONST FLOAT64 FLOAT64_EPSILON = DBL_EPSILON;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// C++ Containers and Helpers
|
// Containers and Helpers
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@ using SocketHandle = int;
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
# warning "IACore SocketOps is not supported on this platform."
|
# error "IACore SocketOps is not supported on this platform."
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,6 @@ namespace IACore
|
|||||||
char high = hex[i];
|
char high = hex[i];
|
||||||
char low = hex[i + 1];
|
char low = hex[i + 1];
|
||||||
|
|
||||||
// Quick helper to decode nibble
|
|
||||||
auto fromHexChar = [](char c) -> int {
|
auto fromHexChar = [](char c) -> int {
|
||||||
if (c >= '0' && c <= '9')
|
if (c >= '0' && c <= '9')
|
||||||
return c - '0';
|
return c - '0';
|
||||||
@ -95,16 +94,10 @@ namespace IACore
|
|||||||
return std::ranges::upper_bound(range, value);
|
return std::ranges::upper_bound(range, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Hash Combination Logic
|
|
||||||
// Uses the "golden ratio" mix (similar to Boost) to preserve entropy.
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
template<typename T> INLINE STATIC void HashCombine(UINT64 &seed, CONST T &v)
|
template<typename T> INLINE STATIC void HashCombine(UINT64 &seed, CONST T &v)
|
||||||
{
|
{
|
||||||
UINT64 h;
|
UINT64 h;
|
||||||
|
|
||||||
// 1. Compile-Time check: Can this be treated as a string view?
|
|
||||||
// This catches "Literal Strings" (char[N]), const char*, and std::string
|
|
||||||
if constexpr (std::is_constructible_v<std::string_view, T>)
|
if constexpr (std::is_constructible_v<std::string_view, T>)
|
||||||
{
|
{
|
||||||
std::string_view sv(v);
|
std::string_view sv(v);
|
||||||
@ -113,34 +106,24 @@ namespace IACore
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 2. Standard types (int, float, custom structs)
|
|
||||||
auto hasher = ankerl::unordered_dense::hash<T>();
|
auto hasher = ankerl::unordered_dense::hash<T>();
|
||||||
h = hasher(v);
|
h = hasher(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x9e3779b97f4a7c15 is the 64-bit golden ratio (phi) approximation
|
|
||||||
seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2);
|
seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Variadic Hasher
|
|
||||||
// Allows: IACore::ComputeHash(x, y, z);
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
template<typename... Args> INLINE STATIC UINT64 ComputeHash(CONST Args &...args)
|
template<typename... Args> INLINE STATIC UINT64 ComputeHash(CONST Args &...args)
|
||||||
{
|
{
|
||||||
UINT64 seed = 0;
|
UINT64 seed = 0;
|
||||||
(HashCombine(seed, args), ...); // C++17/20 Fold Expression
|
(HashCombine(seed, args), ...);
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
template<typename T, typename... MemberPtrs>
|
||||||
// Flat Hasher
|
INLINE STATIC UINT64 ComputeHashFlat(CONST T &obj, MemberPtrs... members)
|
||||||
// Allows: IACore::ComputeHashFlat(x, y, z);
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
template<typename T, typename... MemberPtrs> INLINE STATIC UINT64 ComputeHashFlat(CONST T &obj, MemberPtrs... members)
|
|
||||||
{
|
{
|
||||||
UINT64 seed = 0;
|
UINT64 seed = 0;
|
||||||
// C++17 Fold Expression: applies (seed, obj.*ptr) for every ptr in members
|
|
||||||
(HashCombine(seed, obj.*members), ...);
|
(HashCombine(seed, obj.*members), ...);
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
@ -156,15 +139,14 @@ namespace IACore
|
|||||||
// struct Vector3 { float x, y, z; };
|
// struct Vector3 { float x, y, z; };
|
||||||
// IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z)
|
// IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z)
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#define IA_MAKE_HASHABLE(Type, ...) \
|
#define IA_MAKE_HASHABLE(Type, ...) \
|
||||||
template<> struct ankerl::unordered_dense::hash<Type> \
|
template<> struct ankerl::unordered_dense::hash<Type> \
|
||||||
{ \
|
{ \
|
||||||
using is_avalanching = void; \
|
using is_avalanching = void; \
|
||||||
NO_DISCARD("Hash value should be used") \
|
NO_DISCARD("Hash value should be used") \
|
||||||
UINT64 operator()(CONST Type &v) const NOEXCEPT \
|
UINT64 operator()(CONST Type &v) const NOEXCEPT \
|
||||||
{ \
|
{ \
|
||||||
/* Pass the object and the list of member pointers */ \
|
/* Pass the object and the list of member pointers */ \
|
||||||
return IACore::Utils::ComputeHashFlat(v, __VA_ARGS__); \
|
return IACore::Utils::ComputeHashFlat(v, __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,13 +23,13 @@ set(TEST_SOURCES
|
|||||||
|
|
||||||
add_executable(IACore_Test_Suite ${TEST_SOURCES})
|
add_executable(IACore_Test_Suite ${TEST_SOURCES})
|
||||||
|
|
||||||
# Enable exceptions for testing framework (even if Core is no-except)
|
# Enable exceptions for testing framework
|
||||||
target_compile_options(IACore_Test_Suite PRIVATE -fexceptions)
|
target_compile_options(IACore_Test_Suite PRIVATE -fexceptions)
|
||||||
set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
target_link_libraries(IACore_Test_Suite PRIVATE IACore)
|
target_link_libraries(IACore_Test_Suite PRIVATE IACore)
|
||||||
|
|
||||||
# Copy necessary runtime assets if you have them (like the LongProcess test subject)
|
# Copy necessary runtime assets
|
||||||
add_custom_command(TARGET IACore_Test_Suite POST_BUILD
|
add_custom_command(TARGET IACore_Test_Suite POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
$<TARGET_FILE:LongProcess>
|
$<TARGET_FILE:LongProcess>
|
||||||
|
|||||||
@ -23,8 +23,6 @@ using namespace IACore;
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Constants
|
// Constants
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// We use a unique prefix to ensure we don't accidentally mess with real
|
|
||||||
// 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";
|
||||||
|
|
||||||
@ -123,7 +121,6 @@ BOOL TestDefaults()
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 5. Empty Strings vs Null/Unset
|
// 5. Empty Strings vs Null/Unset
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 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()
|
||||||
|
|||||||
@ -50,7 +50,6 @@ BOOL TestBasicRun()
|
|||||||
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
|
||||||
|
|
||||||
// Note: Echo might add newline, but your LineBuffer trims/handles it.
|
|
||||||
// We check if "HelloIA" is contained or equal.
|
// We check if "HelloIA" is contained or equal.
|
||||||
IAT_CHECK(captured.find("HelloIA") != String::npos);
|
IAT_CHECK(captured.find("HelloIA") != String::npos);
|
||||||
|
|
||||||
@ -137,14 +136,9 @@ BOOL TestMissingExe()
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestLargeOutput()
|
BOOL TestLargeOutput()
|
||||||
{
|
{
|
||||||
// We need to generate output larger than the internal 4096 buffer
|
// 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.
|
||||||
|
|
||||||
// We will construct a python script or shell command to print a massive line.
|
|
||||||
// Cross platform approach: Use Python if available, or just a long echo.
|
|
||||||
// Let's assume 'python3' or 'python' is in path, otherwise skip?
|
|
||||||
// Safer: Use pure shell loop if possible, or just a massive command line arg.
|
|
||||||
|
|
||||||
String massiveString;
|
String massiveString;
|
||||||
massiveString.reserve(5000);
|
massiveString.reserve(5000);
|
||||||
for (int i = 0; i < 500; ++i)
|
for (int i = 0; i < 500; ++i)
|
||||||
@ -223,10 +217,6 @@ BOOL TestComplexArguments()
|
|||||||
// 3. path/to/file
|
// 3. path/to/file
|
||||||
String complexArgs = "-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
|
String complexArgs = "-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
|
||||||
|
|
||||||
// We can't easily inspect the child process argv in this unit test framework without
|
|
||||||
// writing a dedicated child program that prints its argv.
|
|
||||||
// However, for now, we ensure it doesn't crash or return error code 127.
|
|
||||||
|
|
||||||
// Use "echo" to verify what it received.
|
// Use "echo" to verify what it received.
|
||||||
// Expected output: -DDEFINED_MSG="Hello World" -v path/to/file
|
// Expected output: -DDEFINED_MSG="Hello World" -v path/to/file
|
||||||
String cmd = "/bin/echo";
|
String cmd = "/bin/echo";
|
||||||
|
|||||||
@ -21,7 +21,9 @@ using namespace IACore;
|
|||||||
|
|
||||||
IAT_BEGIN_BLOCK(Core, RingBuffer)
|
IAT_BEGIN_BLOCK(Core, RingBuffer)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
// 1. Basic Push Pop
|
// 1. Basic Push Pop
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestPushPop()
|
BOOL TestPushPop()
|
||||||
{
|
{
|
||||||
// Allocate raw memory for the ring buffer
|
// Allocate raw memory for the ring buffer
|
||||||
@ -54,7 +56,9 @@ BOOL TestPushPop()
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Wrap Around (The hardest logic to get right)
|
// -------------------------------------------------------------------------
|
||||||
|
// 2. Wrap Around
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
BOOL TestWrapAround()
|
BOOL TestWrapAround()
|
||||||
{
|
{
|
||||||
// Small buffer to force wrapping quickly
|
// Small buffer to force wrapping quickly
|
||||||
|
|||||||
@ -20,10 +20,6 @@
|
|||||||
|
|
||||||
using namespace IACore;
|
using namespace IACore;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Test Block Definition
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
IAT_BEGIN_BLOCK(Core, StreamReader)
|
IAT_BEGIN_BLOCK(Core, StreamReader)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@ -53,14 +49,13 @@ BOOL TestReadUint8()
|
|||||||
BOOL TestReadMultiByte()
|
BOOL TestReadMultiByte()
|
||||||
{
|
{
|
||||||
// 0x04030201 in Little Endian memory layout
|
// 0x04030201 in Little Endian memory layout
|
||||||
|
// IACore always assumes a Little Endian machine
|
||||||
UINT8 data[] = {0x01, 0x02, 0x03, 0x04};
|
UINT8 data[] = {0x01, 0x02, 0x03, 0x04};
|
||||||
StreamReader reader(data);
|
StreamReader reader(data);
|
||||||
|
|
||||||
auto val = reader.Read<UINT32>();
|
auto val = reader.Read<UINT32>();
|
||||||
IAT_CHECK(val.has_value());
|
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(*val, (UINT32) 0x04030201);
|
||||||
|
|
||||||
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 4);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 4);
|
||||||
|
|||||||
@ -88,7 +88,6 @@ 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());
|
||||||
// Optional: IAT_CHECK_EQ(odd.error(), "Hex string must have even length");
|
|
||||||
|
|
||||||
// Invalid Characters
|
// Invalid Characters
|
||||||
auto invalid = IACore::Utils::HexStringToBinary("ZZTOP");
|
auto invalid = IACore::Utils::HexStringToBinary("ZZTOP");
|
||||||
@ -178,9 +177,8 @@ 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};
|
||||||
|
|
||||||
// Instantiate the hasher manually to verify the struct specialization exists
|
|
||||||
ankerl::unordered_dense::hash<TestVec3> hasher;
|
ankerl::unordered_dense::hash<TestVec3> hasher;
|
||||||
|
|
||||||
UINT64 h1 = hasher(v1);
|
UINT64 h1 = hasher(v1);
|
||||||
@ -194,10 +192,6 @@ BOOL TestHashMacro()
|
|||||||
// Verify ComputeHash integration
|
// Verify ComputeHash integration
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
|
|
||||||
// We cannot check EQ(h1, ComputeHash(v1)) because ComputeHash applies
|
|
||||||
// one extra layer of "Golden Ratio Mixing" on top of the object's hash.
|
|
||||||
// Instead, we verify that ComputeHash behaves exactly like a manual HashCombine.
|
|
||||||
|
|
||||||
UINT64 hManual = 0;
|
UINT64 hManual = 0;
|
||||||
IACore::Utils::HashCombine(hManual, v1);
|
IACore::Utils::HashCombine(hManual, v1);
|
||||||
|
|
||||||
@ -206,7 +200,7 @@ BOOL TestHashMacro()
|
|||||||
// This proves ComputeHash found the specialization and mixed it correctly
|
// This proves ComputeHash found the specialization and mixed it correctly
|
||||||
IAT_CHECK_EQ(hManual, hWrapper);
|
IAT_CHECK_EQ(hManual, hWrapper);
|
||||||
|
|
||||||
// Optional: Verify the avalanche effect took place (hWrapper should NOT be h1)
|
// Verify the avalanche effect took place (hWrapper should NOT be h1)
|
||||||
IAT_CHECK_NEQ(h1, hWrapper);
|
IAT_CHECK_NEQ(h1, hWrapper);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|||||||
Reference in New Issue
Block a user