Release Readt
Some checks failed
CI / build-linux-and-wasm (x64-linux) (push) Has been cancelled

This commit is contained in:
2026-01-25 22:08:16 +05:30
parent 601b573983
commit ebde7c7e66
19 changed files with 51 additions and 567 deletions

View File

@ -20,44 +20,28 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// Helper: RAII Scheduler Guard
// -----------------------------------------------------------------------------
// Ensures the scheduler is terminated at the end of a test scope,
// preventing state pollution between tests.
struct SchedulerGuard { struct SchedulerGuard {
SchedulerGuard(u8 worker_count = 2) { SchedulerGuard(u8 worker_count = 2) {
// We ignore the result here as we assume the test environment is clean.
// Specific initialization tests will check the result manually.
(void)AsyncOps::initialize_scheduler(worker_count); (void)AsyncOps::initialize_scheduler(worker_count);
} }
~SchedulerGuard() { AsyncOps::terminate_scheduler(); } ~SchedulerGuard() { AsyncOps::terminate_scheduler(); }
}; };
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, AsyncOps) IAT_BEGIN_BLOCK(Core, AsyncOps)
// -------------------------------------------------------------------------
// 1. Initialization and Termination
// -------------------------------------------------------------------------
auto test_initialization() -> bool { auto test_initialization() -> bool {
// Ensure clean state
AsyncOps::terminate_scheduler(); AsyncOps::terminate_scheduler();
// Initialize with specific worker countB
const auto res = AsyncOps::initialize_scheduler(4); const auto res = AsyncOps::initialize_scheduler(4);
IAT_CHECK(res.has_value()); IAT_CHECK(res.has_value());
IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(4)); IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(4));
// Terminate
AsyncOps::terminate_scheduler(); AsyncOps::terminate_scheduler();
// Verify we can re-initialize
const auto res2 = AsyncOps::initialize_scheduler(1); const auto res2 = AsyncOps::initialize_scheduler(1);
IAT_CHECK(res2.has_value()); IAT_CHECK(res2.has_value());
IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(1)); IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(1));
@ -66,20 +50,15 @@ auto test_initialization() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Basic Task Execution
// -------------------------------------------------------------------------
auto test_basic_execution() -> bool { auto test_basic_execution() -> bool {
SchedulerGuard guard(2); SchedulerGuard guard(2);
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> run_count{0}; std::atomic<i32> run_count{0};
// Schedule a simple task
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { run_count++; }, 0, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { run_count++; }, 0,
&schedule); &schedule);
// Wait for it to finish
AsyncOps::wait_for_schedule_completion(&schedule); AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(run_count.load(), 1); IAT_CHECK_EQ(run_count.load(), 1);
@ -87,9 +66,6 @@ auto test_basic_execution() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Concurrency and Load
// -------------------------------------------------------------------------
auto test_concurrency() -> bool { auto test_concurrency() -> bool {
SchedulerGuard guard(4); SchedulerGuard guard(4);
@ -100,7 +76,6 @@ auto test_concurrency() -> bool {
for (i32 i = 0; i < total_tasks; ++i) { for (i32 i = 0; i < total_tasks; ++i) {
AsyncOps::schedule_task( AsyncOps::schedule_task(
[&](AsyncOps::WorkerId) { [&](AsyncOps::WorkerId) {
// Simulate a tiny bit of work to encourage thread switching
std::this_thread::sleep_for(std::chrono::microseconds(10)); std::this_thread::sleep_for(std::chrono::microseconds(10));
run_count++; run_count++;
}, },
@ -114,19 +89,12 @@ auto test_concurrency() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Priorities
// -------------------------------------------------------------------------
auto test_priorities() -> bool { auto test_priorities() -> bool {
SchedulerGuard guard(2); SchedulerGuard guard(2);
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> high_priority_ran{0}; std::atomic<i32> high_priority_ran{0};
std::atomic<i32> normal_priority_ran{0}; std::atomic<i32> normal_priority_ran{0};
// Verify API accepts priorities and executes them.
// Note: Deterministic priority testing is difficult in unit tests without
// mocking, so we primarily ensure the API functions correctly.
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { high_priority_ran++; }, 0, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { high_priority_ran++; }, 0,
&schedule, AsyncOps::Priority::High); &schedule, AsyncOps::Priority::High);
@ -141,18 +109,13 @@ auto test_priorities() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. Fire-and-Forget Tasks
// -------------------------------------------------------------------------
auto test_run_task_fire_and_forget() -> bool { auto test_run_task_fire_and_forget() -> bool {
SchedulerGuard guard(2); SchedulerGuard guard(2);
std::atomic<bool> executed{false}; std::atomic<bool> executed{false};
// run_task doesn't use a Schedule object, so we must sync manually
AsyncOps::run_task([&]() { executed = true; }); AsyncOps::run_task([&]() { executed = true; });
// Busy wait with timeout (max 1 second)
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
if (executed.load()) if (executed.load())
break; break;
@ -164,34 +127,25 @@ auto test_run_task_fire_and_forget() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 6. Cancellation Safety
// -------------------------------------------------------------------------
auto test_cancellation_safety() -> bool { auto test_cancellation_safety() -> bool {
SchedulerGuard guard(2); SchedulerGuard guard(2);
// 1. Cancel non-existent tag (Should be safe)
AsyncOps::cancel_tasks_of_tag(999); AsyncOps::cancel_tasks_of_tag(999);
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> counter{0}; std::atomic<i32> counter{0};
// 2. Schedule a task, wait for it
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { counter++; }, 10, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { counter++; }, 10,
&schedule); &schedule);
AsyncOps::wait_for_schedule_completion(&schedule); AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(counter.load(), 1); IAT_CHECK_EQ(counter.load(), 1);
// 3. Cancel tag 10 (already done, should be safe)
AsyncOps::cancel_tasks_of_tag(10); AsyncOps::cancel_tasks_of_tag(10);
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_initialization); IAT_ADD_TEST(test_initialization);
IAT_ADD_TEST(test_basic_execution); IAT_ADD_TEST(test_basic_execution);

View File

@ -18,22 +18,14 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, CLI) IAT_BEGIN_BLOCK(Core, CLI)
// -------------------------------------------------------------------------
// 1. Basic Traversal
// -------------------------------------------------------------------------
auto test_basic_traversal() -> bool { auto test_basic_traversal() -> bool {
const Vec<String> args = {"ignored", "one", "two", "three"}; const Vec<String> args = {"ignored", "one", "two", "three"};
CLIParser parser(args); CLIParser parser(args);
IAT_CHECK(parser.remaining()); IAT_CHECK(parser.remaining());
// Check sequential access
IAT_CHECK_EQ(String(parser.next()), "one"); IAT_CHECK_EQ(String(parser.next()), "one");
IAT_CHECK(parser.remaining()); IAT_CHECK(parser.remaining());
@ -42,30 +34,22 @@ auto test_basic_traversal() -> bool {
IAT_CHECK_EQ(String(parser.next()), "three"); IAT_CHECK_EQ(String(parser.next()), "three");
// Should be exhausted now
IAT_CHECK_NOT(parser.remaining()); IAT_CHECK_NOT(parser.remaining());
// Next on exhausted returns empty string view
IAT_CHECK_EQ(String(parser.next()), ""); IAT_CHECK_EQ(String(parser.next()), "");
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Peek Functionality
// -------------------------------------------------------------------------
auto test_peek() -> bool { auto test_peek() -> bool {
const Vec<String> args = {"ignored", "peek_val", "next_val"}; const Vec<String> args = {"ignored", "peek_val", "next_val"};
CLIParser parser(args); CLIParser parser(args);
// Peek should return value but not advance
IAT_CHECK_EQ(String(parser.peek()), "peek_val"); IAT_CHECK_EQ(String(parser.peek()), "peek_val");
IAT_CHECK(parser.remaining()); IAT_CHECK(parser.remaining());
// Verify we are still at the same element
IAT_CHECK_EQ(String(parser.next()), "peek_val"); IAT_CHECK_EQ(String(parser.next()), "peek_val");
// Now we should be at next_val
IAT_CHECK_EQ(String(parser.peek()), "next_val"); IAT_CHECK_EQ(String(parser.peek()), "next_val");
IAT_CHECK_EQ(String(parser.next()), "next_val"); IAT_CHECK_EQ(String(parser.next()), "next_val");
@ -74,29 +58,20 @@ auto test_peek() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Consume Functionality
// -------------------------------------------------------------------------
auto test_consume() -> bool { auto test_consume() -> bool {
const Vec<String> args = {"ignored", "-v", "--output", "file.txt"}; const Vec<String> args = {"ignored", "-v", "--output", "file.txt"};
CLIParser parser(args); CLIParser parser(args);
// 1. Try consuming something that isn't there
IAT_CHECK_NOT(parser.consume("-x")); IAT_CHECK_NOT(parser.consume("-x"));
// Verify we haven't moved
IAT_CHECK_EQ(String(parser.peek()), "-v"); IAT_CHECK_EQ(String(parser.peek()), "-v");
// 2. Consume the correct flag
IAT_CHECK(parser.consume("-v")); IAT_CHECK(parser.consume("-v"));
// 3. Verify advancement
IAT_CHECK_EQ(String(parser.peek()), "--output"); IAT_CHECK_EQ(String(parser.peek()), "--output");
// 4. Consume next
IAT_CHECK(parser.consume("--output")); IAT_CHECK(parser.consume("--output"));
// 5. Verify final argument
IAT_CHECK_EQ(String(parser.next()), "file.txt"); IAT_CHECK_EQ(String(parser.next()), "file.txt");
IAT_CHECK_NOT(parser.remaining()); IAT_CHECK_NOT(parser.remaining());
@ -104,9 +79,6 @@ auto test_consume() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Empty Argument List
// -------------------------------------------------------------------------
auto test_empty() -> bool { auto test_empty() -> bool {
const Vec<String> args = {}; const Vec<String> args = {};
CLIParser parser(args); CLIParser parser(args);
@ -119,9 +91,6 @@ auto test_empty() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_basic_traversal); IAT_ADD_TEST(test_basic_traversal);
IAT_ADD_TEST(test_peek); IAT_ADD_TEST(test_peek);

View File

@ -1,6 +1,3 @@
# ------------------------------------------------
# The Unified C++ Test Suite
# ------------------------------------------------
set(TEST_SOURCES set(TEST_SOURCES
AsyncOps.cpp AsyncOps.cpp
CLI.cpp CLI.cpp
@ -27,13 +24,11 @@ set(TEST_SOURCES
add_executable(IACore_Test_Suite ${TEST_SOURCES}) add_executable(IACore_Test_Suite ${TEST_SOURCES})
# 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
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>

View File

@ -18,10 +18,6 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, DataOps) IAT_BEGIN_BLOCK(Core, DataOps)
auto test_crc32() -> bool { auto test_crc32() -> bool {
@ -101,9 +97,6 @@ auto test_hash_fnv1a() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_crc32); IAT_ADD_TEST(test_crc32);
IAT_ADD_TEST(test_hash_fnv1a); IAT_ADD_TEST(test_hash_fnv1a);

View File

@ -18,53 +18,35 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------
static constexpr const char *TEST_KEY = "IA_TEST_ENV_VAR_12345"; static constexpr const char *TEST_KEY = "IA_TEST_ENV_VAR_12345";
static constexpr const char *TEST_VAL = "Hello World"; static constexpr const char *TEST_VAL = "Hello World";
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, Environment) IAT_BEGIN_BLOCK(Core, Environment)
// -------------------------------------------------------------------------
// 1. Basic Set and Get (The Happy Path)
// -------------------------------------------------------------------------
auto test_basic_cycle() -> bool { auto test_basic_cycle() -> bool {
// 1. Ensure clean slate
(void)Environment::unset(TEST_KEY); (void)Environment::unset(TEST_KEY);
IAT_CHECK_NOT(Environment::exists(TEST_KEY)); IAT_CHECK_NOT(Environment::exists(TEST_KEY));
// 2. Set
const auto set_res = Environment::set(TEST_KEY, TEST_VAL); const auto set_res = Environment::set(TEST_KEY, TEST_VAL);
IAT_CHECK(set_res.has_value()); IAT_CHECK(set_res.has_value());
IAT_CHECK(Environment::exists(TEST_KEY)); IAT_CHECK(Environment::exists(TEST_KEY));
// 3. Find (Optional)
const auto opt = Environment::find(TEST_KEY); const auto opt = Environment::find(TEST_KEY);
IAT_CHECK(opt.has_value()); IAT_CHECK(opt.has_value());
IAT_CHECK_EQ(*opt, String(TEST_VAL)); IAT_CHECK_EQ(*opt, String(TEST_VAL));
// 4. Get (Direct)
const String val = Environment::get(TEST_KEY); const String val = Environment::get(TEST_KEY);
IAT_CHECK_EQ(val, String(TEST_VAL)); IAT_CHECK_EQ(val, String(TEST_VAL));
// Cleanup
(void)Environment::unset(TEST_KEY); (void)Environment::unset(TEST_KEY);
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Overwriting Values
// -------------------------------------------------------------------------
auto test_overwrite() -> bool { auto test_overwrite() -> bool {
(void)Environment::set(TEST_KEY, "ValueA"); (void)Environment::set(TEST_KEY, "ValueA");
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueA")); IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueA"));
// Overwrite
(void)Environment::set(TEST_KEY, "ValueB"); (void)Environment::set(TEST_KEY, "ValueB");
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueB")); IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueB"));
@ -72,9 +54,6 @@ auto test_overwrite() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Unset Logic
// -------------------------------------------------------------------------
auto test_unset() -> bool { auto test_unset() -> bool {
(void)Environment::set(TEST_KEY, "To Be Deleted"); (void)Environment::set(TEST_KEY, "To Be Deleted");
IAT_CHECK(Environment::exists(TEST_KEY)); IAT_CHECK(Environment::exists(TEST_KEY));
@ -82,82 +61,58 @@ auto test_unset() -> bool {
const auto unset_res = Environment::unset(TEST_KEY); const auto unset_res = Environment::unset(TEST_KEY);
IAT_CHECK(unset_res.has_value()); IAT_CHECK(unset_res.has_value());
// Verify it is actually gone
IAT_CHECK_NOT(Environment::exists(TEST_KEY)); IAT_CHECK_NOT(Environment::exists(TEST_KEY));
// Find should return nullopt
const auto opt = Environment::find(TEST_KEY); const auto opt = Environment::find(TEST_KEY);
IAT_CHECK_NOT(opt.has_value()); IAT_CHECK_NOT(opt.has_value());
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Default Value Fallbacks
// -------------------------------------------------------------------------
auto test_defaults() -> bool { auto test_defaults() -> bool {
const char *ghost_key = "IA_THIS_KEY_DOES_NOT_EXIST"; const char *ghost_key = "IA_THIS_KEY_DOES_NOT_EXIST";
// Ensure it really doesn't exist
(void)Environment::unset(ghost_key); (void)Environment::unset(ghost_key);
// Test Get with implicit default ("")
const String empty = Environment::get(ghost_key); const String empty = Environment::get(ghost_key);
IAT_CHECK(empty.empty()); IAT_CHECK(empty.empty());
// Test Get with explicit default
const String fallback = Environment::get(ghost_key, "MyDefault"); const String fallback = Environment::get(ghost_key, "MyDefault");
IAT_CHECK_EQ(fallback, String("MyDefault")); IAT_CHECK_EQ(fallback, String("MyDefault"));
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. Empty Strings vs Null/Unset
// -------------------------------------------------------------------------
// Does Set(Key, "") create an existing empty variable, or unset it?
// Standard POSIX/Windows API behavior is that it EXISTS, but is empty.
auto test_empty_value() -> bool { auto test_empty_value() -> bool {
(void)Environment::set(TEST_KEY, ""); (void)Environment::set(TEST_KEY, "");
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
// Windows behavior: Setting to empty string removes the variable
// IAT_CHECK_NOT(Environment::exists(TEST_KEY));
// auto opt = Environment::find(TEST_KEY);
// IAT_CHECK_NOT(opt.has_value());
#else #else
// POSIX behavior: Variable exists but is empty
IAT_CHECK(Environment::exists(TEST_KEY)); IAT_CHECK(Environment::exists(TEST_KEY));
const auto opt = Environment::find(TEST_KEY); const auto opt = Environment::find(TEST_KEY);
IAT_CHECK(opt.has_value()); IAT_CHECK(opt.has_value());
IAT_CHECK(opt->empty()); IAT_CHECK(opt->empty());
#endif #endif
// Cleanup
(void)Environment::unset(TEST_KEY); (void)Environment::unset(TEST_KEY);
IAT_CHECK_NOT(Environment::exists(TEST_KEY)); IAT_CHECK_NOT(Environment::exists(TEST_KEY));
return true; return true;
} }
// -------------------------------------------------------------------------
// 6. Validation / Bad Input
// -------------------------------------------------------------------------
auto test_bad_input() -> bool { auto test_bad_input() -> bool {
// Setting an empty key should fail gracefully
const auto res = Environment::set("", "Value"); const auto res = Environment::set("", "Value");
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());
// Unsetting an empty key should fail
const auto res_unset = Environment::unset(""); const auto res_unset = Environment::unset("");
IAT_CHECK_NOT(res_unset.has_value()); IAT_CHECK_NOT(res_unset.has_value());
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_basic_cycle); IAT_ADD_TEST(test_basic_cycle);
IAT_ADD_TEST(test_overwrite); IAT_ADD_TEST(test_overwrite);

View File

@ -20,9 +20,6 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, FileOps) IAT_BEGIN_BLOCK(Core, FileOps)
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
void cleanup_file(const Path &path) { void cleanup_file(const Path &path) {
std::error_code ec; std::error_code ec;
if (std::filesystem::exists(path, ec)) { if (std::filesystem::exists(path, ec)) {
@ -30,41 +27,30 @@ void cleanup_file(const Path &path) {
} }
} }
// -------------------------------------------------------------------------
// 1. Text File I/O
// -------------------------------------------------------------------------
auto test_text_io() -> bool { auto test_text_io() -> bool {
const Path path = "iatest_fileops_text.txt"; const Path path = "iatest_fileops_text.txt";
const String content = "Hello IACore FileOps!\nLine 2"; const String content = "Hello IACore FileOps!\nLine 2";
// 1. Write
const auto write_res = FileOps::write_text_file(path, content, true); const auto write_res = FileOps::write_text_file(path, content, true);
IAT_CHECK(write_res.has_value()); IAT_CHECK(write_res.has_value());
IAT_CHECK_EQ(*write_res, content.size()); IAT_CHECK_EQ(*write_res, content.size());
// 2. Read
const auto read_res = FileOps::read_text_file(path); const auto read_res = FileOps::read_text_file(path);
IAT_CHECK(read_res.has_value()); IAT_CHECK(read_res.has_value());
IAT_CHECK_EQ(*read_res, content); IAT_CHECK_EQ(*read_res, content);
// Cleanup
cleanup_file(path); cleanup_file(path);
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Binary File I/O
// -------------------------------------------------------------------------
auto test_binary_io() -> bool { auto test_binary_io() -> bool {
const Path path = "iatest_fileops_bin.bin"; const Path path = "iatest_fileops_bin.bin";
const Vec<u8> content = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF}; const Vec<u8> content = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
// 1. Write
const auto write_res = FileOps::write_binary_file(path, content, true); const auto write_res = FileOps::write_binary_file(path, content, true);
IAT_CHECK(write_res.has_value()); IAT_CHECK(write_res.has_value());
IAT_CHECK_EQ(*write_res, content.size()); IAT_CHECK_EQ(*write_res, content.size());
// 2. Read
const auto read_res = FileOps::read_binary_file(path); const auto read_res = FileOps::read_binary_file(path);
IAT_CHECK(read_res.has_value()); IAT_CHECK(read_res.has_value());
IAT_CHECK_EQ(read_res->size(), content.size()); IAT_CHECK_EQ(read_res->size(), content.size());
@ -73,22 +59,16 @@ auto test_binary_io() -> bool {
IAT_CHECK_EQ((*read_res)[i], content[i]); IAT_CHECK_EQ((*read_res)[i], content[i]);
} }
// Cleanup
cleanup_file(path); cleanup_file(path);
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Memory Mapping (File)
// -------------------------------------------------------------------------
auto test_file_mapping() -> bool { auto test_file_mapping() -> bool {
const Path path = "iatest_fileops_map.txt"; const Path path = "iatest_fileops_map.txt";
const String content = "MappedContent"; const String content = "MappedContent";
// Setup file
(void)FileOps::write_text_file(path, content, true); (void)FileOps::write_text_file(path, content, true);
// Map
usize size = 0; usize size = 0;
const auto map_res = FileOps::map_file(path, size); const auto map_res = FileOps::map_file(path, size);
IAT_CHECK(map_res.has_value()); IAT_CHECK(map_res.has_value());
@ -97,44 +77,34 @@ auto test_file_mapping() -> bool {
const u8 *ptr = *map_res; const u8 *ptr = *map_res;
IAT_CHECK(ptr != nullptr); IAT_CHECK(ptr != nullptr);
// Verify content via pointer
String read_back(reinterpret_cast<const char *>(ptr), size); String read_back(reinterpret_cast<const char *>(ptr), size);
IAT_CHECK_EQ(read_back, content); IAT_CHECK_EQ(read_back, content);
// Unmap
FileOps::unmap_file(ptr); FileOps::unmap_file(ptr);
cleanup_file(path); cleanup_file(path);
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Shared Memory
// -------------------------------------------------------------------------
auto test_shared_memory() -> bool { auto test_shared_memory() -> bool {
const String shm_name = "iatest_shm_block"; const String shm_name = "iatest_shm_block";
const usize shm_size = 4096; const usize shm_size = 4096;
// 1. Create as Owner
auto owner_res = FileOps::map_shared_memory(shm_name, shm_size, true); auto owner_res = FileOps::map_shared_memory(shm_name, shm_size, true);
IAT_CHECK(owner_res.has_value()); IAT_CHECK(owner_res.has_value());
u8 *owner_ptr = *owner_res; u8 *owner_ptr = *owner_res;
// Write data
std::memset(owner_ptr, 0, shm_size); std::memset(owner_ptr, 0, shm_size);
const String msg = "Shared Memory Message"; const String msg = "Shared Memory Message";
std::memcpy(owner_ptr, msg.data(), msg.size()); std::memcpy(owner_ptr, msg.data(), msg.size());
// 2. Open as Client
auto client_res = FileOps::map_shared_memory(shm_name, shm_size, false); auto client_res = FileOps::map_shared_memory(shm_name, shm_size, false);
IAT_CHECK(client_res.has_value()); IAT_CHECK(client_res.has_value());
u8 *client_ptr = *client_res; u8 *client_ptr = *client_res;
// Verify data
String read_msg(reinterpret_cast<const char *>(client_ptr), msg.size()); String read_msg(reinterpret_cast<const char *>(client_ptr), msg.size());
IAT_CHECK_EQ(read_msg, msg); IAT_CHECK_EQ(read_msg, msg);
// 3. Cleanup
FileOps::unmap_file(owner_ptr); FileOps::unmap_file(owner_ptr);
FileOps::unmap_file(client_ptr); FileOps::unmap_file(client_ptr);
FileOps::unlink_shared_memory(shm_name); FileOps::unlink_shared_memory(shm_name);
@ -142,14 +112,10 @@ auto test_shared_memory() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. Stream Integration
// -------------------------------------------------------------------------
auto test_stream_integration() -> bool { auto test_stream_integration() -> bool {
const Path path = "iatest_fileops_stream.bin"; const Path path = "iatest_fileops_stream.bin";
cleanup_file(path); cleanup_file(path);
// Write via StreamWriter
{ {
auto writer_res = FileOps::stream_to_file(path, true); auto writer_res = FileOps::stream_to_file(path, true);
IAT_CHECK(writer_res.has_value()); IAT_CHECK(writer_res.has_value());
@ -157,9 +123,8 @@ auto test_stream_integration() -> bool {
(void)writer.write<u32>(0x12345678); (void)writer.write<u32>(0x12345678);
(void)writer.write<u8>(0xFF); (void)writer.write<u8>(0xFF);
} // Destructor should flush/close }
// Read via StreamReader
{ {
auto reader_res = FileOps::stream_from_file(path); auto reader_res = FileOps::stream_from_file(path);
IAT_CHECK(reader_res.has_value()); IAT_CHECK(reader_res.has_value());
@ -167,7 +132,7 @@ auto test_stream_integration() -> bool {
auto val_u32 = reader.read<u32>(); auto val_u32 = reader.read<u32>();
IAT_CHECK(val_u32.has_value()); IAT_CHECK(val_u32.has_value());
IAT_CHECK_EQ(*val_u32, 0x12345678); IAT_CHECK_EQ(*val_u32, 0x12345678u);
auto val_u8 = reader.read<u8>(); auto val_u8 = reader.read<u8>();
IAT_CHECK(val_u8.has_value()); IAT_CHECK(val_u8.has_value());
@ -178,9 +143,6 @@ auto test_stream_integration() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_text_io); IAT_ADD_TEST(test_text_io);
IAT_ADD_TEST(test_binary_io); IAT_ADD_TEST(test_binary_io);

View File

@ -21,45 +21,30 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, IPC) IAT_BEGIN_BLOCK(Core, IPC)
// -------------------------------------------------------------------------
// 1. Layout Constraints
// -------------------------------------------------------------------------
auto test_layout_constraints() -> bool { auto test_layout_constraints() -> bool {
// Verify alignment
IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast<usize>(64)); IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast<usize>(64));
// Verify offsets to ensure cache-line isolation
// Header is at 0
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast<usize>(0)); IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast<usize>(0));
// moni_control should be at 64 (cache line 1)
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control), IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control),
static_cast<usize>(64)); static_cast<usize>(64));
// ControlBlock is 128 bytes (64 for producer, 64 for consumer)
// So mino_control should be at 64 + 128 = 192
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control), IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control),
static_cast<usize>(192)); static_cast<usize>(192));
// Data offsets should follow mino_control (192 + 128 = 320)
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset), IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset),
static_cast<usize>(320)); static_cast<usize>(320));
// Ensure the whole struct is a multiple of 64
IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast<usize>(0)); IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast<usize>(0));
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Manual Shared Memory & RingBuffer Setup
// -------------------------------------------------------------------------
auto test_manual_shm_ringbuffer() -> bool { auto test_manual_shm_ringbuffer() -> bool {
const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK"; const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK";
const usize shm_size = 16 * 1024; // 16KB const usize shm_size = 16 * 1024;
// 1. Create Shared Memory
// Ensure it doesn't exist first
FileOps::unlink_shared_memory(shm_name); FileOps::unlink_shared_memory(shm_name);
auto map_res = FileOps::map_shared_memory(shm_name, shm_size, true); auto map_res = FileOps::map_shared_memory(shm_name, shm_size, true);
@ -68,14 +53,10 @@ auto test_manual_shm_ringbuffer() -> bool {
u8 *base_ptr = *map_res; u8 *base_ptr = *map_res;
auto *layout = reinterpret_cast<IpcSharedMemoryLayout *>(base_ptr); auto *layout = reinterpret_cast<IpcSharedMemoryLayout *>(base_ptr);
// 2. Initialize Layout layout->meta.magic = 0xDEADBEEF;
// We simulate what IpcManager would do
layout->meta.magic = 0xDEADBEEF; // Dummy
layout->meta.version = 1; layout->meta.version = 1;
layout->meta.total_size = shm_size; layout->meta.total_size = shm_size;
// Define data regions
// Data starts after the layout struct
const usize header_size = IpcSharedMemoryLayout::get_header_size(); const usize header_size = IpcSharedMemoryLayout::get_header_size();
const usize data_available = shm_size - header_size; const usize data_available = shm_size - header_size;
const usize half_data = data_available / 2; const usize half_data = data_available / 2;
@ -86,9 +67,6 @@ auto test_manual_shm_ringbuffer() -> bool {
layout->mino_data_offset = header_size + half_data; layout->mino_data_offset = header_size + half_data;
layout->mino_data_size = half_data; layout->mino_data_size = half_data;
// 3. Initialize RingBuffers
// MONI (Manager Out, Node In)
// Manager is Owner of MONI
Span<u8> moni_data_span(base_ptr + layout->moni_data_offset, Span<u8> moni_data_span(base_ptr + layout->moni_data_offset,
static_cast<usize>(layout->moni_data_size)); static_cast<usize>(layout->moni_data_size));
auto moni_res = auto moni_res =
@ -96,9 +74,6 @@ auto test_manual_shm_ringbuffer() -> bool {
IAT_CHECK(moni_res.has_value()); IAT_CHECK(moni_res.has_value());
auto moni = std::move(*moni_res); auto moni = std::move(*moni_res);
// MINO (Manager In, Node Out)
// Manager is NOT Owner of MINO (Node writes to it)
// But for this test, let's pretend we are the Node for MINO to test writing
Span<u8> mino_data_span(base_ptr + layout->mino_data_offset, Span<u8> mino_data_span(base_ptr + layout->mino_data_offset,
static_cast<usize>(layout->mino_data_size)); static_cast<usize>(layout->mino_data_size));
auto mino_res = auto mino_res =
@ -106,16 +81,12 @@ auto test_manual_shm_ringbuffer() -> bool {
IAT_CHECK(mino_res.has_value()); IAT_CHECK(mino_res.has_value());
auto _ = std::move(*mino_res); auto _ = std::move(*mino_res);
// 4. Test Data Flow
// Write to MONI
String msg = "IPC_TEST_MESSAGE"; String msg = "IPC_TEST_MESSAGE";
IAT_CHECK( IAT_CHECK(
moni.push(100, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), moni.push(100, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()),
msg.size())) msg.size()))
.has_value()); .has_value());
// Read from MONI (Simulate Node reading)
// Create a reader view
auto moni_reader_res = auto moni_reader_res =
RingBufferView::create(&layout->moni_control, moni_data_span, false); RingBufferView::create(&layout->moni_control, moni_data_span, false);
IAT_CHECK(moni_reader_res.has_value()); IAT_CHECK(moni_reader_res.has_value());
@ -125,22 +96,18 @@ auto test_manual_shm_ringbuffer() -> bool {
u8 buffer[128]; u8 buffer[128];
auto pop_res = moni_reader.pop(header, Span<u8>(buffer, 128)); auto pop_res = moni_reader.pop(header, Span<u8>(buffer, 128));
IAT_CHECK(pop_res.has_value()); IAT_CHECK(pop_res.has_value());
IAT_CHECK(pop_res->has_value()); // Should have data IAT_CHECK(pop_res->has_value());
IAT_CHECK_EQ(header.id, static_cast<u16>(100)); IAT_CHECK_EQ(header.id, static_cast<u16>(100));
String received((char *)buffer, *pop_res.value()); String received((char *)buffer, *pop_res.value());
IAT_CHECK_EQ(received, msg); IAT_CHECK_EQ(received, msg);
// Cleanup
FileOps::unmap_file(base_ptr); FileOps::unmap_file(base_ptr);
FileOps::unlink_shared_memory(shm_name); FileOps::unlink_shared_memory(shm_name);
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Manager Instantiation
// -------------------------------------------------------------------------
class TestManager : public IpcManager { class TestManager : public IpcManager {
public: public:
void on_signal(NativeProcessID, u8) override {} void on_signal(NativeProcessID, u8) override {}

View File

@ -18,31 +18,20 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// Test Structures for Serialization
// -----------------------------------------------------------------------------
struct UserProfile { struct UserProfile {
String username; String username;
u32 id; u32 id;
bool is_active; bool is_active;
Vec<String> roles; Vec<String> roles;
// Equality operator for verification
bool operator==(const UserProfile &other) const { bool operator==(const UserProfile &other) const {
return username == other.username && id == other.id && return username == other.username && id == other.id &&
is_active == other.is_active && roles == other.roles; is_active == other.is_active && roles == other.roles;
} }
}; };
// -----------------------------------------------------------------------------
// Test Block Definition
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, JSON) IAT_BEGIN_BLOCK(Core, JSON)
// -------------------------------------------------------------------------
// 1. Dynamic JSON (nlohmann::json)
// -------------------------------------------------------------------------
auto test_dynamic_parse() -> bool { auto test_dynamic_parse() -> bool {
const String json_text = R"({ const String json_text = R"({
"string": "Hello World", "string": "Hello World",
@ -58,7 +47,6 @@ auto test_dynamic_parse() -> bool {
const auto &j = *res; const auto &j = *res;
// Type checks and value retrieval
IAT_CHECK(j["string"].is_string()); IAT_CHECK(j["string"].is_string());
IAT_CHECK_EQ(j["string"].get<String>(), String("Hello World")); IAT_CHECK_EQ(j["string"].get<String>(), String("Hello World"));
@ -88,7 +76,6 @@ auto test_dynamic_encode() -> bool {
const String encoded = Json::encode(j); const String encoded = Json::encode(j);
// Simple containment check as key order isn't guaranteed
IAT_CHECK(encoded.find("IACore") != String::npos); IAT_CHECK(encoded.find("IACore") != String::npos);
IAT_CHECK(encoded.find("version") != String::npos); IAT_CHECK(encoded.find("version") != String::npos);
IAT_CHECK(encoded.find("2") != String::npos); IAT_CHECK(encoded.find("2") != String::npos);
@ -97,31 +84,25 @@ auto test_dynamic_encode() -> bool {
} }
auto test_parse_invalid() -> bool { auto test_parse_invalid() -> bool {
const String bad_json = "{ key: value }"; // Missing quotes const String bad_json = "{ key: value }";
auto res = Json::parse(bad_json); auto res = Json::parse(bad_json);
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Struct Serialization (Glaze)
// -------------------------------------------------------------------------
auto test_struct_round_trip() -> bool { auto test_struct_round_trip() -> bool {
UserProfile original{.username = "test_user", UserProfile original{.username = "test_user",
.id = 12345, .id = 12345,
.is_active = true, .is_active = true,
.roles = {"admin", "editor"}}; .roles = {"admin", "editor"}};
// Struct -> JSON
auto encode_res = Json::encode_struct(original); auto encode_res = Json::encode_struct(original);
IAT_CHECK(encode_res.has_value()); IAT_CHECK(encode_res.has_value());
String json_str = *encode_res; String json_str = *encode_res;
// Verify JSON structure roughly
IAT_CHECK(json_str.find("test_user") != String::npos); IAT_CHECK(json_str.find("test_user") != String::npos);
IAT_CHECK(json_str.find("roles") != String::npos); IAT_CHECK(json_str.find("roles") != String::npos);
// JSON -> Struct
auto decode_res = Json::parse_to_struct<UserProfile>(json_str); auto decode_res = Json::parse_to_struct<UserProfile>(json_str);
IAT_CHECK(decode_res.has_value()); IAT_CHECK(decode_res.has_value());
@ -138,9 +119,6 @@ auto test_struct_parse_error() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Read-Only Parsing (simdjson)
// -------------------------------------------------------------------------
auto test_read_only() -> bool { auto test_read_only() -> bool {
const String json_text = R"({ const String json_text = R"({
"id": 999, "id": 999,
@ -154,19 +132,16 @@ auto test_read_only() -> bool {
auto &doc = *res; auto &doc = *res;
simdjson::dom::element root = doc.root(); simdjson::dom::element root = doc.root();
// Check ID
u64 id = 0; u64 id = 0;
auto err_id = root["id"].get(id); auto err_id = root["id"].get(id);
IAT_CHECK(!err_id); IAT_CHECK(!err_id);
IAT_CHECK_EQ(id, 999ULL); IAT_CHECK_EQ(id, 999ULL);
// Check Name
std::string_view name; std::string_view name;
auto err_name = root["name"].get(name); auto err_name = root["name"].get(name);
IAT_CHECK(!err_name); IAT_CHECK(!err_name);
IAT_CHECK_EQ(String(name), String("Simd")); IAT_CHECK_EQ(String(name), String("Simd"));
// Check Array
simdjson::dom::array scores; simdjson::dom::array scores;
auto err_arr = root["scores"].get(scores); auto err_arr = root["scores"].get(scores);
IAT_CHECK(!err_arr); IAT_CHECK(!err_arr);
@ -175,9 +150,6 @@ auto test_read_only() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_dynamic_parse); IAT_ADD_TEST(test_dynamic_parse);
IAT_ADD_TEST(test_dynamic_encode); IAT_ADD_TEST(test_dynamic_encode);

View File

@ -24,14 +24,12 @@ IAT_BEGIN_BLOCK(Core, Logger)
static constexpr const char *LOG_FILE = "iacore_test_log.txt"; static constexpr const char *LOG_FILE = "iacore_test_log.txt";
auto test_file_logging() -> bool { auto test_file_logging() -> bool {
// 1. Enable logging to disk
const auto res = Logger::enable_logging_to_disk(LOG_FILE); const auto res = Logger::enable_logging_to_disk(LOG_FILE);
IAT_CHECK(res.has_value()); IAT_CHECK(res.has_value());
// 2. Set level to Trace to ensure we capture everything
Logger::set_log_level(Logger::LogLevel::Trace); Logger::set_log_level(Logger::LogLevel::Trace);
// 3. Log unique messages
const String msg_info = "Test_Info_Msg_123"; const String msg_info = "Test_Info_Msg_123";
const String msg_err = "Test_Error_Msg_456"; const String msg_err = "Test_Error_Msg_456";
const String msg_warn = "Test_Warn_Msg_789"; const String msg_warn = "Test_Warn_Msg_789";
@ -40,10 +38,8 @@ auto test_file_logging() -> bool {
Logger::error("{}", msg_err); Logger::error("{}", msg_err);
Logger::warn("{}", msg_warn); Logger::warn("{}", msg_warn);
// 4. Flush
Logger::flush_logs(); Logger::flush_logs();
// 5. Read back
auto read_res = FileOps::read_text_file(LOG_FILE); auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) { if (!read_res) {
std::cout << console::YELLOW << " Warning: Could not read log file (" std::cout << console::YELLOW << " Warning: Could not read log file ("
@ -58,7 +54,6 @@ auto test_file_logging() -> bool {
IAT_CHECK(content.find(msg_err) != String::npos); IAT_CHECK(content.find(msg_err) != String::npos);
IAT_CHECK(content.find(msg_warn) != String::npos); IAT_CHECK(content.find(msg_warn) != String::npos);
// Check for log tags
IAT_CHECK(content.find("INFO") != String::npos); IAT_CHECK(content.find("INFO") != String::npos);
IAT_CHECK(content.find("ERROR") != String::npos); IAT_CHECK(content.find("ERROR") != String::npos);
IAT_CHECK(content.find("WARN") != String::npos); IAT_CHECK(content.find("WARN") != String::npos);
@ -67,7 +62,7 @@ auto test_file_logging() -> bool {
} }
auto test_log_levels() -> bool { auto test_log_levels() -> bool {
// 1. Set level to Warn (Trace < Debug < Info < Warn < Error)
Logger::set_log_level(Logger::LogLevel::Warn); Logger::set_log_level(Logger::LogLevel::Warn);
const String unique_info = "Hidden_Info_Msg"; const String unique_info = "Hidden_Info_Msg";
@ -85,9 +80,8 @@ auto test_log_levels() -> bool {
const String content = *read_res; const String content = *read_res;
// Info should NOT be present
IAT_CHECK(content.find(unique_info) == String::npos); IAT_CHECK(content.find(unique_info) == String::npos);
// Warn SHOULD be present
IAT_CHECK(content.find(unique_warn) != String::npos); IAT_CHECK(content.find(unique_warn) != String::npos);
return true; return true;

View File

@ -33,7 +33,7 @@ IACORE_MAIN() {
<< "===============================================================\n" << "===============================================================\n"
<< console::RESET << "\n"; << console::RESET << "\n";
const i32 result = Test::TestRegistry::run_all(); Const<i32> result = Test::TestRegistry::run_all();
SocketOps::terminate(); SocketOps::terminate();

View File

@ -21,9 +21,6 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, Platform) IAT_BEGIN_BLOCK(Core, Platform)
// -------------------------------------------------------------------------
// 1. OS Name Detection
// -------------------------------------------------------------------------
auto test_os_name() -> bool { auto test_os_name() -> bool {
const char *os_name = Platform::get_operating_system_name(); const char *os_name = Platform::get_operating_system_name();
IAT_CHECK(os_name != nullptr); IAT_CHECK(os_name != nullptr);
@ -44,9 +41,6 @@ auto test_os_name() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Architecture Name Detection
// -------------------------------------------------------------------------
auto test_arch_name() -> bool { auto test_arch_name() -> bool {
const char *arch_name = Platform::get_architecture_name(); const char *arch_name = Platform::get_architecture_name();
IAT_CHECK(arch_name != nullptr); IAT_CHECK(arch_name != nullptr);
@ -65,60 +59,45 @@ auto test_arch_name() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. CPU Capabilities
// -------------------------------------------------------------------------
auto test_capabilities() -> bool { auto test_capabilities() -> bool {
// Initialize detection
const bool check_result = Platform::check_cpu(); const bool check_result = Platform::check_cpu();
IAT_CHECK(check_result); IAT_CHECK(check_result);
const auto &caps = Platform::get_capabilities(); const auto &caps = Platform::get_capabilities();
// We verify that we can access the capabilities struct.
// The actual value of hardware_crc32 depends on the host machine,
// so we cannot assert true or false, but we ensure no crash occurs.
volatile bool has_crc = caps.hardware_crc32; volatile bool has_crc = caps.hardware_crc32;
(void)has_crc; (void)has_crc;
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. CPUID (x64 Only)
// -------------------------------------------------------------------------
#if IA_ARCH_X64 #if IA_ARCH_X64
auto test_cpuid() -> bool { auto test_cpuid() -> bool {
i32 regs[4] = {0}; i32 regs[4] = {0};
// Call CPUID with Function 0 (Vendor ID)
Platform::cpuid(0, 0, regs); Platform::cpuid(0, 0, regs);
// EAX (regs[0]) holds max supported function. Should be > 0 on any modern
// CPU.
IAT_CHECK(regs[0] >= 0); IAT_CHECK(regs[0] >= 0);
// EBX, EDX, ECX hold the vendor string.
// The order for the string is EBX, EDX, ECX.
char vendor[13]; char vendor[13];
std::memset(vendor, 0, 13); std::memset(vendor, 0, 13);
std::memcpy(vendor, &regs[1], 4); // EBX std::memcpy(vendor, &regs[1], 4);
std::memcpy(vendor + 4, &regs[3], 4); // EDX std::memcpy(vendor + 4, &regs[3], 4);
std::memcpy(vendor + 8, &regs[2], 4); // ECX std::memcpy(vendor + 8, &regs[2], 4);
vendor[12] = '\0'; vendor[12] = '\0';
const String vendor_str(vendor); const String vendor_str(vendor);
IAT_CHECK(!vendor_str.empty()); IAT_CHECK(!vendor_str.empty());
// Check against common vendors to ensure registers contained valid ASCII
bool is_known = bool is_known =
(vendor_str == "GenuineIntel" || vendor_str == "AuthenticAMD" || (vendor_str == "GenuineIntel" || vendor_str == "AuthenticAMD" ||
vendor_str == "KVMKVMKVM" || vendor_str == "Microsoft Hv" || vendor_str == "KVMKVMKVM" || vendor_str == "Microsoft Hv" ||
vendor_str == "VBoxVBoxVBox"); vendor_str == "VBoxVBoxVBox");
if (!is_known) { if (!is_known) {
// Not a failure, just an unknown CPU vendor (or virtualization)
std::cout << " [Info] Unknown CPU Vendor: " << vendor_str << "\n"; std::cout << " [Info] Unknown CPU Vendor: " << vendor_str << "\n";
} }
@ -126,9 +105,6 @@ auto test_cpuid() -> bool {
} }
#endif #endif
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_os_name); IAT_ADD_TEST(test_os_name);
IAT_ADD_TEST(test_arch_name); IAT_ADD_TEST(test_arch_name);

View File

@ -18,9 +18,6 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// 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"
#define CMD_ARG_PREFIX "/c echo" #define CMD_ARG_PREFIX "/c echo"
@ -33,11 +30,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, ProcessOps) IAT_BEGIN_BLOCK(Core, ProcessOps)
// -------------------------------------------------------------------------
// 1. Basic Execution (Exit Code 0)
// -------------------------------------------------------------------------
auto test_basic_run() -> bool { auto test_basic_run() -> bool {
// Simple "echo hello"
String captured; String captured;
const auto result = const auto result =
@ -45,26 +39,19 @@ auto test_basic_run() -> bool {
[&](StringView line) { captured = line; }); [&](StringView 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);
// We check if "HelloIA" is contained or equal.
IAT_CHECK(captured.find("HelloIA") != String::npos); IAT_CHECK(captured.find("HelloIA") != String::npos);
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Argument Parsing
// -------------------------------------------------------------------------
auto test_arguments() -> bool { auto test_arguments() -> bool {
Vec<String> lines; Vec<String> lines;
// Echo two distinct words.
// Windows: cmd.exe /c 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.empty() && args[0] == ' ') { if (!args.empty() && args[0] == ' ') {
args.erase(0, 1); // cleanup space if prefix empty args.erase(0, 1);
} }
const auto result = const auto result =
@ -75,19 +62,12 @@ auto test_arguments() -> bool {
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
IAT_CHECK(lines.size() > 0); IAT_CHECK(lines.size() > 0);
// Output should contain "one two"
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
// -------------------------------------------------------------------------
auto test_exit_codes() -> bool { auto test_exit_codes() -> bool {
// We need a command that returns non-zero.
// Windows: cmd /c exit 1
// Linux: /bin/sh -c "exit 1"
String cmd; String cmd;
String arg; String arg;
@ -97,7 +77,7 @@ auto test_exit_codes() -> bool {
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\"";
#endif #endif
const auto result = const auto result =
@ -109,21 +89,15 @@ auto test_exit_codes() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Missing Executable Handling
// -------------------------------------------------------------------------
auto test_missing_exe() -> bool { auto test_missing_exe() -> bool {
// Try to run a random string
const auto result = const auto result =
ProcessOps::spawn_process_sync("sdflkjghsdflkjg", "", [](StringView) {}); ProcessOps::spawn_process_sync("sdflkjghsdflkjg", "", [](StringView) {});
// Windows: CreateProcess usually fails -> returns unexpected
// 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());
#else #else
// 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
@ -131,18 +105,12 @@ auto test_missing_exe() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. Line Buffer Logic (The 4096 split test)
// -------------------------------------------------------------------------
auto test_large_output() -> bool { auto test_large_output() -> bool {
// Need to generate output larger than the internal 4096 buffer
// to ensure the "partial line" logic works when a line crosses a buffer
// boundary.
String massive_string; String massive_string;
massive_string.reserve(5000); massive_string.reserve(5000);
for (i32 i = 0; i < 500; ++i) { for (i32 i = 0; i < 500; ++i) {
massive_string += "1234567890"; // 5000 chars massive_string += "1234567890";
} }
String cmd; String cmd;
@ -150,7 +118,7 @@ auto test_large_output() -> bool {
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
cmd = "cmd.exe"; cmd = "cmd.exe";
// Windows has command line length limits (~8k), 5k is safe.
arg = "/c echo " + massive_string; arg = "/c echo " + massive_string;
#else #else
cmd = "/bin/echo"; cmd = "/bin/echo";
@ -164,19 +132,12 @@ auto test_large_output() -> bool {
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
// If the LineBuffer failed to stitch chunks, the length wouldn't match
// or we would get multiple callbacks if we expected 1 line.
IAT_CHECK_EQ(captured.length(), massive_string.length()); IAT_CHECK_EQ(captured.length(), massive_string.length());
return true; return true;
} }
// -------------------------------------------------------------------------
// 6. Multi-Line Handling
// -------------------------------------------------------------------------
auto test_multi_line() -> bool { auto test_multi_line() -> bool {
// Windows: cmd /c "echo A && echo B"
// Linux: /bin/sh -c "echo A; echo B"
String cmd; String cmd;
String arg; String arg;
@ -206,22 +167,14 @@ auto test_multi_line() -> bool {
IAT_CHECK(found_a); IAT_CHECK(found_a);
IAT_CHECK(found_b); IAT_CHECK(found_b);
// We expect at least 2 lines.
// (Windows sometimes echoes the command itself depending on echo settings,
// but we check contents)
IAT_CHECK(line_count >= 2); IAT_CHECK(line_count >= 2);
return true; return true;
} }
// -------------------------------------------------------------------------
// 6. Complex Command Line Arguments Handling
// -------------------------------------------------------------------------
auto test_complex_arguments() -> bool { auto test_complex_arguments() -> bool {
// Should parse as 3 arguments:
// 1. -DDEFINED_MSG="Hello World"
// 2. -v
// 3. path/to/file
const String complex_args = const String complex_args =
"-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file"; "-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
@ -241,14 +194,10 @@ auto test_complex_arguments() -> bool {
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
// Verify the quotes were preserved in the output
IAT_CHECK(captured.find("Hello World") != String::npos); IAT_CHECK(captured.find("Hello World") != String::npos);
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_basic_run); IAT_ADD_TEST(test_basic_run);
IAT_ADD_TEST(test_arguments); IAT_ADD_TEST(test_arguments);

View File

@ -20,31 +20,23 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, RingBuffer) IAT_BEGIN_BLOCK(Core, RingBuffer)
// -------------------------------------------------------------------------
// 1. Basic Push Pop
// -------------------------------------------------------------------------
auto test_push_pop() -> bool { auto test_push_pop() -> bool {
// Allocate raw memory for the ring buffer
// ControlBlock (128 bytes) + Data
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 1024); Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 1024);
// Initialize as OWNER (Producer)
auto producer_res = RingBufferView::create(Span<u8>(memory), true); auto producer_res = RingBufferView::create(Span<u8>(memory), true);
IAT_CHECK(producer_res.has_value()); IAT_CHECK(producer_res.has_value());
auto producer = std::move(*producer_res); auto producer = std::move(*producer_res);
// Initialize as CONSUMER (Pointer to same memory)
auto consumer_res = RingBufferView::create(Span<u8>(memory), false); auto consumer_res = RingBufferView::create(Span<u8>(memory), false);
IAT_CHECK(consumer_res.has_value()); IAT_CHECK(consumer_res.has_value());
auto consumer = std::move(*consumer_res); auto consumer = std::move(*consumer_res);
// Data to send
String msg = "Hello RingBuffer"; String msg = "Hello RingBuffer";
const auto push_res = producer.push( const auto push_res = producer.push(
1, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size())); 1, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size()));
IAT_CHECK(push_res.has_value()); IAT_CHECK(push_res.has_value());
// Read back
RingBufferView::PacketHeader header; RingBufferView::PacketHeader header;
u8 read_buf[128]; u8 read_buf[128];
@ -65,38 +57,28 @@ auto test_push_pop() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Wrap Around
// -------------------------------------------------------------------------
auto test_wrap_around() -> bool { auto test_wrap_around() -> bool {
// Small buffer to force wrapping quickly
// Capacity will be 100 bytes
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 100); Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 100);
auto rb_res = RingBufferView::create(Span<u8>(memory), true); auto rb_res = RingBufferView::create(Span<u8>(memory), true);
IAT_CHECK(rb_res.has_value()); IAT_CHECK(rb_res.has_value());
auto rb = std::move(*rb_res); auto rb = std::move(*rb_res);
// Fill buffer to near end
// Push 80 bytes
Vec<u8> junk(80, 0xFF); Vec<u8> junk(80, 0xFF);
const auto push1 = rb.push(1, junk); const auto push1 = rb.push(1, junk);
IAT_CHECK(push1.has_value()); IAT_CHECK(push1.has_value());
// Pop them to advance READ cursor
RingBufferView::PacketHeader header; RingBufferView::PacketHeader header;
u8 out_buf[100]; u8 out_buf[100];
const auto pop1 = rb.pop(header, out_buf); const auto pop1 = rb.pop(header, out_buf);
IAT_CHECK(pop1.has_value()); IAT_CHECK(pop1.has_value());
IAT_CHECK(pop1->has_value()); IAT_CHECK(pop1->has_value());
// Now READ and WRITE are near index 80.
// Pushing 40 bytes should trigger a wrap-around (split write)
Vec<u8> wrap_data(40, 0xAA); Vec<u8> wrap_data(40, 0xAA);
const auto push2 = rb.push(2, wrap_data); const auto push2 = rb.push(2, wrap_data);
IAT_CHECK(push2.has_value()); IAT_CHECK(push2.has_value());
// Pop and verify integrity
const auto pop2 = rb.pop(header, out_buf); const auto pop2 = rb.pop(header, out_buf);
IAT_CHECK(pop2.has_value()); IAT_CHECK(pop2.has_value());
IAT_CHECK(pop2->has_value()); IAT_CHECK(pop2->has_value());
@ -104,7 +86,6 @@ auto test_wrap_around() -> bool {
const usize pop_size = *pop2.value(); const usize pop_size = *pop2.value();
IAT_CHECK_EQ(pop_size, static_cast<usize>(40)); IAT_CHECK_EQ(pop_size, static_cast<usize>(40));
// Check if data is intact
bool match = true; bool match = true;
for (usize i = 0; i < 40; i++) { for (usize i = 0; i < 40; i++) {
if (out_buf[i] != 0xAA) { if (out_buf[i] != 0xAA) {

View File

@ -20,82 +20,56 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, SocketOps) IAT_BEGIN_BLOCK(Core, SocketOps)
// -------------------------------------------------------------------------
// 1. Initialization Logic
// -------------------------------------------------------------------------
auto test_initialization() -> bool { auto test_initialization() -> bool {
IAT_CHECK(SocketOps::is_initialized()); IAT_CHECK(SocketOps::is_initialized());
// Increment ref count
const auto res = SocketOps::initialize(); const auto res = SocketOps::initialize();
IAT_CHECK(res.has_value()); IAT_CHECK(res.has_value());
// Decrement ref count
SocketOps::terminate(); SocketOps::terminate();
// Should still be initialized (ref count > 0)
IAT_CHECK(SocketOps::is_initialized()); IAT_CHECK(SocketOps::is_initialized());
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Port Availability Checks
// -------------------------------------------------------------------------
auto test_port_availability() -> bool { auto test_port_availability() -> bool {
// We cannot easily guarantee a port is free or taken without binding it,
// and SocketOps doesn't expose a generic TCP bind in its public API
// (only Unix sockets).
// However, we can verify the functions execute without crashing.
const u16 port = 54321; const u16 port = 54321;
// These return bools, we just ensure they run.
(void)SocketOps::is_port_available_tcp(port); (void)SocketOps::is_port_available_tcp(port);
(void)SocketOps::is_port_available_udp(port); (void)SocketOps::is_port_available_udp(port);
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Unix Domain Socket Lifecycle (Create, Bind, Listen, Connect)
// -------------------------------------------------------------------------
auto test_unix_socket_lifecycle() -> bool { auto test_unix_socket_lifecycle() -> bool {
const String socket_path = "iatest_ipc.sock"; const String socket_path = "iatest_ipc.sock";
// Ensure clean state
SocketOps::unlink_file(socket_path.c_str()); SocketOps::unlink_file(socket_path.c_str());
// 1. Create Server Socket
auto server_res = SocketOps::create_unix_socket(); auto server_res = SocketOps::create_unix_socket();
IAT_CHECK(server_res.has_value()); IAT_CHECK(server_res.has_value());
SocketHandle server = *server_res; SocketHandle server = *server_res;
// 2. Bind
auto bind_res = SocketOps::bind_unix_socket(server, socket_path.c_str()); auto bind_res = SocketOps::bind_unix_socket(server, socket_path.c_str());
if (!bind_res) { if (!bind_res) {
// If bind fails (e.g. permissions), we clean up and fail the test
SocketOps::close(server); SocketOps::close(server);
return false; return false;
} }
// 3. Listen
auto listen_res = SocketOps::listen(server); auto listen_res = SocketOps::listen(server);
IAT_CHECK(listen_res.has_value()); IAT_CHECK(listen_res.has_value());
// 4. Create Client Socket
auto client_res = SocketOps::create_unix_socket(); auto client_res = SocketOps::create_unix_socket();
IAT_CHECK(client_res.has_value()); IAT_CHECK(client_res.has_value());
SocketHandle client = *client_res; SocketHandle client = *client_res;
// 5. Connect
// Note: This relies on the OS backlog. We aren't calling accept() on the
// server, but connect() should succeed if the server is listening.
auto connect_res = auto connect_res =
SocketOps::connect_unix_socket(client, socket_path.c_str()); SocketOps::connect_unix_socket(client, socket_path.c_str());
IAT_CHECK(connect_res.has_value()); IAT_CHECK(connect_res.has_value());
// 6. Cleanup
SocketOps::close(client); SocketOps::close(client);
SocketOps::close(server); SocketOps::close(server);
SocketOps::unlink_file(socket_path.c_str()); SocketOps::unlink_file(socket_path.c_str());
@ -103,20 +77,15 @@ auto test_unix_socket_lifecycle() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Unix Socket Error Handling
// -------------------------------------------------------------------------
auto test_unix_socket_errors() -> bool { auto test_unix_socket_errors() -> bool {
const String socket_path = "iatest_missing.sock"; const String socket_path = "iatest_missing.sock";
// Ensure it doesn't exist
SocketOps::unlink_file(socket_path.c_str()); SocketOps::unlink_file(socket_path.c_str());
auto client_res = SocketOps::create_unix_socket(); auto client_res = SocketOps::create_unix_socket();
IAT_CHECK(client_res.has_value()); IAT_CHECK(client_res.has_value());
SocketHandle client = *client_res; SocketHandle client = *client_res;
// Should fail to connect to non-existent file
auto connect_res = auto connect_res =
SocketOps::connect_unix_socket(client, socket_path.c_str()); SocketOps::connect_unix_socket(client, socket_path.c_str());
IAT_CHECK_NOT(connect_res.has_value()); IAT_CHECK_NOT(connect_res.has_value());
@ -126,9 +95,6 @@ auto test_unix_socket_errors() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_initialization); IAT_ADD_TEST(test_initialization);
IAT_ADD_TEST(test_port_availability); IAT_ADD_TEST(test_port_availability);

View File

@ -20,20 +20,15 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, StreamReader) IAT_BEGIN_BLOCK(Core, StreamReader)
// -------------------------------------------------------------------------
// 1. Basic Primitive Reading (u8)
// -------------------------------------------------------------------------
auto test_read_uint8() -> bool { auto test_read_uint8() -> bool {
u8 data[] = {0xAA, 0xBB, 0xCC}; u8 data[] = {0xAA, 0xBB, 0xCC};
StreamReader reader(data); StreamReader reader(data);
// Read First Byte
auto val1 = reader.read<u8>(); auto val1 = reader.read<u8>();
IAT_CHECK(val1.has_value()); IAT_CHECK(val1.has_value());
IAT_CHECK_EQ(*val1, 0xAA); IAT_CHECK_EQ(*val1, 0xAA);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(1)); IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(1));
// Read Second Byte
auto val2 = reader.read<u8>(); auto val2 = reader.read<u8>();
IAT_CHECK(val2.has_value()); IAT_CHECK(val2.has_value());
IAT_CHECK_EQ(*val2, 0xBB); IAT_CHECK_EQ(*val2, 0xBB);
@ -41,12 +36,8 @@ auto test_read_uint8() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Multi-byte Reading (Endianness check)
// -------------------------------------------------------------------------
auto test_read_multi_byte() -> bool { auto test_read_multi_byte() -> bool {
// 0x04030201 in Little Endian memory layout
// IACore always assumes a Little Endian machine
u8 data[] = {0x01, 0x02, 0x03, 0x04}; u8 data[] = {0x01, 0x02, 0x03, 0x04};
StreamReader reader(data); StreamReader reader(data);
@ -61,12 +52,9 @@ auto test_read_multi_byte() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Floating Point (Approx check)
// -------------------------------------------------------------------------
auto test_read_float() -> bool { auto test_read_float() -> bool {
const f32 pi = 3.14159f; const f32 pi = 3.14159f;
// Bit-cast float to bytes for setup
u8 data[4]; u8 data[4];
std::memcpy(data, &pi, 4); std::memcpy(data, &pi, 4);
@ -79,49 +67,37 @@ auto test_read_float() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Batch Buffer Reading
// -------------------------------------------------------------------------
auto test_read_buffer() -> bool { auto test_read_buffer() -> bool {
u8 src[] = {1, 2, 3, 4, 5}; u8 src[] = {1, 2, 3, 4, 5};
u8 dst[3] = {0}; u8 dst[3] = {0};
StreamReader reader(src); StreamReader reader(src);
// Read 3 bytes into dst
const auto res = reader.read(dst, 3); const auto res = reader.read(dst, 3);
IAT_CHECK(res.has_value()); IAT_CHECK(res.has_value());
// Verify dst content
IAT_CHECK_EQ(dst[0], 1); IAT_CHECK_EQ(dst[0], 1);
IAT_CHECK_EQ(dst[1], 2); IAT_CHECK_EQ(dst[1], 2);
IAT_CHECK_EQ(dst[2], 3); IAT_CHECK_EQ(dst[2], 3);
// Verify cursor
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(3)); IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(3));
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. Navigation (Seek, Skip, Remaining)
// -------------------------------------------------------------------------
auto test_navigation() -> bool { auto test_navigation() -> bool {
u8 data[10] = {0}; // Zero init u8 data[10] = {0};
StreamReader reader(data); StreamReader reader(data);
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(10)); IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(10));
// Skip
reader.skip(5); reader.skip(5);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(5)); IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(5));
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(5)); IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(5));
// Skip clamping reader.skip(100);
reader.skip(100); // Should clamp to 10
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(10)); IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(10));
IAT_CHECK(reader.is_eof()); IAT_CHECK(reader.is_eof());
// Seek
reader.seek(2); reader.seek(2);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(2)); IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(2));
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(8)); IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(8));
@ -130,22 +106,16 @@ auto test_navigation() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 6. Error Handling (EOF Protection)
// -------------------------------------------------------------------------
auto test_boundary_checks() -> bool { auto test_boundary_checks() -> bool {
u8 data[] = {0x00, 0x00}; u8 data[] = {0x00, 0x00};
StreamReader reader(data); StreamReader reader(data);
// Valid read
(void)reader.read<u16>(); (void)reader.read<u16>();
IAT_CHECK(reader.is_eof()); IAT_CHECK(reader.is_eof());
// Invalid Read Primitive
auto val = reader.read<u8>(); auto val = reader.read<u8>();
IAT_CHECK_NOT(val.has_value()); // Should be unexpected IAT_CHECK_NOT(val.has_value());
// Invalid Batch Read
u8 buf[1]; u8 buf[1];
auto batch = reader.read(buf, 1); auto batch = reader.read(buf, 1);
IAT_CHECK_NOT(batch.has_value()); IAT_CHECK_NOT(batch.has_value());
@ -153,9 +123,6 @@ auto test_boundary_checks() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_read_uint8); IAT_ADD_TEST(test_read_uint8);
IAT_ADD_TEST(test_read_multi_byte); IAT_ADD_TEST(test_read_multi_byte);

View File

@ -21,24 +21,16 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, StreamWriter) IAT_BEGIN_BLOCK(Core, StreamWriter)
// -------------------------------------------------------------------------
// 1. Memory Writer (Dynamic Vector)
// -------------------------------------------------------------------------
auto test_memory_writer() -> bool { auto test_memory_writer() -> bool {
StreamWriter writer; StreamWriter writer;
// Write single byte repeated
IAT_CHECK(writer.write(static_cast<u8>(0xAA), 1).has_value()); IAT_CHECK(writer.write(static_cast<u8>(0xAA), 1).has_value());
// Write primitive (u32) - 0x12345678
// Little Endian: 78 56 34 12
const u32 val = 0x12345678; const u32 val = 0x12345678;
IAT_CHECK(writer.write(val).has_value()); IAT_CHECK(writer.write(val).has_value());
// Check cursor
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(1 + 4)); IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(1 + 4));
// Check data content
const u8 *ptr = writer.data(); const u8 *ptr = writer.data();
IAT_CHECK_EQ(ptr[0], 0xAA); IAT_CHECK_EQ(ptr[0], 0xAA);
IAT_CHECK_EQ(ptr[1], 0x78); IAT_CHECK_EQ(ptr[1], 0x78);
@ -47,26 +39,19 @@ auto test_memory_writer() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Fixed Buffer Writer (Non-Owning)
// -------------------------------------------------------------------------
auto test_fixed_buffer() -> bool { auto test_fixed_buffer() -> bool {
u8 buffer[4] = {0}; u8 buffer[4] = {0};
StreamWriter writer(Span<u8>(buffer, 4)); StreamWriter writer(Span<u8>(buffer, 4));
// Write 2 bytes
IAT_CHECK(writer.write(static_cast<u8>(0xFF), 2).has_value()); IAT_CHECK(writer.write(static_cast<u8>(0xFF), 2).has_value());
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(2)); IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(2));
// Write 2 more bytes
IAT_CHECK(writer.write(static_cast<u8>(0xEE), 2).has_value()); IAT_CHECK(writer.write(static_cast<u8>(0xEE), 2).has_value());
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(4)); IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(4));
// Write 1 more byte -> Should fail (Out of bounds)
const auto res = writer.write(static_cast<u8>(0x00), 1); const auto res = writer.write(static_cast<u8>(0x00), 1);
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());
// Verify content
IAT_CHECK_EQ(buffer[0], 0xFF); IAT_CHECK_EQ(buffer[0], 0xFF);
IAT_CHECK_EQ(buffer[1], 0xFF); IAT_CHECK_EQ(buffer[1], 0xFF);
IAT_CHECK_EQ(buffer[2], 0xEE); IAT_CHECK_EQ(buffer[2], 0xEE);
@ -75,13 +60,9 @@ auto test_fixed_buffer() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. File Writer
// -------------------------------------------------------------------------
auto test_file_writer() -> bool { auto test_file_writer() -> bool {
const Path path = "test_stream_writer.bin"; const Path path = "test_stream_writer.bin";
// Ensure clean state
if (std::filesystem::exists(path)) { if (std::filesystem::exists(path)) {
std::filesystem::remove(path); std::filesystem::remove(path);
} }
@ -94,11 +75,9 @@ auto test_file_writer() -> bool {
const String hello = "Hello World"; const String hello = "Hello World";
IAT_CHECK(writer.write(hello.data(), hello.size()).has_value()); IAT_CHECK(writer.write(hello.data(), hello.size()).has_value());
// Explicit flush
IAT_CHECK(writer.flush().has_value()); IAT_CHECK(writer.flush().has_value());
} }
// Verify file content via FileOps
auto read_res = FileOps::read_binary_file(path); auto read_res = FileOps::read_binary_file(path);
IAT_CHECK(read_res.has_value()); IAT_CHECK(read_res.has_value());
@ -106,15 +85,11 @@ auto test_file_writer() -> bool {
read_res->size()); read_res->size());
IAT_CHECK_EQ(read_str, String("Hello World")); IAT_CHECK_EQ(read_str, String("Hello World"));
// Cleanup
std::filesystem::remove(path); std::filesystem::remove(path);
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Primitive Types
// -------------------------------------------------------------------------
auto test_primitives() -> bool { auto test_primitives() -> bool {
StreamWriter writer; StreamWriter writer;
@ -129,9 +104,6 @@ auto test_primitives() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_memory_writer); IAT_ADD_TEST(test_memory_writer);
IAT_ADD_TEST(test_fixed_buffer); IAT_ADD_TEST(test_fixed_buffer);

View File

@ -20,11 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, StringOps) IAT_BEGIN_BLOCK(Core, StringOps)
// -------------------------------------------------------------------------
// 1. Base64 Encoding
// -------------------------------------------------------------------------
auto test_base64_encode() -> bool { auto test_base64_encode() -> bool {
// Case 1: Standard text
{ {
const String s = "Hello World"; const String s = "Hello World";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size()); const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
@ -32,7 +29,6 @@ auto test_base64_encode() -> bool {
IAT_CHECK_EQ(encoded, String("SGVsbG8gV29ybGQ=")); IAT_CHECK_EQ(encoded, String("SGVsbG8gV29ybGQ="));
} }
// Case 2: Padding Logic (1 byte -> 2 pad)
{ {
const String s = "M"; const String s = "M";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size()); const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
@ -40,7 +36,6 @@ auto test_base64_encode() -> bool {
IAT_CHECK_EQ(encoded, String("TQ==")); IAT_CHECK_EQ(encoded, String("TQ=="));
} }
// Case 3: Padding Logic (2 bytes -> 1 pad)
{ {
const String s = "Ma"; const String s = "Ma";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size()); const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
@ -48,7 +43,6 @@ auto test_base64_encode() -> bool {
IAT_CHECK_EQ(encoded, String("TWE=")); IAT_CHECK_EQ(encoded, String("TWE="));
} }
// Case 4: Padding Logic (3 bytes -> 0 pad)
{ {
const String s = "Man"; const String s = "Man";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size()); const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
@ -56,7 +50,6 @@ auto test_base64_encode() -> bool {
IAT_CHECK_EQ(encoded, String("TWFu")); IAT_CHECK_EQ(encoded, String("TWFu"));
} }
// Case 5: Empty
{ {
const String encoded = StringOps::encode_base64({}); const String encoded = StringOps::encode_base64({});
IAT_CHECK(encoded.empty()); IAT_CHECK(encoded.empty());
@ -65,11 +58,8 @@ auto test_base64_encode() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Base64 Decoding
// -------------------------------------------------------------------------
auto test_base64_decode() -> bool { auto test_base64_decode() -> bool {
// Case 1: Standard text
{ {
const String encoded = "SGVsbG8gV29ybGQ="; const String encoded = "SGVsbG8gV29ybGQ=";
const Vec<u8> decoded = StringOps::decode_base64(encoded); const Vec<u8> decoded = StringOps::decode_base64(encoded);
@ -78,7 +68,6 @@ auto test_base64_decode() -> bool {
IAT_CHECK_EQ(result, String("Hello World")); IAT_CHECK_EQ(result, String("Hello World"));
} }
// Case 2: Empty
{ {
const Vec<u8> decoded = StringOps::decode_base64(""); const Vec<u8> decoded = StringOps::decode_base64("");
IAT_CHECK(decoded.empty()); IAT_CHECK(decoded.empty());
@ -87,9 +76,6 @@ auto test_base64_decode() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Round Trip (Binary Data)
// -------------------------------------------------------------------------
auto test_base64_round_trip() -> bool { auto test_base64_round_trip() -> bool {
Vec<u8> original; Vec<u8> original;
original.reserve(256); original.reserve(256);

View File

@ -18,53 +18,35 @@
using namespace IACore; using namespace IACore;
// -----------------------------------------------------------------------------
// Test Structs for Hashing (Must be defined at Global Scope)
// -----------------------------------------------------------------------------
struct TestVec3 { struct TestVec3 {
f32 x, y, z; f32 x, y, z;
// Equality operator required for hash maps, though strictly
// 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;
} }
}; };
// Inject the hash specialization into the ankerl namespace
// 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
// -----------------------------------------------------------------------------
IAT_BEGIN_BLOCK(Core, Utils) IAT_BEGIN_BLOCK(Core, Utils)
// -------------------------------------------------------------------------
// 1. Binary <-> Hex String Conversion
// -------------------------------------------------------------------------
auto test_hex_conversion() -> bool { auto test_hex_conversion() -> bool {
// A. Binary To Hex
u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF}; u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
String hex = Utils::binary_to_hex_string(bin); String hex = Utils::binary_to_hex_string(bin);
IAT_CHECK_EQ(hex, String("DEADBEEF00FF")); IAT_CHECK_EQ(hex, String("DEADBEEF00FF"));
// B. Hex To Binary (Valid Upper)
auto res_upper = Utils::hex_string_to_binary("DEADBEEF00FF"); auto res_upper = Utils::hex_string_to_binary("DEADBEEF00FF");
IAT_CHECK(res_upper.has_value()); IAT_CHECK(res_upper.has_value());
IAT_CHECK_EQ(res_upper->size(), static_cast<usize>(6)); IAT_CHECK_EQ(res_upper->size(), static_cast<usize>(6));
IAT_CHECK_EQ((*res_upper)[0], 0xDE); IAT_CHECK_EQ((*res_upper)[0], 0xDE);
IAT_CHECK_EQ((*res_upper)[5], 0xFF); IAT_CHECK_EQ((*res_upper)[5], 0xFF);
// C. Hex To Binary (Valid Lower/Mixed)
auto res_lower = Utils::hex_string_to_binary("deadbeef00ff"); auto res_lower = Utils::hex_string_to_binary("deadbeef00ff");
IAT_CHECK(res_lower.has_value()); IAT_CHECK(res_lower.has_value());
IAT_CHECK_EQ((*res_lower)[0], 0xDE); IAT_CHECK_EQ((*res_lower)[0], 0xDE);
// D. Round Trip Integrity
Vec<u8> original = {1, 2, 3, 4, 5}; Vec<u8> original = {1, 2, 3, 4, 5};
String s = Utils::binary_to_hex_string(original); String s = Utils::binary_to_hex_string(original);
auto back = Utils::hex_string_to_binary(s); auto back = Utils::hex_string_to_binary(s);
@ -75,19 +57,14 @@ auto test_hex_conversion() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Hex Error Handling
// -------------------------------------------------------------------------
auto test_hex_errors() -> bool { auto test_hex_errors() -> bool {
// Odd Length
auto odd = Utils::hex_string_to_binary("ABC"); auto odd = Utils::hex_string_to_binary("ABC");
IAT_CHECK_NOT(odd.has_value()); IAT_CHECK_NOT(odd.has_value());
// Invalid Characters
auto invalid = Utils::hex_string_to_binary("ZZTOP"); auto invalid = Utils::hex_string_to_binary("ZZTOP");
IAT_CHECK_NOT(invalid.has_value()); IAT_CHECK_NOT(invalid.has_value());
// Empty string is valid (empty vector)
auto empty = Utils::hex_string_to_binary(""); auto empty = Utils::hex_string_to_binary("");
IAT_CHECK(empty.has_value()); IAT_CHECK(empty.has_value());
IAT_CHECK_EQ(empty->size(), static_cast<usize>(0)); IAT_CHECK_EQ(empty->size(), static_cast<usize>(0));
@ -95,9 +72,6 @@ auto test_hex_errors() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Algorithms: Sorting
// -------------------------------------------------------------------------
auto test_sort() -> bool { auto test_sort() -> bool {
Vec<i32> nums = {5, 1, 4, 2, 3}; Vec<i32> nums = {5, 1, 4, 2, 3};
@ -112,48 +86,35 @@ auto test_sort() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. Algorithms: Binary Search (Left/Right)
// -------------------------------------------------------------------------
auto test_binary_search() -> bool { auto test_binary_search() -> bool {
// Must be sorted for Binary Search
Vec<i32> nums = {10, 20, 20, 20, 30}; Vec<i32> nums = {10, 20, 20, 20, 30};
// Search Left (Lower Bound) -> First element >= value
auto it_left = Utils::binary_search_left(nums, 20); auto it_left = Utils::binary_search_left(nums, 20);
IAT_CHECK(it_left != nums.end()); IAT_CHECK(it_left != nums.end());
IAT_CHECK_EQ(*it_left, 20); IAT_CHECK_EQ(*it_left, 20);
IAT_CHECK_EQ(std::distance(nums.begin(), it_left), 1); // Index 1 is first 20 IAT_CHECK_EQ(std::distance(nums.begin(), it_left), 1);
// Search Right (Upper Bound) -> First element > value
auto it_right = Utils::binary_search_right(nums, 20); auto it_right = Utils::binary_search_right(nums, 20);
IAT_CHECK(it_right != nums.end()); IAT_CHECK(it_right != nums.end());
IAT_CHECK_EQ(*it_right, 30); // Points to 30 IAT_CHECK_EQ(*it_right, 30);
IAT_CHECK_EQ(std::distance(nums.begin(), it_right), 4); // Index 4 IAT_CHECK_EQ(std::distance(nums.begin(), it_right), 4);
// Search for non-existent
auto it_fail = Utils::binary_search_left(nums, 99); auto it_fail = Utils::binary_search_left(nums, 99);
IAT_CHECK(it_fail == nums.end()); IAT_CHECK(it_fail == nums.end());
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. Hashing Basics
// -------------------------------------------------------------------------
auto test_hash_basics() -> bool { auto test_hash_basics() -> bool {
u64 h1 = Utils::compute_hash(10, 20.5f, "Hello"); u64 h1 = Utils::compute_hash(10, 20.5f, "Hello");
u64 h2 = Utils::compute_hash(10, 20.5f, "Hello"); u64 h2 = Utils::compute_hash(10, 20.5f, "Hello");
u64 h3 = Utils::compute_hash(10, 20.5f, "World"); u64 h3 = Utils::compute_hash(10, 20.5f, "World");
// Determinism
IAT_CHECK_EQ(h1, h2); IAT_CHECK_EQ(h1, h2);
// Differentiation
IAT_CHECK_NEQ(h1, h3); IAT_CHECK_NEQ(h1, h3);
// Order sensitivity (Golden ratio combine should care about order)
// Hash(A, B) != Hash(B, A)
u64 order_a = Utils::compute_hash(1, 2); u64 order_a = Utils::compute_hash(1, 2);
u64 order_b = Utils::compute_hash(2, 1); u64 order_b = Utils::compute_hash(2, 1);
IAT_CHECK_NEQ(order_a, order_b); IAT_CHECK_NEQ(order_a, order_b);
@ -161,9 +122,6 @@ auto test_hash_basics() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 6. Macro Verification (IA_MAKE_HASHABLE)
// -------------------------------------------------------------------------
auto test_hash_macro() -> bool { auto test_hash_macro() -> bool {
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};
@ -175,30 +133,21 @@ auto test_hash_macro() -> bool {
u64 h2 = hasher(v2); u64 h2 = hasher(v2);
u64 h3 = hasher(v3); u64 h3 = hasher(v3);
IAT_CHECK_EQ(h1, h2); // Same content = same hash IAT_CHECK_EQ(h1, h2);
IAT_CHECK_NEQ(h1, h3); // Different content = different hash IAT_CHECK_NEQ(h1, h3);
// -------------------------------------------------------------
// Verify ComputeHash integration
// -------------------------------------------------------------
u64 h_manual = 0; u64 h_manual = 0;
Utils::hash_combine(h_manual, v1); Utils::hash_combine(h_manual, v1);
u64 h_wrapper = Utils::compute_hash(v1); u64 h_wrapper = Utils::compute_hash(v1);
// This proves ComputeHash found the specialization and mixed it correctly
IAT_CHECK_EQ(h_manual, h_wrapper); IAT_CHECK_EQ(h_manual, h_wrapper);
// Verify the avalanche effect took place (hWrapper should NOT be h1)
IAT_CHECK_NEQ(h1, h_wrapper); IAT_CHECK_NEQ(h1, h_wrapper);
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_hex_conversion); IAT_ADD_TEST(test_hex_conversion);
IAT_ADD_TEST(test_hex_errors); IAT_ADD_TEST(test_hex_errors);

View File

@ -21,9 +21,6 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, XML) IAT_BEGIN_BLOCK(Core, XML)
// -------------------------------------------------------------------------
// 1. Basic String Parsing
// -------------------------------------------------------------------------
auto test_parse_string() -> bool { auto test_parse_string() -> bool {
const String xml_content = R"( const String xml_content = R"(
<root> <root>
@ -50,9 +47,6 @@ auto test_parse_string() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 2. Error Handling
// -------------------------------------------------------------------------
auto test_parse_error() -> bool { auto test_parse_error() -> bool {
const String invalid_xml = "<root><unclosed>"; const String invalid_xml = "<root><unclosed>";
auto res = XML::parse_from_string(invalid_xml); auto res = XML::parse_from_string(invalid_xml);
@ -60,9 +54,6 @@ auto test_parse_error() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 3. Serialization
// -------------------------------------------------------------------------
auto test_serialize() -> bool { auto test_serialize() -> bool {
const String xml_content = "<root><node>Text</node></root>"; const String xml_content = "<root><node>Text</node></root>";
auto res = XML::parse_from_string(xml_content); auto res = XML::parse_from_string(xml_content);
@ -70,21 +61,16 @@ auto test_serialize() -> bool {
String output = XML::serialize_to_string(*res); String output = XML::serialize_to_string(*res);
// Basic containment check as formatting might vary
IAT_CHECK(output.find("<root>") != String::npos); IAT_CHECK(output.find("<root>") != String::npos);
IAT_CHECK(output.find("<node>Text</node>") != String::npos); IAT_CHECK(output.find("<node>Text</node>") != String::npos);
return true; return true;
} }
// -------------------------------------------------------------------------
// 4. String Escaping
// -------------------------------------------------------------------------
auto test_escape() -> bool { auto test_escape() -> bool {
const String raw = "< & > \" '"; const String raw = "< & > \" '";
const String escaped = XML::escape_xml_string(raw); const String escaped = XML::escape_xml_string(raw);
// Check for standard XML entities
IAT_CHECK(escaped.find("&lt;") != String::npos); IAT_CHECK(escaped.find("&lt;") != String::npos);
IAT_CHECK(escaped.find("&amp;") != String::npos); IAT_CHECK(escaped.find("&amp;") != String::npos);
IAT_CHECK(escaped.find("&gt;") != String::npos); IAT_CHECK(escaped.find("&gt;") != String::npos);
@ -94,18 +80,13 @@ auto test_escape() -> bool {
return true; return true;
} }
// -------------------------------------------------------------------------
// 5. File I/O Integration
// -------------------------------------------------------------------------
auto test_file_io() -> bool { auto test_file_io() -> bool {
const Path path = "test_temp_xml_doc.xml"; const Path path = "test_temp_xml_doc.xml";
const String content = "<config><ver>1.0</ver></config>"; const String content = "<config><ver>1.0</ver></config>";
// 1. Write Test File
auto write_res = FileOps::write_text_file(path, content, true); auto write_res = FileOps::write_text_file(path, content, true);
IAT_CHECK(write_res.has_value()); IAT_CHECK(write_res.has_value());
// 2. Parse from File
auto parse_res = XML::parse_from_file(path); auto parse_res = XML::parse_from_file(path);
IAT_CHECK(parse_res.has_value()); IAT_CHECK(parse_res.has_value());
@ -113,15 +94,11 @@ auto test_file_io() -> bool {
IAT_CHECK_EQ(String(doc.child("config").child("ver").child_value()), IAT_CHECK_EQ(String(doc.child("config").child("ver").child_value()),
String("1.0")); String("1.0"));
// 3. Cleanup
std::filesystem::remove(path); std::filesystem::remove(path);
return true; return true;
} }
// -------------------------------------------------------------------------
// Registration
// -------------------------------------------------------------------------
IAT_BEGIN_TEST_LIST() IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_parse_string); IAT_ADD_TEST(test_parse_string);
IAT_ADD_TEST(test_parse_error); IAT_ADD_TEST(test_parse_error);