This commit is contained in:
@ -13,9 +13,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <IACore/Utils.hpp>
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
#include <IACore/Utils.hpp>
|
||||
|
||||
using namespace IACore;
|
||||
|
||||
@ -23,16 +22,14 @@ using namespace IACore;
|
||||
// Test Structs for Hashing (Must be defined at Global Scope)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct TestVec3
|
||||
{
|
||||
f32 x, y, z;
|
||||
struct TestVec3 {
|
||||
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
|
||||
{
|
||||
return x == other.x && y == other.y && z == other.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
|
||||
@ -48,175 +45,169 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
||||
// -------------------------------------------------------------------------
|
||||
// 1. Binary <-> Hex String Conversion
|
||||
// -------------------------------------------------------------------------
|
||||
bool TestHexConversion()
|
||||
{
|
||||
// A. Binary To Hex
|
||||
u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
|
||||
String hex = IACore::Utils::BinaryToHexString(bin);
|
||||
auto test_hex_conversion() -> bool {
|
||||
// A. Binary To Hex
|
||||
u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
|
||||
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 resUpper = IACore::Utils::HexStringToBinary("DEADBEEF00FF");
|
||||
IAT_CHECK(resUpper.has_value());
|
||||
IAT_CHECK_EQ(resUpper->size(), (usize) 6);
|
||||
IAT_CHECK_EQ((*resUpper)[0], 0xDE);
|
||||
IAT_CHECK_EQ((*resUpper)[5], 0xFF);
|
||||
// B. Hex To Binary (Valid Upper)
|
||||
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);
|
||||
|
||||
// C. Hex To Binary (Valid Lower/Mixed)
|
||||
auto resLower = IACore::Utils::HexStringToBinary("deadbeef00ff");
|
||||
IAT_CHECK(resLower.has_value());
|
||||
IAT_CHECK_EQ((*resLower)[0], 0xDE);
|
||||
// C. Hex To Binary (Valid Lower/Mixed)
|
||||
auto res_lower = Utils::hex_string_to_binary("deadbeef00ff");
|
||||
IAT_CHECK(res_lower.has_value());
|
||||
IAT_CHECK_EQ((*res_lower)[0], 0xDE);
|
||||
|
||||
// D. Round Trip Integrity
|
||||
Vec<u8> 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]);
|
||||
// D. Round Trip Integrity
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 2. Hex Error Handling
|
||||
// -------------------------------------------------------------------------
|
||||
bool TestHexErrors()
|
||||
{
|
||||
// Odd Length
|
||||
auto odd = IACore::Utils::HexStringToBinary("ABC");
|
||||
IAT_CHECK_NOT(odd.has_value());
|
||||
auto test_hex_errors() -> bool {
|
||||
// Odd Length
|
||||
auto odd = Utils::hex_string_to_binary("ABC");
|
||||
IAT_CHECK_NOT(odd.has_value());
|
||||
|
||||
// Invalid Characters
|
||||
auto invalid = IACore::Utils::HexStringToBinary("ZZTOP");
|
||||
IAT_CHECK_NOT(invalid.has_value());
|
||||
// Invalid Characters
|
||||
auto invalid = Utils::hex_string_to_binary("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(), (usize) 0);
|
||||
// Empty string is valid (empty vector)
|
||||
auto empty = Utils::hex_string_to_binary("");
|
||||
IAT_CHECK(empty.has_value());
|
||||
IAT_CHECK_EQ(empty->size(), static_cast<usize>(0));
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 3. Algorithms: Sorting
|
||||
// -------------------------------------------------------------------------
|
||||
bool TestSort()
|
||||
{
|
||||
Vec<int> nums = {5, 1, 4, 2, 3};
|
||||
auto test_sort() -> bool {
|
||||
Vec<i32> nums = {5, 1, 4, 2, 3};
|
||||
|
||||
IACore::Utils::Sort(nums);
|
||||
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);
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 4. Algorithms: Binary Search (Left/Right)
|
||||
// -------------------------------------------------------------------------
|
||||
bool TestBinarySearch()
|
||||
{
|
||||
// Must be sorted for Binary Search
|
||||
Vec<int> nums = {10, 20, 20, 20, 30};
|
||||
auto test_binary_search() -> bool {
|
||||
// Must be sorted for Binary Search
|
||||
Vec<i32> 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 Left (Lower Bound) -> First element >= value
|
||||
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); // 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 Right (Upper Bound) -> First element > value
|
||||
auto it_right = Utils::binary_search_right(nums, 20);
|
||||
IAT_CHECK(it_right != nums.end());
|
||||
IAT_CHECK_EQ(*it_right, 30); // Points to 30
|
||||
IAT_CHECK_EQ(std::distance(nums.begin(), it_right), 4); // Index 4
|
||||
|
||||
// Search for non-existent
|
||||
auto itFail = IACore::Utils::BinarySearchLeft(nums, 99);
|
||||
IAT_CHECK(itFail == nums.end());
|
||||
// Search for non-existent
|
||||
auto it_fail = Utils::binary_search_left(nums, 99);
|
||||
IAT_CHECK(it_fail == nums.end());
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 5. Hashing Basics
|
||||
// -------------------------------------------------------------------------
|
||||
bool TestHashBasics()
|
||||
{
|
||||
u64 h1 = IACore::Utils::ComputeHash(10, 20.5f, "Hello");
|
||||
u64 h2 = IACore::Utils::ComputeHash(10, 20.5f, "Hello");
|
||||
u64 h3 = IACore::Utils::ComputeHash(10, 20.5f, "World");
|
||||
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");
|
||||
|
||||
// Determinism
|
||||
IAT_CHECK_EQ(h1, h2);
|
||||
// Determinism
|
||||
IAT_CHECK_EQ(h1, h2);
|
||||
|
||||
// Differentiation
|
||||
IAT_CHECK_NEQ(h1, h3);
|
||||
// Differentiation
|
||||
IAT_CHECK_NEQ(h1, h3);
|
||||
|
||||
// Order sensitivity (Golden ratio combine should care about order)
|
||||
// Hash(A, B) != Hash(B, A)
|
||||
u64 orderA = IACore::Utils::ComputeHash(1, 2);
|
||||
u64 orderB = IACore::Utils::ComputeHash(2, 1);
|
||||
IAT_CHECK_NEQ(orderA, orderB);
|
||||
// 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_b = Utils::compute_hash(2, 1);
|
||||
IAT_CHECK_NEQ(order_a, order_b);
|
||||
|
||||
return TRUE;
|
||||
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};
|
||||
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;
|
||||
ankerl::unordered_dense::hash<TestVec3> hasher;
|
||||
|
||||
u64 h1 = hasher(v1);
|
||||
u64 h2 = hasher(v2);
|
||||
u64 h3 = hasher(v3);
|
||||
u64 h1 = hasher(v1);
|
||||
u64 h2 = hasher(v2);
|
||||
u64 h3 = hasher(v3);
|
||||
|
||||
IAT_CHECK_EQ(h1, h2); // Same content = same hash
|
||||
IAT_CHECK_NEQ(h1, h3); // Different content = different hash
|
||||
IAT_CHECK_EQ(h1, h2); // Same content = same hash
|
||||
IAT_CHECK_NEQ(h1, h3); // Different content = different hash
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Verify ComputeHash integration
|
||||
// -------------------------------------------------------------
|
||||
// -------------------------------------------------------------
|
||||
// Verify ComputeHash integration
|
||||
// -------------------------------------------------------------
|
||||
|
||||
u64 hManual = 0;
|
||||
IACore::Utils::HashCombine(hManual, v1);
|
||||
u64 h_manual = 0;
|
||||
Utils::hash_combine(h_manual, v1);
|
||||
|
||||
u64 hWrapper = IACore::Utils::ComputeHash(v1);
|
||||
u64 h_wrapper = Utils::compute_hash(v1);
|
||||
|
||||
// This proves ComputeHash found the specialization and mixed it correctly
|
||||
IAT_CHECK_EQ(hManual, hWrapper);
|
||||
// This proves ComputeHash found the specialization and mixed it correctly
|
||||
IAT_CHECK_EQ(h_manual, h_wrapper);
|
||||
|
||||
// Verify the avalanche effect took place (hWrapper should NOT be h1)
|
||||
IAT_CHECK_NEQ(h1, hWrapper);
|
||||
// Verify the avalanche effect took place (hWrapper should NOT be h1)
|
||||
IAT_CHECK_NEQ(h1, h_wrapper);
|
||||
|
||||
return TRUE;
|
||||
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_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)
|
||||
IAT_REGISTER_ENTRY(Core, Utils)
|
||||
Reference in New Issue
Block a user