IACore v1.2
This commit is contained in:
160
Tests/Unit/AsyncOps.cpp
Normal file
160
Tests/Unit/AsyncOps.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/AsyncOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
struct SchedulerGuard {
|
||||
SchedulerGuard(u8 worker_count = 2) {
|
||||
|
||||
(void)AsyncOps::initialize_scheduler(worker_count);
|
||||
}
|
||||
|
||||
~SchedulerGuard() { AsyncOps::terminate_scheduler(); }
|
||||
};
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, AsyncOps)
|
||||
|
||||
auto test_initialization() -> bool {
|
||||
|
||||
AsyncOps::terminate_scheduler();
|
||||
|
||||
const auto res = AsyncOps::initialize_scheduler(4);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(4));
|
||||
|
||||
AsyncOps::terminate_scheduler();
|
||||
|
||||
const auto res2 = AsyncOps::initialize_scheduler(1);
|
||||
IAT_CHECK(res2.has_value());
|
||||
IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(1));
|
||||
|
||||
AsyncOps::terminate_scheduler();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_basic_execution() -> bool {
|
||||
SchedulerGuard guard(2);
|
||||
|
||||
AsyncOps::Schedule schedule;
|
||||
std::atomic<i32> run_count{0};
|
||||
|
||||
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { run_count++; }, 0,
|
||||
&schedule);
|
||||
|
||||
AsyncOps::wait_for_schedule_completion(&schedule);
|
||||
|
||||
IAT_CHECK_EQ(run_count.load(), 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_concurrency() -> bool {
|
||||
SchedulerGuard guard(4);
|
||||
|
||||
AsyncOps::Schedule schedule;
|
||||
std::atomic<i32> run_count{0};
|
||||
const i32 total_tasks = 100;
|
||||
|
||||
for (i32 i = 0; i < total_tasks; ++i) {
|
||||
AsyncOps::schedule_task(
|
||||
[&](AsyncOps::WorkerId) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
run_count++;
|
||||
},
|
||||
0, &schedule);
|
||||
}
|
||||
|
||||
AsyncOps::wait_for_schedule_completion(&schedule);
|
||||
|
||||
IAT_CHECK_EQ(run_count.load(), total_tasks);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_priorities() -> bool {
|
||||
SchedulerGuard guard(2);
|
||||
AsyncOps::Schedule schedule;
|
||||
std::atomic<i32> high_priority_ran{0};
|
||||
std::atomic<i32> normal_priority_ran{0};
|
||||
|
||||
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { high_priority_ran++; }, 0,
|
||||
&schedule, AsyncOps::Priority::High);
|
||||
|
||||
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { normal_priority_ran++; }, 0,
|
||||
&schedule, AsyncOps::Priority::Normal);
|
||||
|
||||
AsyncOps::wait_for_schedule_completion(&schedule);
|
||||
|
||||
IAT_CHECK_EQ(high_priority_ran.load(), 1);
|
||||
IAT_CHECK_EQ(normal_priority_ran.load(), 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_run_task_fire_and_forget() -> bool {
|
||||
SchedulerGuard guard(2);
|
||||
|
||||
std::atomic<bool> executed{false};
|
||||
|
||||
AsyncOps::run_task([&]() { executed = true; });
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
if (executed.load())
|
||||
break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
IAT_CHECK(executed.load());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_cancellation_safety() -> bool {
|
||||
SchedulerGuard guard(2);
|
||||
|
||||
AsyncOps::cancel_tasks_of_tag(999);
|
||||
|
||||
AsyncOps::Schedule schedule;
|
||||
std::atomic<i32> counter{0};
|
||||
|
||||
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { counter++; }, 10,
|
||||
&schedule);
|
||||
|
||||
AsyncOps::wait_for_schedule_completion(&schedule);
|
||||
IAT_CHECK_EQ(counter.load(), 1);
|
||||
|
||||
AsyncOps::cancel_tasks_of_tag(10);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_initialization);
|
||||
IAT_ADD_TEST(test_basic_execution);
|
||||
IAT_ADD_TEST(test_concurrency);
|
||||
IAT_ADD_TEST(test_priorities);
|
||||
IAT_ADD_TEST(test_run_task_fire_and_forget);
|
||||
IAT_ADD_TEST(test_cancellation_safety);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, AsyncOps)
|
||||
103
Tests/Unit/CLI.cpp
Normal file
103
Tests/Unit/CLI.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/CLI.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, CLI)
|
||||
|
||||
auto test_basic_traversal() -> bool {
|
||||
const Vec<String> args = {"ignored", "one", "two", "three"};
|
||||
CLIParser parser(args);
|
||||
|
||||
IAT_CHECK(parser.remaining());
|
||||
|
||||
IAT_CHECK_EQ(String(parser.next()), "one");
|
||||
IAT_CHECK(parser.remaining());
|
||||
|
||||
IAT_CHECK_EQ(String(parser.next()), "two");
|
||||
IAT_CHECK(parser.remaining());
|
||||
|
||||
IAT_CHECK_EQ(String(parser.next()), "three");
|
||||
|
||||
IAT_CHECK_NOT(parser.remaining());
|
||||
|
||||
IAT_CHECK_EQ(String(parser.next()), "");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_peek() -> bool {
|
||||
const Vec<String> args = {"ignored", "peek_val", "next_val"};
|
||||
CLIParser parser(args);
|
||||
|
||||
IAT_CHECK_EQ(String(parser.peek()), "peek_val");
|
||||
IAT_CHECK(parser.remaining());
|
||||
|
||||
IAT_CHECK_EQ(String(parser.next()), "peek_val");
|
||||
|
||||
IAT_CHECK_EQ(String(parser.peek()), "next_val");
|
||||
IAT_CHECK_EQ(String(parser.next()), "next_val");
|
||||
|
||||
IAT_CHECK_NOT(parser.remaining());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_consume() -> bool {
|
||||
const Vec<String> args = {"ignored", "-v", "--output", "file.txt"};
|
||||
CLIParser parser(args);
|
||||
|
||||
IAT_CHECK_NOT(parser.consume("-x"));
|
||||
|
||||
IAT_CHECK_EQ(String(parser.peek()), "-v");
|
||||
|
||||
IAT_CHECK(parser.consume("-v"));
|
||||
|
||||
IAT_CHECK_EQ(String(parser.peek()), "--output");
|
||||
|
||||
IAT_CHECK(parser.consume("--output"));
|
||||
|
||||
IAT_CHECK_EQ(String(parser.next()), "file.txt");
|
||||
|
||||
IAT_CHECK_NOT(parser.remaining());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_empty() -> bool {
|
||||
const Vec<String> args = {};
|
||||
CLIParser parser(args);
|
||||
|
||||
IAT_CHECK_NOT(parser.remaining());
|
||||
IAT_CHECK_EQ(String(parser.peek()), "");
|
||||
IAT_CHECK_EQ(String(parser.next()), "");
|
||||
IAT_CHECK_NOT(parser.consume("-help"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_basic_traversal);
|
||||
IAT_ADD_TEST(test_peek);
|
||||
IAT_ADD_TEST(test_consume);
|
||||
IAT_ADD_TEST(test_empty);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, CLI)
|
||||
36
Tests/Unit/CMakeLists.txt
Normal file
36
Tests/Unit/CMakeLists.txt
Normal file
@ -0,0 +1,36 @@
|
||||
set(TEST_SOURCES
|
||||
AsyncOps.cpp
|
||||
CLI.cpp
|
||||
DataOps.cpp
|
||||
Environment.cpp
|
||||
FileOps.cpp
|
||||
IPC.cpp
|
||||
JSON.cpp
|
||||
Logger.cpp
|
||||
Main.cpp
|
||||
Platform.cpp
|
||||
ProcessOps.cpp
|
||||
RingBuffer.cpp
|
||||
SocketOps.cpp
|
||||
StreamReader.cpp
|
||||
StreamWriter.cpp
|
||||
StringOps.cpp
|
||||
Utils.cpp
|
||||
XML.cpp
|
||||
|
||||
SIMD/IntVec4.cpp
|
||||
SIMD/FloatVec4.cpp
|
||||
)
|
||||
|
||||
add_executable(IACore_Test_Suite ${TEST_SOURCES})
|
||||
|
||||
target_compile_options(IACore_Test_Suite PRIVATE -fexceptions)
|
||||
set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON)
|
||||
|
||||
target_link_libraries(IACore_Test_Suite PRIVATE IACore)
|
||||
|
||||
add_custom_command(TARGET IACore_Test_Suite POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:LongProcess>
|
||||
$<TARGET_FILE_DIR:IACore_Test_Suite>/LongProcess${CMAKE_EXECUTABLE_SUFFIX}
|
||||
)
|
||||
108
Tests/Unit/DataOps.cpp
Normal file
108
Tests/Unit/DataOps.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/DataOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, DataOps)
|
||||
|
||||
auto test_crc32() -> bool {
|
||||
{
|
||||
const String s = "123456789";
|
||||
const Span<const u8> span(reinterpret_cast<const u8 *>(s.data()), s.size());
|
||||
const u32 result = DataOps::crc32(span);
|
||||
|
||||
IAT_CHECK_EQ(result, 0xE3069283);
|
||||
}
|
||||
|
||||
{
|
||||
const u32 result = DataOps::crc32({});
|
||||
IAT_CHECK_EQ(result, 0U);
|
||||
}
|
||||
|
||||
{
|
||||
Vec<u8> buffer(33);
|
||||
for (usize i = 1; i < 33; ++i) {
|
||||
buffer[i] = static_cast<u8>(i);
|
||||
}
|
||||
|
||||
Vec<u8> ref_data(32);
|
||||
for (usize i = 0; i < 32; ++i) {
|
||||
ref_data[i] = static_cast<u8>(i + 1);
|
||||
}
|
||||
|
||||
const u32 hash_ref =
|
||||
DataOps::crc32(Span<const u8>(ref_data.data(), ref_data.size()));
|
||||
|
||||
const u32 hash_unaligned =
|
||||
DataOps::crc32(Span<const u8>(buffer.data() + 1, 32));
|
||||
|
||||
IAT_CHECK_EQ(hash_ref, hash_unaligned);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_hash_xxhash() -> bool {
|
||||
{
|
||||
const String s = "123456789";
|
||||
const u32 result = DataOps::hash_xxhash(s);
|
||||
IAT_CHECK_EQ(result, 0x937bad67);
|
||||
}
|
||||
|
||||
{
|
||||
const String s = "The quick brown fox jumps over the lazy dog";
|
||||
const u32 result = DataOps::hash_xxhash(s);
|
||||
IAT_CHECK_EQ(result, 0xE85EA4DE);
|
||||
}
|
||||
|
||||
{
|
||||
const String s = "Test";
|
||||
const u32 r1 = DataOps::hash_xxhash(s);
|
||||
const u32 r2 = DataOps::hash_xxhash(
|
||||
Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
|
||||
IAT_CHECK_EQ(r1, r2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_hash_fnv1a() -> bool {
|
||||
{
|
||||
const String s = "123456789";
|
||||
const u32 result = DataOps::hash_fnv1a(
|
||||
Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
|
||||
IAT_CHECK_EQ(result, 0xbb86b11c);
|
||||
}
|
||||
|
||||
{
|
||||
const u32 result = DataOps::hash_fnv1a(Span<const u8>{});
|
||||
IAT_CHECK_EQ(result, 0x811C9DC5);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_crc32);
|
||||
IAT_ADD_TEST(test_hash_fnv1a);
|
||||
IAT_ADD_TEST(test_hash_xxhash);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, DataOps)
|
||||
127
Tests/Unit/Environment.cpp
Normal file
127
Tests/Unit/Environment.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/Environment.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
static constexpr const char *TEST_KEY = "IA_TEST_ENV_VAR_12345";
|
||||
static constexpr const char *TEST_VAL = "Hello World";
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, Environment)
|
||||
|
||||
auto test_basic_cycle() -> bool {
|
||||
|
||||
(void)Environment::unset(TEST_KEY);
|
||||
IAT_CHECK_NOT(Environment::exists(TEST_KEY));
|
||||
|
||||
const auto set_res = Environment::set(TEST_KEY, TEST_VAL);
|
||||
IAT_CHECK(set_res.has_value());
|
||||
IAT_CHECK(Environment::exists(TEST_KEY));
|
||||
|
||||
const auto opt = Environment::find(TEST_KEY);
|
||||
IAT_CHECK(opt.has_value());
|
||||
IAT_CHECK_EQ(*opt, String(TEST_VAL));
|
||||
|
||||
const String val = Environment::get(TEST_KEY);
|
||||
IAT_CHECK_EQ(val, String(TEST_VAL));
|
||||
|
||||
(void)Environment::unset(TEST_KEY);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_overwrite() -> bool {
|
||||
(void)Environment::set(TEST_KEY, "ValueA");
|
||||
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueA"));
|
||||
|
||||
(void)Environment::set(TEST_KEY, "ValueB");
|
||||
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueB"));
|
||||
|
||||
(void)Environment::unset(TEST_KEY);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_unset() -> bool {
|
||||
(void)Environment::set(TEST_KEY, "To Be Deleted");
|
||||
IAT_CHECK(Environment::exists(TEST_KEY));
|
||||
|
||||
const auto unset_res = Environment::unset(TEST_KEY);
|
||||
IAT_CHECK(unset_res.has_value());
|
||||
|
||||
IAT_CHECK_NOT(Environment::exists(TEST_KEY));
|
||||
|
||||
const auto opt = Environment::find(TEST_KEY);
|
||||
IAT_CHECK_NOT(opt.has_value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_defaults() -> bool {
|
||||
const char *ghost_key = "IA_THIS_KEY_DOES_NOT_EXIST";
|
||||
|
||||
(void)Environment::unset(ghost_key);
|
||||
|
||||
const String empty = Environment::get(ghost_key);
|
||||
IAT_CHECK(empty.empty());
|
||||
|
||||
const String fallback = Environment::get(ghost_key, "MyDefault");
|
||||
IAT_CHECK_EQ(fallback, String("MyDefault"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_empty_value() -> bool {
|
||||
(void)Environment::set(TEST_KEY, "");
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
|
||||
#else
|
||||
|
||||
IAT_CHECK(Environment::exists(TEST_KEY));
|
||||
const auto opt = Environment::find(TEST_KEY);
|
||||
IAT_CHECK(opt.has_value());
|
||||
IAT_CHECK(opt->empty());
|
||||
#endif
|
||||
|
||||
(void)Environment::unset(TEST_KEY);
|
||||
IAT_CHECK_NOT(Environment::exists(TEST_KEY));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_bad_input() -> bool {
|
||||
|
||||
const auto res = Environment::set("", "Value");
|
||||
IAT_CHECK_NOT(res.has_value());
|
||||
|
||||
const auto res_unset = Environment::unset("");
|
||||
IAT_CHECK_NOT(res_unset.has_value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_basic_cycle);
|
||||
IAT_ADD_TEST(test_overwrite);
|
||||
IAT_ADD_TEST(test_unset);
|
||||
IAT_ADD_TEST(test_defaults);
|
||||
IAT_ADD_TEST(test_empty_value);
|
||||
IAT_ADD_TEST(test_bad_input);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, Environment)
|
||||
156
Tests/Unit/FileOps.cpp
Normal file
156
Tests/Unit/FileOps.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, FileOps)
|
||||
|
||||
void cleanup_file(const Path &path) {
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(path, ec)) {
|
||||
std::filesystem::remove(path, ec);
|
||||
}
|
||||
}
|
||||
|
||||
auto test_text_io() -> bool {
|
||||
const Path path = "iatest_fileops_text.txt";
|
||||
const String content = "Hello IACore FileOps!\nLine 2";
|
||||
|
||||
const auto write_res = FileOps::write_text_file(path, content, true);
|
||||
IAT_CHECK(write_res.has_value());
|
||||
IAT_CHECK_EQ(*write_res, content.size());
|
||||
|
||||
const auto read_res = FileOps::read_text_file(path);
|
||||
IAT_CHECK(read_res.has_value());
|
||||
IAT_CHECK_EQ(*read_res, content);
|
||||
|
||||
cleanup_file(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_binary_io() -> bool {
|
||||
const Path path = "iatest_fileops_bin.bin";
|
||||
const Vec<u8> content = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
|
||||
|
||||
const auto write_res = FileOps::write_binary_file(path, content, true);
|
||||
IAT_CHECK(write_res.has_value());
|
||||
IAT_CHECK_EQ(*write_res, content.size());
|
||||
|
||||
const auto read_res = FileOps::read_binary_file(path);
|
||||
IAT_CHECK(read_res.has_value());
|
||||
IAT_CHECK_EQ(read_res->size(), content.size());
|
||||
|
||||
for (usize i = 0; i < content.size(); ++i) {
|
||||
IAT_CHECK_EQ((*read_res)[i], content[i]);
|
||||
}
|
||||
|
||||
cleanup_file(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_file_mapping() -> bool {
|
||||
const Path path = "iatest_fileops_map.txt";
|
||||
const String content = "MappedContent";
|
||||
|
||||
(void)FileOps::write_text_file(path, content, true);
|
||||
|
||||
usize size = 0;
|
||||
const auto map_res = FileOps::map_file(path, size);
|
||||
IAT_CHECK(map_res.has_value());
|
||||
IAT_CHECK_EQ(size, content.size());
|
||||
|
||||
const u8 *ptr = *map_res;
|
||||
IAT_CHECK(ptr != nullptr);
|
||||
|
||||
String read_back(reinterpret_cast<const char *>(ptr), size);
|
||||
IAT_CHECK_EQ(read_back, content);
|
||||
|
||||
FileOps::unmap_file(ptr);
|
||||
|
||||
cleanup_file(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_shared_memory() -> bool {
|
||||
const String shm_name = "iatest_shm_block";
|
||||
const usize shm_size = 4096;
|
||||
|
||||
auto owner_res = FileOps::map_shared_memory(shm_name, shm_size, true);
|
||||
IAT_CHECK(owner_res.has_value());
|
||||
u8 *owner_ptr = *owner_res;
|
||||
|
||||
std::memset(owner_ptr, 0, shm_size);
|
||||
const String msg = "Shared Memory Message";
|
||||
std::memcpy(owner_ptr, msg.data(), msg.size());
|
||||
|
||||
auto client_res = FileOps::map_shared_memory(shm_name, shm_size, false);
|
||||
IAT_CHECK(client_res.has_value());
|
||||
u8 *client_ptr = *client_res;
|
||||
|
||||
String read_msg(reinterpret_cast<const char *>(client_ptr), msg.size());
|
||||
IAT_CHECK_EQ(read_msg, msg);
|
||||
|
||||
FileOps::unmap_file(owner_ptr);
|
||||
FileOps::unmap_file(client_ptr);
|
||||
FileOps::unlink_shared_memory(shm_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_stream_integration() -> bool {
|
||||
const Path path = "iatest_fileops_stream.bin";
|
||||
cleanup_file(path);
|
||||
|
||||
{
|
||||
auto writer_res = FileOps::stream_to_file(path, true);
|
||||
IAT_CHECK(writer_res.has_value());
|
||||
auto &writer = *writer_res;
|
||||
|
||||
(void)writer.write<u32>(0x12345678);
|
||||
(void)writer.write<u8>(0xFF);
|
||||
}
|
||||
|
||||
{
|
||||
auto reader_res = FileOps::stream_from_file(path);
|
||||
IAT_CHECK(reader_res.has_value());
|
||||
auto &reader = *reader_res;
|
||||
|
||||
auto val_u32 = reader.read<u32>();
|
||||
IAT_CHECK(val_u32.has_value());
|
||||
IAT_CHECK_EQ(*val_u32, 0x12345678u);
|
||||
|
||||
auto val_u8 = reader.read<u8>();
|
||||
IAT_CHECK(val_u8.has_value());
|
||||
IAT_CHECK_EQ(*val_u8, 0xFF);
|
||||
}
|
||||
|
||||
cleanup_file(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_text_io);
|
||||
IAT_ADD_TEST(test_binary_io);
|
||||
IAT_ADD_TEST(test_file_mapping);
|
||||
IAT_ADD_TEST(test_shared_memory);
|
||||
IAT_ADD_TEST(test_stream_integration);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, FileOps)
|
||||
130
Tests/Unit/IPC.cpp
Normal file
130
Tests/Unit/IPC.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/IPC.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, IPC)
|
||||
|
||||
auto test_layout_constraints() -> bool {
|
||||
|
||||
IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast<usize>(64));
|
||||
|
||||
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast<usize>(0));
|
||||
|
||||
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control),
|
||||
static_cast<usize>(64));
|
||||
|
||||
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control),
|
||||
static_cast<usize>(192));
|
||||
|
||||
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset),
|
||||
static_cast<usize>(320));
|
||||
|
||||
IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast<usize>(0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_manual_shm_ringbuffer() -> bool {
|
||||
const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK";
|
||||
const usize shm_size = 16 * 1024;
|
||||
|
||||
FileOps::unlink_shared_memory(shm_name);
|
||||
|
||||
auto map_res = FileOps::map_shared_memory(shm_name, shm_size, true);
|
||||
IAT_CHECK(map_res.has_value());
|
||||
|
||||
u8 *base_ptr = *map_res;
|
||||
auto *layout = reinterpret_cast<IpcSharedMemoryLayout *>(base_ptr);
|
||||
|
||||
layout->meta.magic = 0xDEADBEEF;
|
||||
layout->meta.version = 1;
|
||||
layout->meta.total_size = shm_size;
|
||||
|
||||
const usize header_size = IpcSharedMemoryLayout::get_header_size();
|
||||
const usize data_available = shm_size - header_size;
|
||||
const usize half_data = data_available / 2;
|
||||
|
||||
layout->moni_data_offset = header_size;
|
||||
layout->moni_data_size = half_data;
|
||||
|
||||
layout->mino_data_offset = header_size + half_data;
|
||||
layout->mino_data_size = half_data;
|
||||
|
||||
Span<u8> moni_data_span(base_ptr + layout->moni_data_offset,
|
||||
static_cast<usize>(layout->moni_data_size));
|
||||
auto moni_res =
|
||||
RingBufferView::create(&layout->moni_control, moni_data_span, true);
|
||||
IAT_CHECK(moni_res.has_value());
|
||||
auto moni = std::move(*moni_res);
|
||||
|
||||
Span<u8> mino_data_span(base_ptr + layout->mino_data_offset,
|
||||
static_cast<usize>(layout->mino_data_size));
|
||||
auto mino_res =
|
||||
RingBufferView::create(&layout->mino_control, mino_data_span, true);
|
||||
IAT_CHECK(mino_res.has_value());
|
||||
auto _ = std::move(*mino_res);
|
||||
|
||||
String msg = "IPC_TEST_MESSAGE";
|
||||
IAT_CHECK(
|
||||
moni.push(100, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()),
|
||||
msg.size()))
|
||||
.has_value());
|
||||
|
||||
auto moni_reader_res =
|
||||
RingBufferView::create(&layout->moni_control, moni_data_span, false);
|
||||
IAT_CHECK(moni_reader_res.has_value());
|
||||
auto moni_reader = std::move(*moni_reader_res);
|
||||
|
||||
RingBufferView::PacketHeader header;
|
||||
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_EQ(header.id, static_cast<u16>(100));
|
||||
|
||||
String received((char *)buffer, *pop_res.value());
|
||||
IAT_CHECK_EQ(received, msg);
|
||||
|
||||
FileOps::unmap_file(base_ptr);
|
||||
FileOps::unlink_shared_memory(shm_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class TestManager : public IpcManager {
|
||||
public:
|
||||
void on_signal(NativeProcessID, u8) override {}
|
||||
void on_packet(NativeProcessID, u16, Span<const u8>) override {}
|
||||
};
|
||||
|
||||
auto test_manager_instantiation() -> bool {
|
||||
TestManager mgr;
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_layout_constraints);
|
||||
IAT_ADD_TEST(test_manual_shm_ringbuffer);
|
||||
IAT_ADD_TEST(test_manager_instantiation);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, IPC)
|
||||
164
Tests/Unit/JSON.cpp
Normal file
164
Tests/Unit/JSON.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/JSON.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
struct UserProfile {
|
||||
String username;
|
||||
u32 id;
|
||||
bool is_active;
|
||||
Vec<String> roles;
|
||||
|
||||
bool operator==(const UserProfile &other) const {
|
||||
return username == other.username && id == other.id &&
|
||||
is_active == other.is_active && roles == other.roles;
|
||||
}
|
||||
};
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, JSON)
|
||||
|
||||
auto test_dynamic_parse() -> bool {
|
||||
const String json_text = R"({
|
||||
"string": "Hello World",
|
||||
"int": 42,
|
||||
"float": 3.14159,
|
||||
"bool": true,
|
||||
"array": [10, 20, 30],
|
||||
"object": { "key": "value" }
|
||||
})";
|
||||
|
||||
auto res = Json::parse(json_text);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
const auto &j = *res;
|
||||
|
||||
IAT_CHECK(j["string"].is_string());
|
||||
IAT_CHECK_EQ(j["string"].get<String>(), String("Hello World"));
|
||||
|
||||
IAT_CHECK(j["int"].is_number_integer());
|
||||
IAT_CHECK_EQ(j["int"].get<i32>(), 42);
|
||||
|
||||
IAT_CHECK(j["float"].is_number_float());
|
||||
IAT_CHECK_APPROX(j["float"].get<f32>(), 3.14159f);
|
||||
|
||||
IAT_CHECK(j["bool"].is_boolean());
|
||||
IAT_CHECK_EQ(j["bool"].get<bool>(), true);
|
||||
|
||||
IAT_CHECK(j["array"].is_array());
|
||||
IAT_CHECK_EQ(j["array"].size(), 3u);
|
||||
IAT_CHECK_EQ(j["array"][0].get<i32>(), 10);
|
||||
|
||||
IAT_CHECK(j["object"].is_object());
|
||||
IAT_CHECK_EQ(j["object"]["key"].get<String>(), String("value"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_dynamic_encode() -> bool {
|
||||
nlohmann::json j;
|
||||
j["name"] = "IACore";
|
||||
j["version"] = 2;
|
||||
|
||||
const String encoded = Json::encode(j);
|
||||
|
||||
IAT_CHECK(encoded.find("IACore") != String::npos);
|
||||
IAT_CHECK(encoded.find("version") != String::npos);
|
||||
IAT_CHECK(encoded.find("2") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_parse_invalid() -> bool {
|
||||
const String bad_json = "{ key: value }";
|
||||
auto res = Json::parse(bad_json);
|
||||
IAT_CHECK_NOT(res.has_value());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_struct_round_trip() -> bool {
|
||||
UserProfile original{.username = "test_user",
|
||||
.id = 12345,
|
||||
.is_active = true,
|
||||
.roles = {"admin", "editor"}};
|
||||
|
||||
auto encode_res = Json::encode_struct(original);
|
||||
IAT_CHECK(encode_res.has_value());
|
||||
String json_str = *encode_res;
|
||||
|
||||
IAT_CHECK(json_str.find("test_user") != String::npos);
|
||||
IAT_CHECK(json_str.find("roles") != String::npos);
|
||||
|
||||
auto decode_res = Json::parse_to_struct<UserProfile>(json_str);
|
||||
IAT_CHECK(decode_res.has_value());
|
||||
|
||||
UserProfile decoded = *decode_res;
|
||||
IAT_CHECK(decoded == original);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_struct_parse_error() -> bool {
|
||||
const String malformed = "{ broken_json: ";
|
||||
auto res = Json::parse_to_struct<UserProfile>(malformed);
|
||||
IAT_CHECK_NOT(res.has_value());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_read_only() -> bool {
|
||||
const String json_text = R"({
|
||||
"id": 999,
|
||||
"name": "Simd",
|
||||
"scores": [1.1, 2.2]
|
||||
})";
|
||||
|
||||
auto res = Json::parse_read_only(json_text);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
auto &doc = *res;
|
||||
simdjson::dom::element root = doc.root();
|
||||
|
||||
u64 id = 0;
|
||||
auto err_id = root["id"].get(id);
|
||||
IAT_CHECK(!err_id);
|
||||
IAT_CHECK_EQ(id, 999ULL);
|
||||
|
||||
std::string_view name;
|
||||
auto err_name = root["name"].get(name);
|
||||
IAT_CHECK(!err_name);
|
||||
IAT_CHECK_EQ(String(name), String("Simd"));
|
||||
|
||||
simdjson::dom::array scores;
|
||||
auto err_arr = root["scores"].get(scores);
|
||||
IAT_CHECK(!err_arr);
|
||||
IAT_CHECK_EQ(scores.size(), 2u);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_dynamic_parse);
|
||||
IAT_ADD_TEST(test_dynamic_encode);
|
||||
IAT_ADD_TEST(test_parse_invalid);
|
||||
IAT_ADD_TEST(test_struct_round_trip);
|
||||
IAT_ADD_TEST(test_struct_parse_error);
|
||||
IAT_ADD_TEST(test_read_only);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, JSON)
|
||||
118
Tests/Unit/Logger.cpp
Normal file
118
Tests/Unit/Logger.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/Logger.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, Logger)
|
||||
|
||||
static constexpr const char *LOG_FILE = "iacore_test_log.txt";
|
||||
|
||||
auto test_file_logging() -> bool {
|
||||
|
||||
const auto res = Logger::enable_logging_to_disk(LOG_FILE);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
Logger::set_log_level(Logger::LogLevel::Trace);
|
||||
|
||||
const String msg_info = "Test_Info_Msg_123";
|
||||
const String msg_err = "Test_Error_Msg_456";
|
||||
const String msg_warn = "Test_Warn_Msg_789";
|
||||
|
||||
Logger::info("{}", msg_info);
|
||||
Logger::error("{}", msg_err);
|
||||
Logger::warn("{}", msg_warn);
|
||||
|
||||
Logger::flush_logs();
|
||||
|
||||
auto read_res = FileOps::read_text_file(LOG_FILE);
|
||||
if (!read_res) {
|
||||
std::cout << console::YELLOW << " Warning: Could not read log file ("
|
||||
<< read_res.error() << "). Skipping verification.\n"
|
||||
<< console::RESET;
|
||||
return true;
|
||||
}
|
||||
|
||||
const String content = *read_res;
|
||||
|
||||
IAT_CHECK(content.find(msg_info) != String::npos);
|
||||
IAT_CHECK(content.find(msg_err) != String::npos);
|
||||
IAT_CHECK(content.find(msg_warn) != String::npos);
|
||||
|
||||
IAT_CHECK(content.find("INFO") != String::npos);
|
||||
IAT_CHECK(content.find("ERROR") != String::npos);
|
||||
IAT_CHECK(content.find("WARN") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_log_levels() -> bool {
|
||||
|
||||
Logger::set_log_level(Logger::LogLevel::Warn);
|
||||
|
||||
const String unique_info = "Hidden_Info_Msg";
|
||||
const String unique_warn = "Visible_Warn_Msg";
|
||||
|
||||
Logger::info("{}", unique_info);
|
||||
Logger::warn("{}", unique_warn);
|
||||
|
||||
Logger::flush_logs();
|
||||
|
||||
auto read_res = FileOps::read_text_file(LOG_FILE);
|
||||
if (!read_res) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const String content = *read_res;
|
||||
|
||||
IAT_CHECK(content.find(unique_info) == String::npos);
|
||||
|
||||
IAT_CHECK(content.find(unique_warn) != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_formatting() -> bool {
|
||||
Logger::set_log_level(Logger::LogLevel::Info);
|
||||
|
||||
const String name = "IACore";
|
||||
const i32 version = 99;
|
||||
|
||||
Logger::info("System {} online v{}", name, version);
|
||||
Logger::flush_logs();
|
||||
|
||||
auto read_res = FileOps::read_text_file(LOG_FILE);
|
||||
if (!read_res) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const String content = *read_res;
|
||||
IAT_CHECK(content.find("System IACore online v99") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_file_logging);
|
||||
IAT_ADD_TEST(test_log_levels);
|
||||
IAT_ADD_TEST(test_formatting);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, Logger)
|
||||
41
Tests/Unit/Main.cpp
Normal file
41
Tests/Unit/Main.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IACore.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
#include <IACore/SocketOps.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IACORE_MAIN() {
|
||||
(void)args;
|
||||
|
||||
OX_TRY_PURE(SocketOps::initialize());
|
||||
|
||||
std::cout
|
||||
<< console::GREEN
|
||||
<< "\n===============================================================\n";
|
||||
std::cout << " IACore (Independent Architecture Core) - Unit Test Suite\n";
|
||||
std::cout
|
||||
<< "===============================================================\n"
|
||||
<< console::RESET << "\n";
|
||||
|
||||
Const<i32> result = Test::TestRegistry::run_all();
|
||||
|
||||
SocketOps::terminate();
|
||||
|
||||
return result;
|
||||
}
|
||||
119
Tests/Unit/Platform.cpp
Normal file
119
Tests/Unit/Platform.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/Platform.hpp>
|
||||
#include <cstring>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, Platform)
|
||||
|
||||
auto test_os_name() -> bool {
|
||||
const char *os_name = Platform::get_operating_system_name();
|
||||
IAT_CHECK(os_name != nullptr);
|
||||
|
||||
const String os(os_name);
|
||||
IAT_CHECK(!os.empty());
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
IAT_CHECK_EQ(os, String("Windows"));
|
||||
#elif IA_PLATFORM_LINUX
|
||||
IAT_CHECK_EQ(os, String("Linux"));
|
||||
#elif IA_PLATFORM_APPLE
|
||||
IAT_CHECK_EQ(os, String("MacOS"));
|
||||
#elif IA_PLATFORM_WASM
|
||||
IAT_CHECK_EQ(os, String("WebAssembly"));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_arch_name() -> bool {
|
||||
const char *arch_name = Platform::get_architecture_name();
|
||||
IAT_CHECK(arch_name != nullptr);
|
||||
|
||||
const String arch(arch_name);
|
||||
IAT_CHECK(!arch.empty());
|
||||
|
||||
#if IA_ARCH_X64
|
||||
IAT_CHECK_EQ(arch, String("x86_64"));
|
||||
#elif IA_ARCH_ARM64
|
||||
IAT_CHECK_EQ(arch, String("ARM64"));
|
||||
#elif IA_ARCH_WASM
|
||||
IAT_CHECK_EQ(arch, String("WASM"));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_capabilities() -> bool {
|
||||
|
||||
const bool check_result = Platform::check_cpu();
|
||||
IAT_CHECK(check_result);
|
||||
|
||||
const auto &caps = Platform::get_capabilities();
|
||||
|
||||
volatile bool has_crc = caps.hardware_crc32;
|
||||
(void)has_crc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if IA_ARCH_X64
|
||||
auto test_cpuid() -> bool {
|
||||
i32 regs[4] = {0};
|
||||
|
||||
Platform::cpuid(0, 0, regs);
|
||||
|
||||
IAT_CHECK(regs[0] >= 0);
|
||||
|
||||
char vendor[13];
|
||||
std::memset(vendor, 0, 13);
|
||||
|
||||
std::memcpy(vendor, ®s[1], 4);
|
||||
std::memcpy(vendor + 4, ®s[3], 4);
|
||||
std::memcpy(vendor + 8, ®s[2], 4);
|
||||
vendor[12] = '\0';
|
||||
|
||||
const String vendor_str(vendor);
|
||||
IAT_CHECK(!vendor_str.empty());
|
||||
|
||||
bool is_known =
|
||||
(vendor_str == "GenuineIntel" || vendor_str == "AuthenticAMD" ||
|
||||
vendor_str == "KVMKVMKVM" || vendor_str == "Microsoft Hv" ||
|
||||
vendor_str == "VBoxVBoxVBox");
|
||||
|
||||
if (!is_known) {
|
||||
|
||||
std::cout << " [Info] Unknown CPU Vendor: " << vendor_str << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_os_name);
|
||||
IAT_ADD_TEST(test_arch_name);
|
||||
IAT_ADD_TEST(test_capabilities);
|
||||
#if IA_ARCH_X64
|
||||
IAT_ADD_TEST(test_cpuid);
|
||||
#endif
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, Platform)
|
||||
213
Tests/Unit/ProcessOps.cpp
Normal file
213
Tests/Unit/ProcessOps.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/ProcessOps.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
#define CMD_ECHO_EXE "cmd.exe"
|
||||
#define CMD_ARG_PREFIX "/c echo"
|
||||
#define NULL_DEVICE "NUL"
|
||||
#else
|
||||
#define CMD_ECHO_EXE "/bin/echo"
|
||||
#define CMD_ARG_PREFIX ""
|
||||
#define NULL_DEVICE "/dev/null"
|
||||
#endif
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, ProcessOps)
|
||||
|
||||
auto test_basic_run() -> bool {
|
||||
|
||||
String captured;
|
||||
|
||||
const auto result =
|
||||
ProcessOps::spawn_process_sync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
|
||||
[&](StringView line) { captured = line; });
|
||||
|
||||
IAT_CHECK(result.has_value());
|
||||
IAT_CHECK_EQ(*result, 0);
|
||||
|
||||
IAT_CHECK(captured.find("HelloIA") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_arguments() -> bool {
|
||||
Vec<String> lines;
|
||||
|
||||
String args = String(CMD_ARG_PREFIX) + " one two";
|
||||
if (!args.empty() && args[0] == ' ') {
|
||||
args.erase(0, 1);
|
||||
}
|
||||
|
||||
const auto result =
|
||||
ProcessOps::spawn_process_sync(CMD_ECHO_EXE, args, [&](StringView line) {
|
||||
lines.push_back(String(line));
|
||||
});
|
||||
|
||||
IAT_CHECK_EQ(*result, 0);
|
||||
IAT_CHECK(lines.size() > 0);
|
||||
|
||||
IAT_CHECK(lines[0].find("one two") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_exit_codes() -> bool {
|
||||
|
||||
String cmd;
|
||||
String arg;
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
cmd = "cmd.exe";
|
||||
arg = "/c exit 42";
|
||||
#else
|
||||
cmd = "/bin/sh";
|
||||
arg = "-c \"exit 42\"";
|
||||
#endif
|
||||
|
||||
const auto result =
|
||||
ProcessOps::spawn_process_sync(cmd, arg, [](StringView) {});
|
||||
|
||||
IAT_CHECK(result.has_value());
|
||||
IAT_CHECK_EQ(*result, 42);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_missing_exe() -> bool {
|
||||
|
||||
const auto result =
|
||||
ProcessOps::spawn_process_sync("sdflkjghsdflkjg", "", [](StringView) {});
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
IAT_CHECK_NOT(result.has_value());
|
||||
#else
|
||||
|
||||
IAT_CHECK(result.has_value());
|
||||
IAT_CHECK_EQ(*result, 127);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_large_output() -> bool {
|
||||
|
||||
String massive_string;
|
||||
massive_string.reserve(5000);
|
||||
for (i32 i = 0; i < 500; ++i) {
|
||||
massive_string += "1234567890";
|
||||
}
|
||||
|
||||
String cmd;
|
||||
String arg;
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
cmd = "cmd.exe";
|
||||
|
||||
arg = "/c echo " + massive_string;
|
||||
#else
|
||||
cmd = "/bin/echo";
|
||||
arg = massive_string;
|
||||
#endif
|
||||
|
||||
String captured;
|
||||
const auto result = ProcessOps::spawn_process_sync(
|
||||
cmd, arg, [&](StringView line) { captured += line; });
|
||||
|
||||
IAT_CHECK(result.has_value());
|
||||
IAT_CHECK_EQ(*result, 0);
|
||||
|
||||
IAT_CHECK_EQ(captured.length(), massive_string.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_multi_line() -> bool {
|
||||
|
||||
String cmd;
|
||||
String arg;
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
cmd = "cmd.exe";
|
||||
arg = "/c \"echo LineA && echo LineB\"";
|
||||
#else
|
||||
cmd = "/bin/sh";
|
||||
arg = "-c \"echo LineA; echo LineB\"";
|
||||
#endif
|
||||
|
||||
i32 line_count = 0;
|
||||
bool found_a = false;
|
||||
bool found_b = false;
|
||||
|
||||
const auto res =
|
||||
ProcessOps::spawn_process_sync(cmd, arg, [&](StringView line) {
|
||||
line_count++;
|
||||
if (line.find("LineA") != String::npos) {
|
||||
found_a = true;
|
||||
}
|
||||
if (line.find("LineB") != String::npos) {
|
||||
found_b = true;
|
||||
}
|
||||
});
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
IAT_CHECK(found_a);
|
||||
IAT_CHECK(found_b);
|
||||
|
||||
IAT_CHECK(line_count >= 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_complex_arguments() -> bool {
|
||||
|
||||
const String complex_args =
|
||||
"-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
|
||||
|
||||
const String cmd = CMD_ECHO_EXE;
|
||||
|
||||
String final_args;
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
final_args = "/c echo " + complex_args;
|
||||
#else
|
||||
final_args = complex_args;
|
||||
#endif
|
||||
|
||||
String captured;
|
||||
const auto result = ProcessOps::spawn_process_sync(
|
||||
cmd, final_args, [&](StringView line) { captured += line; });
|
||||
|
||||
IAT_CHECK(result.has_value());
|
||||
IAT_CHECK_EQ(*result, 0);
|
||||
|
||||
IAT_CHECK(captured.find("Hello World") != String::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_basic_run);
|
||||
IAT_ADD_TEST(test_arguments);
|
||||
IAT_ADD_TEST(test_exit_codes);
|
||||
IAT_ADD_TEST(test_missing_exe);
|
||||
IAT_ADD_TEST(test_large_output);
|
||||
IAT_ADD_TEST(test_multi_line);
|
||||
IAT_ADD_TEST(test_complex_arguments);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, ProcessOps)
|
||||
107
Tests/Unit/RingBuffer.cpp
Normal file
107
Tests/Unit/RingBuffer.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/ADT/RingBuffer.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, RingBuffer)
|
||||
|
||||
auto test_push_pop() -> bool {
|
||||
|
||||
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 1024);
|
||||
|
||||
auto producer_res = RingBufferView::create(Span<u8>(memory), true);
|
||||
IAT_CHECK(producer_res.has_value());
|
||||
auto producer = std::move(*producer_res);
|
||||
|
||||
auto consumer_res = RingBufferView::create(Span<u8>(memory), false);
|
||||
IAT_CHECK(consumer_res.has_value());
|
||||
auto consumer = std::move(*consumer_res);
|
||||
|
||||
String msg = "Hello RingBuffer";
|
||||
const auto push_res = producer.push(
|
||||
1, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size()));
|
||||
IAT_CHECK(push_res.has_value());
|
||||
|
||||
RingBufferView::PacketHeader header;
|
||||
u8 read_buf[128];
|
||||
|
||||
const auto pop_res = consumer.pop(header, Span<u8>(read_buf, 128));
|
||||
IAT_CHECK(pop_res.has_value());
|
||||
|
||||
const auto bytes_read_opt = *pop_res;
|
||||
IAT_CHECK(bytes_read_opt.has_value());
|
||||
|
||||
const usize bytes_read = *bytes_read_opt;
|
||||
|
||||
IAT_CHECK_EQ(header.id, static_cast<u16>(1));
|
||||
IAT_CHECK_EQ(bytes_read, static_cast<usize>(msg.size()));
|
||||
|
||||
String read_msg(reinterpret_cast<char *>(read_buf), bytes_read);
|
||||
IAT_CHECK_EQ(read_msg, msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_wrap_around() -> bool {
|
||||
|
||||
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 100);
|
||||
|
||||
auto rb_res = RingBufferView::create(Span<u8>(memory), true);
|
||||
IAT_CHECK(rb_res.has_value());
|
||||
auto rb = std::move(*rb_res);
|
||||
|
||||
Vec<u8> junk(80, 0xFF);
|
||||
const auto push1 = rb.push(1, junk);
|
||||
IAT_CHECK(push1.has_value());
|
||||
|
||||
RingBufferView::PacketHeader header;
|
||||
u8 out_buf[100];
|
||||
const auto pop1 = rb.pop(header, out_buf);
|
||||
IAT_CHECK(pop1.has_value());
|
||||
IAT_CHECK(pop1->has_value());
|
||||
|
||||
Vec<u8> wrap_data(40, 0xAA);
|
||||
const auto push2 = rb.push(2, wrap_data);
|
||||
IAT_CHECK(push2.has_value());
|
||||
|
||||
const auto pop2 = rb.pop(header, out_buf);
|
||||
IAT_CHECK(pop2.has_value());
|
||||
IAT_CHECK(pop2->has_value());
|
||||
|
||||
const usize pop_size = *pop2.value();
|
||||
IAT_CHECK_EQ(pop_size, static_cast<usize>(40));
|
||||
|
||||
bool match = true;
|
||||
for (usize i = 0; i < 40; i++) {
|
||||
if (out_buf[i] != 0xAA) {
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
IAT_CHECK(match);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_push_pop);
|
||||
IAT_ADD_TEST(test_wrap_around);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, RingBuffer)
|
||||
102
Tests/Unit/SIMD/FloatVec4.cpp
Normal file
102
Tests/Unit/SIMD/FloatVec4.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/SIMD.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, FloatVec4)
|
||||
|
||||
auto test_float_arithmetic() -> bool {
|
||||
FloatVec4 v1(10.0f, 20.0f, 30.0f, 40.0f);
|
||||
FloatVec4 v2(2.0f, 4.0f, 5.0f, 8.0f);
|
||||
|
||||
alignas(16) f32 res[4];
|
||||
|
||||
(v1 / v2).store(res);
|
||||
IAT_CHECK_APPROX(res[0], 5.0f);
|
||||
IAT_CHECK_APPROX(res[3], 5.0f);
|
||||
|
||||
(v1 * v2).store(res);
|
||||
IAT_CHECK_APPROX(res[0], 20.0f);
|
||||
|
||||
(v1 + v2).store(res);
|
||||
IAT_CHECK_APPROX(res[0], 12.0f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_math_helpers() -> bool {
|
||||
alignas(16) f32 res[4];
|
||||
|
||||
FloatVec4 v_sq(4.0f, 9.0f, 16.0f, 25.0f);
|
||||
v_sq.sqrt().store(res);
|
||||
IAT_CHECK_APPROX(res[0], 2.0f);
|
||||
IAT_CHECK_APPROX(res[3], 5.0f);
|
||||
|
||||
FloatVec4 v_neg(-1.0f, -5.0f, 10.0f, -0.0f);
|
||||
v_neg.abs().store(res);
|
||||
IAT_CHECK_APPROX(res[0], 1.0f);
|
||||
IAT_CHECK_APPROX(res[2], 10.0f);
|
||||
|
||||
FloatVec4 v_clamp(-100.0f, 0.0f, 50.0f, 200.0f);
|
||||
v_clamp.clamp(0.0f, 100.0f).store(res);
|
||||
IAT_CHECK_APPROX(res[0], 0.0f);
|
||||
IAT_CHECK_APPROX(res[2], 50.0f);
|
||||
IAT_CHECK_APPROX(res[3], 100.0f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_approx_math() -> bool {
|
||||
alignas(16) f32 res[4];
|
||||
FloatVec4 v(16.0f, 25.0f, 100.0f, 1.0f);
|
||||
|
||||
v.rsqrt().store(res);
|
||||
|
||||
IAT_CHECK_APPROX(res[0], 0.25f);
|
||||
IAT_CHECK_APPROX(res[2], 0.1f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_linear_algebra() -> bool {
|
||||
FloatVec4 v1(1.0f, 2.0f, 3.0f, 4.0f);
|
||||
FloatVec4 v2(1.0f, 0.0f, 1.0f, 0.0f);
|
||||
|
||||
f32 dot = v1.dot(v2);
|
||||
IAT_CHECK_APPROX(dot, 4.0f);
|
||||
|
||||
FloatVec4 v_norm(10.0f, 0.0f, 0.0f, 0.0f);
|
||||
alignas(16) f32 res[4];
|
||||
|
||||
v_norm.normalize().store(res);
|
||||
IAT_CHECK_APPROX(res[0], 1.0f);
|
||||
IAT_CHECK_APPROX(res[1], 0.0f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_float_arithmetic);
|
||||
IAT_ADD_TEST(test_math_helpers);
|
||||
IAT_ADD_TEST(test_approx_math);
|
||||
IAT_ADD_TEST(test_linear_algebra);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, FloatVec4)
|
||||
144
Tests/Unit/SIMD/IntVec4.cpp
Normal file
144
Tests/Unit/SIMD/IntVec4.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/SIMD.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, IntVec4)
|
||||
|
||||
auto test_constructors() -> bool {
|
||||
IntVec4 v_broadcast(10);
|
||||
alignas(16) u32 store_buf[4];
|
||||
v_broadcast.store(store_buf);
|
||||
|
||||
IAT_CHECK_EQ(store_buf[0], 10U);
|
||||
IAT_CHECK_EQ(store_buf[3], 10U);
|
||||
|
||||
IntVec4 v_comp(1, 2, 3, 4);
|
||||
v_comp.store(store_buf);
|
||||
IAT_CHECK_EQ(store_buf[0], 1U);
|
||||
IAT_CHECK_EQ(store_buf[3], 4U);
|
||||
|
||||
alignas(16) u32 src_buf[4] = {100, 200, 300, 400};
|
||||
IntVec4 v_load = IntVec4::load(src_buf);
|
||||
v_load.store(store_buf);
|
||||
IAT_CHECK_EQ(store_buf[1], 200U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_arithmetic() -> bool {
|
||||
const IntVec4 v1(10, 20, 30, 40);
|
||||
const IntVec4 v2(1, 2, 3, 4);
|
||||
|
||||
IntVec4 v_add = v1 + v2;
|
||||
alignas(16) u32 res[4];
|
||||
v_add.store(res);
|
||||
IAT_CHECK_EQ(res[0], 11U);
|
||||
IAT_CHECK_EQ(res[3], 44U);
|
||||
|
||||
IntVec4 v_sub = v1 - v2;
|
||||
v_sub.store(res);
|
||||
IAT_CHECK_EQ(res[0], 9U);
|
||||
|
||||
IntVec4 v_mul = v1 * v2;
|
||||
v_mul.store(res);
|
||||
IAT_CHECK_EQ(res[0], 10U);
|
||||
IAT_CHECK_EQ(res[2], 90U);
|
||||
IAT_CHECK_EQ(res[3], 160U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_bitwise() -> bool {
|
||||
const IntVec4 v_all_ones(0xFFFFFFFF);
|
||||
const IntVec4 v_zero((u32)0);
|
||||
const IntVec4 v_pattern(0xAAAAAAAA);
|
||||
|
||||
alignas(16) u32 res[4];
|
||||
|
||||
(v_all_ones & v_pattern).store(res);
|
||||
IAT_CHECK_EQ(res[0], 0xAAAAAAAAU);
|
||||
|
||||
(v_zero | v_pattern).store(res);
|
||||
IAT_CHECK_EQ(res[0], 0xAAAAAAAAU);
|
||||
|
||||
(v_all_ones ^ v_pattern).store(res);
|
||||
IAT_CHECK_EQ(res[0], 0x55555555U);
|
||||
|
||||
(~v_pattern).store(res);
|
||||
IAT_CHECK_EQ(res[0], 0x55555555U);
|
||||
|
||||
const IntVec4 v_shift(1);
|
||||
(v_shift << 1).store(res);
|
||||
IAT_CHECK_EQ(res[0], 2U);
|
||||
|
||||
const IntVec4 v_shift_right(4);
|
||||
(v_shift_right >> 1).store(res);
|
||||
IAT_CHECK_EQ(res[0], 2U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_saturation() -> bool {
|
||||
const u32 max = 0xFFFFFFFF;
|
||||
const IntVec4 v_high(max - 10);
|
||||
const IntVec4 v_add(20);
|
||||
|
||||
alignas(16) u32 res[4];
|
||||
|
||||
v_high.sat_add(v_add).store(res);
|
||||
IAT_CHECK_EQ(res[0], max);
|
||||
|
||||
const IntVec4 v_low(10);
|
||||
const IntVec4 v_sub(20);
|
||||
v_low.sat_sub(v_sub).store(res);
|
||||
IAT_CHECK_EQ(res[0], 0U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_advanced_ops() -> bool {
|
||||
const IntVec4 v(0, 50, 100, 150);
|
||||
alignas(16) u32 res[4];
|
||||
|
||||
v.clamp(40, 110).store(res);
|
||||
IAT_CHECK_EQ(res[0], 40U);
|
||||
IAT_CHECK_EQ(res[1], 50U);
|
||||
IAT_CHECK_EQ(res[2], 100U);
|
||||
IAT_CHECK_EQ(res[3], 110U);
|
||||
|
||||
const IntVec4 a(2);
|
||||
const IntVec4 b(10);
|
||||
const IntVec4 c(5);
|
||||
a.mult_add(b, c).store(res);
|
||||
IAT_CHECK_EQ(res[0], 25U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_constructors);
|
||||
IAT_ADD_TEST(test_arithmetic);
|
||||
IAT_ADD_TEST(test_bitwise);
|
||||
IAT_ADD_TEST(test_saturation);
|
||||
IAT_ADD_TEST(test_advanced_ops);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, IntVec4)
|
||||
107
Tests/Unit/SocketOps.cpp
Normal file
107
Tests/Unit/SocketOps.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/SocketOps.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, SocketOps)
|
||||
|
||||
auto test_initialization() -> bool {
|
||||
IAT_CHECK(SocketOps::is_initialized());
|
||||
|
||||
const auto res = SocketOps::initialize();
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
SocketOps::terminate();
|
||||
|
||||
IAT_CHECK(SocketOps::is_initialized());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_port_availability() -> bool {
|
||||
|
||||
const u16 port = 54321;
|
||||
|
||||
(void)SocketOps::is_port_available_tcp(port);
|
||||
(void)SocketOps::is_port_available_udp(port);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_unix_socket_lifecycle() -> bool {
|
||||
const String socket_path = "iatest_ipc.sock";
|
||||
|
||||
SocketOps::unlink_file(socket_path.c_str());
|
||||
|
||||
auto server_res = SocketOps::create_unix_socket();
|
||||
IAT_CHECK(server_res.has_value());
|
||||
SocketHandle server = *server_res;
|
||||
|
||||
auto bind_res = SocketOps::bind_unix_socket(server, socket_path.c_str());
|
||||
if (!bind_res) {
|
||||
|
||||
SocketOps::close(server);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto listen_res = SocketOps::listen(server);
|
||||
IAT_CHECK(listen_res.has_value());
|
||||
|
||||
auto client_res = SocketOps::create_unix_socket();
|
||||
IAT_CHECK(client_res.has_value());
|
||||
SocketHandle client = *client_res;
|
||||
|
||||
auto connect_res =
|
||||
SocketOps::connect_unix_socket(client, socket_path.c_str());
|
||||
IAT_CHECK(connect_res.has_value());
|
||||
|
||||
SocketOps::close(client);
|
||||
SocketOps::close(server);
|
||||
SocketOps::unlink_file(socket_path.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_unix_socket_errors() -> bool {
|
||||
const String socket_path = "iatest_missing.sock";
|
||||
|
||||
SocketOps::unlink_file(socket_path.c_str());
|
||||
|
||||
auto client_res = SocketOps::create_unix_socket();
|
||||
IAT_CHECK(client_res.has_value());
|
||||
SocketHandle client = *client_res;
|
||||
|
||||
auto connect_res =
|
||||
SocketOps::connect_unix_socket(client, socket_path.c_str());
|
||||
IAT_CHECK_NOT(connect_res.has_value());
|
||||
|
||||
SocketOps::close(client);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_initialization);
|
||||
IAT_ADD_TEST(test_port_availability);
|
||||
IAT_ADD_TEST(test_unix_socket_lifecycle);
|
||||
IAT_ADD_TEST(test_unix_socket_errors);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, SocketOps)
|
||||
137
Tests/Unit/StreamReader.cpp
Normal file
137
Tests/Unit/StreamReader.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/StreamReader.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, StreamReader)
|
||||
|
||||
auto test_read_uint8() -> bool {
|
||||
u8 data[] = {0xAA, 0xBB, 0xCC};
|
||||
StreamReader reader(data);
|
||||
|
||||
auto val1 = reader.read<u8>();
|
||||
IAT_CHECK(val1.has_value());
|
||||
IAT_CHECK_EQ(*val1, 0xAA);
|
||||
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(1));
|
||||
|
||||
auto val2 = reader.read<u8>();
|
||||
IAT_CHECK(val2.has_value());
|
||||
IAT_CHECK_EQ(*val2, 0xBB);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_read_multi_byte() -> bool {
|
||||
|
||||
u8 data[] = {0x01, 0x02, 0x03, 0x04};
|
||||
StreamReader reader(data);
|
||||
|
||||
auto val = reader.read<u32>();
|
||||
IAT_CHECK(val.has_value());
|
||||
|
||||
IAT_CHECK_EQ(*val, static_cast<u32>(0x04030201));
|
||||
|
||||
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(4));
|
||||
IAT_CHECK(reader.is_eof());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_read_float() -> bool {
|
||||
const f32 pi = 3.14159f;
|
||||
|
||||
u8 data[4];
|
||||
std::memcpy(data, &pi, 4);
|
||||
|
||||
StreamReader reader(data);
|
||||
auto val = reader.read<f32>();
|
||||
|
||||
IAT_CHECK(val.has_value());
|
||||
IAT_CHECK_APPROX(*val, pi);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_read_buffer() -> bool {
|
||||
u8 src[] = {1, 2, 3, 4, 5};
|
||||
u8 dst[3] = {0};
|
||||
StreamReader reader(src);
|
||||
|
||||
const auto res = reader.read(dst, 3);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
IAT_CHECK_EQ(dst[0], 1);
|
||||
IAT_CHECK_EQ(dst[1], 2);
|
||||
IAT_CHECK_EQ(dst[2], 3);
|
||||
|
||||
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(3));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_navigation() -> bool {
|
||||
u8 data[10] = {0};
|
||||
StreamReader reader(data);
|
||||
|
||||
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(10));
|
||||
|
||||
reader.skip(5);
|
||||
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(5));
|
||||
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(5));
|
||||
|
||||
reader.skip(100);
|
||||
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(10));
|
||||
IAT_CHECK(reader.is_eof());
|
||||
|
||||
reader.seek(2);
|
||||
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(2));
|
||||
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(8));
|
||||
IAT_CHECK_NOT(reader.is_eof());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_boundary_checks() -> bool {
|
||||
u8 data[] = {0x00, 0x00};
|
||||
StreamReader reader(data);
|
||||
|
||||
(void)reader.read<u16>();
|
||||
IAT_CHECK(reader.is_eof());
|
||||
|
||||
auto val = reader.read<u8>();
|
||||
IAT_CHECK_NOT(val.has_value());
|
||||
|
||||
u8 buf[1];
|
||||
auto batch = reader.read(buf, 1);
|
||||
IAT_CHECK_NOT(batch.has_value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_read_uint8);
|
||||
IAT_ADD_TEST(test_read_multi_byte);
|
||||
IAT_ADD_TEST(test_read_float);
|
||||
IAT_ADD_TEST(test_read_buffer);
|
||||
IAT_ADD_TEST(test_navigation);
|
||||
IAT_ADD_TEST(test_boundary_checks);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, StreamReader)
|
||||
116
Tests/Unit/StreamWriter.cpp
Normal file
116
Tests/Unit/StreamWriter.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/StreamWriter.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, StreamWriter)
|
||||
|
||||
auto test_memory_writer() -> bool {
|
||||
StreamWriter writer;
|
||||
|
||||
IAT_CHECK(writer.write(static_cast<u8>(0xAA), 1).has_value());
|
||||
|
||||
const u32 val = 0x12345678;
|
||||
IAT_CHECK(writer.write(val).has_value());
|
||||
|
||||
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(1 + 4));
|
||||
|
||||
const u8 *ptr = writer.data();
|
||||
IAT_CHECK_EQ(ptr[0], 0xAA);
|
||||
IAT_CHECK_EQ(ptr[1], 0x78);
|
||||
IAT_CHECK_EQ(ptr[4], 0x12);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_fixed_buffer() -> bool {
|
||||
u8 buffer[4] = {0};
|
||||
StreamWriter writer(Span<u8>(buffer, 4));
|
||||
|
||||
IAT_CHECK(writer.write(static_cast<u8>(0xFF), 2).has_value());
|
||||
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(2));
|
||||
|
||||
IAT_CHECK(writer.write(static_cast<u8>(0xEE), 2).has_value());
|
||||
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(4));
|
||||
|
||||
const auto res = writer.write(static_cast<u8>(0x00), 1);
|
||||
IAT_CHECK_NOT(res.has_value());
|
||||
|
||||
IAT_CHECK_EQ(buffer[0], 0xFF);
|
||||
IAT_CHECK_EQ(buffer[1], 0xFF);
|
||||
IAT_CHECK_EQ(buffer[2], 0xEE);
|
||||
IAT_CHECK_EQ(buffer[3], 0xEE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_file_writer() -> bool {
|
||||
const Path path = "test_stream_writer.bin";
|
||||
|
||||
if (std::filesystem::exists(path)) {
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
|
||||
{
|
||||
auto res = StreamWriter::create_from_file(path);
|
||||
IAT_CHECK(res.has_value());
|
||||
StreamWriter writer = std::move(*res);
|
||||
|
||||
const String hello = "Hello World";
|
||||
IAT_CHECK(writer.write(hello.data(), hello.size()).has_value());
|
||||
|
||||
IAT_CHECK(writer.flush().has_value());
|
||||
}
|
||||
|
||||
auto read_res = FileOps::read_binary_file(path);
|
||||
IAT_CHECK(read_res.has_value());
|
||||
|
||||
const String read_str(reinterpret_cast<const char *>(read_res->data()),
|
||||
read_res->size());
|
||||
IAT_CHECK_EQ(read_str, String("Hello World"));
|
||||
|
||||
std::filesystem::remove(path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_primitives() -> bool {
|
||||
StreamWriter writer;
|
||||
|
||||
const f32 f = 1.5f;
|
||||
const u64 big = 0xDEADBEEFCAFEBABE;
|
||||
|
||||
IAT_CHECK(writer.write(f).has_value());
|
||||
IAT_CHECK(writer.write(big).has_value());
|
||||
|
||||
IAT_CHECK_EQ(writer.cursor(), sizeof(f32) + sizeof(u64));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_memory_writer);
|
||||
IAT_ADD_TEST(test_fixed_buffer);
|
||||
IAT_ADD_TEST(test_file_writer);
|
||||
IAT_ADD_TEST(test_primitives);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, StreamWriter)
|
||||
111
Tests/Unit/StringOps.cpp
Normal file
111
Tests/Unit/StringOps.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/StringOps.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, StringOps)
|
||||
|
||||
auto test_base64_encode() -> bool {
|
||||
|
||||
{
|
||||
const String s = "Hello World";
|
||||
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
|
||||
const String encoded = StringOps::encode_base64(data);
|
||||
IAT_CHECK_EQ(encoded, String("SGVsbG8gV29ybGQ="));
|
||||
}
|
||||
|
||||
{
|
||||
const String s = "M";
|
||||
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
|
||||
const String encoded = StringOps::encode_base64(data);
|
||||
IAT_CHECK_EQ(encoded, String("TQ=="));
|
||||
}
|
||||
|
||||
{
|
||||
const String s = "Ma";
|
||||
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
|
||||
const String encoded = StringOps::encode_base64(data);
|
||||
IAT_CHECK_EQ(encoded, String("TWE="));
|
||||
}
|
||||
|
||||
{
|
||||
const String s = "Man";
|
||||
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
|
||||
const String encoded = StringOps::encode_base64(data);
|
||||
IAT_CHECK_EQ(encoded, String("TWFu"));
|
||||
}
|
||||
|
||||
{
|
||||
const String encoded = StringOps::encode_base64({});
|
||||
IAT_CHECK(encoded.empty());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_base64_decode() -> bool {
|
||||
|
||||
{
|
||||
const String encoded = "SGVsbG8gV29ybGQ=";
|
||||
const Vec<u8> decoded = StringOps::decode_base64(encoded);
|
||||
const String result(reinterpret_cast<const char *>(decoded.data()),
|
||||
decoded.size());
|
||||
IAT_CHECK_EQ(result, String("Hello World"));
|
||||
}
|
||||
|
||||
{
|
||||
const Vec<u8> decoded = StringOps::decode_base64("");
|
||||
IAT_CHECK(decoded.empty());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_base64_round_trip() -> bool {
|
||||
Vec<u8> original;
|
||||
original.reserve(256);
|
||||
for (usize i = 0; i < 256; ++i) {
|
||||
original.push_back(static_cast<u8>(i));
|
||||
}
|
||||
|
||||
const String encoded = StringOps::encode_base64(original);
|
||||
const Vec<u8> decoded = StringOps::decode_base64(encoded);
|
||||
|
||||
IAT_CHECK_EQ(original.size(), decoded.size());
|
||||
|
||||
bool match = true;
|
||||
for (usize i = 0; i < original.size(); ++i) {
|
||||
if (original[i] != decoded[i]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
IAT_CHECK(match);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_base64_encode);
|
||||
IAT_ADD_TEST(test_base64_decode);
|
||||
IAT_ADD_TEST(test_base64_round_trip);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, StringOps)
|
||||
162
Tests/Unit/Utils.cpp
Normal file
162
Tests/Unit/Utils.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/Utils.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
struct TestVec3 {
|
||||
f32 x, y, z;
|
||||
|
||||
bool operator==(const TestVec3 &other) const {
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
};
|
||||
|
||||
IA_MAKE_HASHABLE(TestVec3, &TestVec3::x, &TestVec3::y, &TestVec3::z);
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, Utils)
|
||||
|
||||
auto test_hex_conversion() -> bool {
|
||||
|
||||
u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
|
||||
String hex = Utils::binary_to_hex_string(bin);
|
||||
|
||||
IAT_CHECK_EQ(hex, String("DEADBEEF00FF"));
|
||||
|
||||
auto res_upper = Utils::hex_string_to_binary("DEADBEEF00FF");
|
||||
IAT_CHECK(res_upper.has_value());
|
||||
IAT_CHECK_EQ(res_upper->size(), static_cast<usize>(6));
|
||||
IAT_CHECK_EQ((*res_upper)[0], 0xDE);
|
||||
IAT_CHECK_EQ((*res_upper)[5], 0xFF);
|
||||
|
||||
auto res_lower = Utils::hex_string_to_binary("deadbeef00ff");
|
||||
IAT_CHECK(res_lower.has_value());
|
||||
IAT_CHECK_EQ((*res_lower)[0], 0xDE);
|
||||
|
||||
Vec<u8> original = {1, 2, 3, 4, 5};
|
||||
String s = Utils::binary_to_hex_string(original);
|
||||
auto back = Utils::hex_string_to_binary(s);
|
||||
IAT_CHECK(back.has_value());
|
||||
IAT_CHECK_EQ(original.size(), back->size());
|
||||
IAT_CHECK_EQ(original[2], (*back)[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_hex_errors() -> bool {
|
||||
|
||||
auto odd = Utils::hex_string_to_binary("ABC");
|
||||
IAT_CHECK_NOT(odd.has_value());
|
||||
|
||||
auto invalid = Utils::hex_string_to_binary("ZZTOP");
|
||||
IAT_CHECK_NOT(invalid.has_value());
|
||||
|
||||
auto empty = Utils::hex_string_to_binary("");
|
||||
IAT_CHECK(empty.has_value());
|
||||
IAT_CHECK_EQ(empty->size(), static_cast<usize>(0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_sort() -> bool {
|
||||
Vec<i32> nums = {5, 1, 4, 2, 3};
|
||||
|
||||
Utils::sort(nums);
|
||||
|
||||
IAT_CHECK_EQ(nums[0], 1);
|
||||
IAT_CHECK_EQ(nums[1], 2);
|
||||
IAT_CHECK_EQ(nums[2], 3);
|
||||
IAT_CHECK_EQ(nums[3], 4);
|
||||
IAT_CHECK_EQ(nums[4], 5);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_binary_search() -> bool {
|
||||
|
||||
Vec<i32> nums = {10, 20, 20, 20, 30};
|
||||
|
||||
auto it_left = Utils::binary_search_left(nums, 20);
|
||||
IAT_CHECK(it_left != nums.end());
|
||||
IAT_CHECK_EQ(*it_left, 20);
|
||||
IAT_CHECK_EQ(std::distance(nums.begin(), it_left), 1);
|
||||
|
||||
auto it_right = Utils::binary_search_right(nums, 20);
|
||||
IAT_CHECK(it_right != nums.end());
|
||||
IAT_CHECK_EQ(*it_right, 30);
|
||||
IAT_CHECK_EQ(std::distance(nums.begin(), it_right), 4);
|
||||
|
||||
auto it_fail = Utils::binary_search_left(nums, 99);
|
||||
IAT_CHECK(it_fail == nums.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_hash_basics() -> bool {
|
||||
u64 h1 = 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");
|
||||
|
||||
IAT_CHECK_EQ(h1, h2);
|
||||
|
||||
IAT_CHECK_NEQ(h1, h3);
|
||||
|
||||
u64 order_a = Utils::compute_hash(1, 2);
|
||||
u64 order_b = Utils::compute_hash(2, 1);
|
||||
IAT_CHECK_NEQ(order_a, order_b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_hash_macro() -> bool {
|
||||
TestVec3 v1{1.0f, 2.0f, 3.0f};
|
||||
TestVec3 v2{1.0f, 2.0f, 3.0f};
|
||||
TestVec3 v3{1.0f, 2.0f, 4.0f};
|
||||
|
||||
ankerl::unordered_dense::hash<TestVec3> hasher;
|
||||
|
||||
u64 h1 = hasher(v1);
|
||||
u64 h2 = hasher(v2);
|
||||
u64 h3 = hasher(v3);
|
||||
|
||||
IAT_CHECK_EQ(h1, h2);
|
||||
IAT_CHECK_NEQ(h1, h3);
|
||||
|
||||
u64 h_manual = 0;
|
||||
Utils::hash_combine(h_manual, v1);
|
||||
|
||||
u64 h_wrapper = Utils::compute_hash(v1);
|
||||
|
||||
IAT_CHECK_EQ(h_manual, h_wrapper);
|
||||
|
||||
IAT_CHECK_NEQ(h1, h_wrapper);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_hex_conversion);
|
||||
IAT_ADD_TEST(test_hex_errors);
|
||||
IAT_ADD_TEST(test_sort);
|
||||
IAT_ADD_TEST(test_binary_search);
|
||||
IAT_ADD_TEST(test_hash_basics);
|
||||
IAT_ADD_TEST(test_hash_macro);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, Utils)
|
||||
112
Tests/Unit/XML.cpp
Normal file
112
Tests/Unit/XML.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2026 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/XML.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
IAT_BEGIN_BLOCK(Core, XML)
|
||||
|
||||
auto test_parse_string() -> bool {
|
||||
const String xml_content = R"(
|
||||
<root>
|
||||
<item id="1">Value1</item>
|
||||
<item id="2">Value2</item>
|
||||
</root>
|
||||
)";
|
||||
|
||||
auto res = XML::parse_from_string(xml_content);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
auto &doc = *res;
|
||||
auto root = doc.child("root");
|
||||
IAT_CHECK(root);
|
||||
|
||||
auto item1 = root.find_child_by_attribute("item", "id", "1");
|
||||
IAT_CHECK(item1);
|
||||
IAT_CHECK_EQ(String(item1.child_value()), String("Value1"));
|
||||
|
||||
auto item2 = root.find_child_by_attribute("item", "id", "2");
|
||||
IAT_CHECK(item2);
|
||||
IAT_CHECK_EQ(String(item2.child_value()), String("Value2"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_parse_error() -> bool {
|
||||
const String invalid_xml = "<root><unclosed>";
|
||||
auto res = XML::parse_from_string(invalid_xml);
|
||||
IAT_CHECK_NOT(res.has_value());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_serialize() -> bool {
|
||||
const String xml_content = "<root><node>Text</node></root>";
|
||||
auto res = XML::parse_from_string(xml_content);
|
||||
IAT_CHECK(res.has_value());
|
||||
|
||||
String output = XML::serialize_to_string(*res);
|
||||
|
||||
IAT_CHECK(output.find("<root>") != String::npos);
|
||||
IAT_CHECK(output.find("<node>Text</node>") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_escape() -> bool {
|
||||
const String raw = "< & > \" '";
|
||||
const String escaped = XML::escape_xml_string(raw);
|
||||
|
||||
IAT_CHECK(escaped.find("<") != String::npos);
|
||||
IAT_CHECK(escaped.find("&") != String::npos);
|
||||
IAT_CHECK(escaped.find(">") != String::npos);
|
||||
IAT_CHECK(escaped.find(""") != String::npos);
|
||||
IAT_CHECK(escaped.find("'") != String::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto test_file_io() -> bool {
|
||||
const Path path = "test_temp_xml_doc.xml";
|
||||
const String content = "<config><ver>1.0</ver></config>";
|
||||
|
||||
auto write_res = FileOps::write_text_file(path, content, true);
|
||||
IAT_CHECK(write_res.has_value());
|
||||
|
||||
auto parse_res = XML::parse_from_file(path);
|
||||
IAT_CHECK(parse_res.has_value());
|
||||
|
||||
auto &doc = *parse_res;
|
||||
IAT_CHECK_EQ(String(doc.child("config").child("ver").child_value()),
|
||||
String("1.0"));
|
||||
|
||||
std::filesystem::remove(path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IAT_BEGIN_TEST_LIST()
|
||||
IAT_ADD_TEST(test_parse_string);
|
||||
IAT_ADD_TEST(test_parse_error);
|
||||
IAT_ADD_TEST(test_serialize);
|
||||
IAT_ADD_TEST(test_escape);
|
||||
IAT_ADD_TEST(test_file_io);
|
||||
IAT_END_TEST_LIST()
|
||||
|
||||
IAT_END_BLOCK()
|
||||
|
||||
IAT_REGISTER_ENTRY(Core, XML)
|
||||
Reference in New Issue
Block a user