// IACore-OSS; The Core Library for All IA Open Source Projects // Copyright (C) 2025 IAS (ias@iasoft.dev) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include using namespace IACore; // ----------------------------------------------------------------------------- // Test Structs for Hashing (Must be defined at Global Scope) // ----------------------------------------------------------------------------- struct TestVec3 { FLOAT32 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 { 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); // ----------------------------------------------------------------------------- // Test Block Definition // ----------------------------------------------------------------------------- IAT_BEGIN_BLOCK(Core, Utils) // ------------------------------------------------------------------------- // 1. Binary <-> Hex String Conversion // ------------------------------------------------------------------------- BOOL TestHexConversion() { // A. Binary To Hex UINT8 bin[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF }; String hex = IACore::Utils::BinaryToHexString(bin); IAT_CHECK_EQ(hex, String("DEADBEEF00FF")); // B. Hex To Binary (Valid Upper) auto resUpper = IACore::Utils::HexStringToBinary("DEADBEEF00FF"); IAT_CHECK(resUpper.has_value()); IAT_CHECK_EQ(resUpper->size(), 6); IAT_CHECK_EQ((*resUpper)[0], 0xDE); IAT_CHECK_EQ((*resUpper)[5], 0xFF); // C. Hex To Binary (Valid Lower/Mixed) auto resLower = IACore::Utils::HexStringToBinary("deadbeef00ff"); IAT_CHECK(resLower.has_value()); IAT_CHECK_EQ((*resLower)[0], 0xDE); // D. Round Trip Integrity Vector original = { 1, 2, 3, 4, 5 }; String s = IACore::Utils::BinaryToHexString(original); auto back = IACore::Utils::HexStringToBinary(s); IAT_CHECK(back.has_value()); IAT_CHECK_EQ(original.size(), back->size()); IAT_CHECK_EQ(original[2], (*back)[2]); return TRUE; } // ------------------------------------------------------------------------- // 2. Hex Error Handling // ------------------------------------------------------------------------- BOOL TestHexErrors() { // Odd Length auto odd = IACore::Utils::HexStringToBinary("ABC"); IAT_CHECK_NOT(odd.has_value()); // Optional: IAT_CHECK_EQ(odd.error(), "Hex string must have even length"); // Invalid Characters auto invalid = IACore::Utils::HexStringToBinary("ZZTOP"); IAT_CHECK_NOT(invalid.has_value()); // Empty string is valid (empty vector) auto empty = IACore::Utils::HexStringToBinary(""); IAT_CHECK(empty.has_value()); IAT_CHECK_EQ(empty->size(), 0); return TRUE; } // ------------------------------------------------------------------------- // 3. Algorithms: Sorting // ------------------------------------------------------------------------- BOOL TestSort() { Vector nums = { 5, 1, 4, 2, 3 }; IACore::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; } // ------------------------------------------------------------------------- // 4. Algorithms: Binary Search (Left/Right) // ------------------------------------------------------------------------- BOOL TestBinarySearch() { // Must be sorted for Binary Search Vector nums = { 10, 20, 20, 20, 30 }; // Search Left (Lower Bound) -> First element >= value auto itLeft = IACore::Utils::BinarySearchLeft(nums, 20); IAT_CHECK(itLeft != nums.end()); IAT_CHECK_EQ(*itLeft, 20); IAT_CHECK_EQ(std::distance(nums.begin(), itLeft), 1); // Index 1 is first 20 // Search Right (Upper Bound) -> First element > value auto itRight = IACore::Utils::BinarySearchRight(nums, 20); IAT_CHECK(itRight != nums.end()); IAT_CHECK_EQ(*itRight, 30); // Points to 30 IAT_CHECK_EQ(std::distance(nums.begin(), itRight), 4); // Index 4 // Search for non-existent auto itFail = IACore::Utils::BinarySearchLeft(nums, 99); IAT_CHECK(itFail == nums.end()); return TRUE; } // ------------------------------------------------------------------------- // 5. Hashing Basics // ------------------------------------------------------------------------- BOOL TestHashBasics() { UINT64 h1 = IACore::Utils::ComputeHash(10, 20.5f, "Hello"); UINT64 h2 = IACore::Utils::ComputeHash(10, 20.5f, "Hello"); UINT64 h3 = IACore::Utils::ComputeHash(10, 20.5f, "World"); // Determinism IAT_CHECK_EQ(h1, h2); // Differentiation IAT_CHECK_NEQ(h1, h3); // Order sensitivity (Golden ratio combine should care about order) // Hash(A, B) != Hash(B, A) UINT64 orderA = IACore::Utils::ComputeHash(1, 2); UINT64 orderB = IACore::Utils::ComputeHash(2, 1); IAT_CHECK_NEQ(orderA, orderB); return TRUE; } // ------------------------------------------------------------------------- // 6. Macro Verification (IA_MAKE_HASHABLE) // ------------------------------------------------------------------------- BOOL TestHashMacro() { TestVec3 v1 { 1.0f, 2.0f, 3.0f }; TestVec3 v2 { 1.0f, 2.0f, 3.0f }; TestVec3 v3 { 1.0f, 2.0f, 4.0f }; // Slight change // Instantiate the hasher manually to verify the struct specialization exists ankerl::unordered_dense::hash hasher; UINT64 h1 = hasher(v1); UINT64 h2 = hasher(v2); UINT64 h3 = hasher(v3); IAT_CHECK_EQ(h1, h2); // Same content = same hash IAT_CHECK_NEQ(h1, h3); // Different content = different hash // Verify it works with ComputeHash when passed as object // (Assuming you extend ComputeHash to handle custom types via the specialized hasher) // Since ComputeHash uses ankerl::unordered_dense::hash internally, this should work: UINT64 hWrapper = IACore::Utils::ComputeHash(v1); IAT_CHECK_EQ(h1, hWrapper); return TRUE; } // ------------------------------------------------------------------------- // Registration // ------------------------------------------------------------------------- IAT_BEGIN_TEST_LIST() IAT_ADD_TEST(TestHexConversion); IAT_ADD_TEST(TestHexErrors); IAT_ADD_TEST(TestSort); IAT_ADD_TEST(TestBinarySearch); IAT_ADD_TEST(TestHashBasics); IAT_ADD_TEST(TestHashMacro); IAT_END_TEST_LIST() IAT_END_BLOCK() int main(int argc, char* argv[]) { UNUSED(argc); UNUSED(argv); // Define runner (StopOnFail=false, Verbose=true) ia::iatest::runner testRunner; // Run the BinaryReader block testRunner.testBlock(); return 0; }