mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-11 08:54:33 +00:00
Tests: Add more unit tests for common classes
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
add_executable(common-tests
|
||||
bitutils_tests.cpp
|
||||
file_system_tests.cpp
|
||||
gsvector.cpp
|
||||
gsvector_yuvtorgb_test.cpp
|
||||
hash_tests.cpp
|
||||
path_tests.cpp
|
||||
rectangle_tests.cpp
|
||||
sha256_tests.cpp
|
||||
string_tests.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,12 +1,424 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "common/align.h"
|
||||
#include "common/bitfield.h"
|
||||
#include "common/bitutils.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
// Test fixture for BitField tests
|
||||
namespace {
|
||||
// Simple test structs using different backing types and field configurations
|
||||
union TestUnion8
|
||||
{
|
||||
u8 bits;
|
||||
BitField<u8, bool, 0, 1> flag;
|
||||
BitField<u8, u8, 1, 3> value3bit;
|
||||
BitField<u8, u8, 4, 4> value4bit;
|
||||
BitField<u8, s8, 4, 4> signed4bit;
|
||||
};
|
||||
|
||||
union TestUnion16
|
||||
{
|
||||
u16 bits;
|
||||
BitField<u16, bool, 0, 1> flag;
|
||||
BitField<u16, u8, 1, 8> value8bit;
|
||||
BitField<u16, u8, 9, 7> value7bit;
|
||||
BitField<u16, s8, 8, 8> signed8bit;
|
||||
};
|
||||
|
||||
union TestUnion32
|
||||
{
|
||||
u32 bits;
|
||||
BitField<u32, bool, 0, 1> flag;
|
||||
BitField<u32, u16, 1, 16> value16bit;
|
||||
BitField<u32, u32, 17, 15> value15bit;
|
||||
BitField<u32, s16, 16, 16> signed16bit;
|
||||
};
|
||||
|
||||
union TestUnion64
|
||||
{
|
||||
u64 bits;
|
||||
BitField<u64, bool, 0, 1> flag;
|
||||
BitField<u64, u32, 1, 32> value32bit;
|
||||
BitField<u64, u64, 33, 31> value31bit;
|
||||
BitField<u64, s32, 32, 32> signed32bit;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Tests for GetMask method
|
||||
TEST(BitField, GetMask)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
EXPECT_EQ(test8.flag.GetMask(), 0x01u);
|
||||
EXPECT_EQ(test8.value3bit.GetMask(), 0x0Eu);
|
||||
EXPECT_EQ(test8.value4bit.GetMask(), 0xF0u);
|
||||
|
||||
TestUnion16 test16{};
|
||||
EXPECT_EQ(test16.flag.GetMask(), 0x0001u);
|
||||
EXPECT_EQ(test16.value8bit.GetMask(), 0x01FEu);
|
||||
EXPECT_EQ(test16.value7bit.GetMask(), 0xFE00u);
|
||||
|
||||
TestUnion32 test32{};
|
||||
EXPECT_EQ(test32.flag.GetMask(), 0x00000001u);
|
||||
EXPECT_EQ(test32.value16bit.GetMask(), 0x0001FFFEu);
|
||||
EXPECT_EQ(test32.value15bit.GetMask(), 0xFFFE0000u);
|
||||
|
||||
TestUnion64 test64{};
|
||||
EXPECT_EQ(test64.flag.GetMask(), 0x0000000000000001ULL);
|
||||
EXPECT_EQ(test64.value32bit.GetMask(), 0x00000001FFFFFFFEULL);
|
||||
EXPECT_EQ(test64.value31bit.GetMask(), 0xFFFFFFFE00000000ULL);
|
||||
}
|
||||
|
||||
// Tests for implicit conversion operator (operator DataType())
|
||||
TEST(BitField, ImplicitConversion)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0xFF;
|
||||
|
||||
bool flag_value = test8.flag;
|
||||
EXPECT_TRUE(flag_value);
|
||||
|
||||
u8 value3bit_value = test8.value3bit;
|
||||
EXPECT_EQ(value3bit_value, 7);
|
||||
|
||||
u8 value4bit_value = test8.value4bit;
|
||||
EXPECT_EQ(value4bit_value, 15);
|
||||
|
||||
// Test with zeros
|
||||
test8.bits = 0x00;
|
||||
flag_value = test8.flag;
|
||||
EXPECT_FALSE(flag_value);
|
||||
|
||||
value3bit_value = test8.value3bit;
|
||||
EXPECT_EQ(value3bit_value, 0);
|
||||
}
|
||||
|
||||
// Tests for assignment operator (operator=)
|
||||
TEST(BitField, Assignment)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
|
||||
test8.flag = true;
|
||||
EXPECT_EQ(test8.bits, 0x01);
|
||||
|
||||
test8.value3bit = 5;
|
||||
EXPECT_EQ(test8.bits, 0x0B); // 0001 (flag) + 1010 (5 << 1)
|
||||
|
||||
test8.value4bit = 12;
|
||||
EXPECT_EQ(test8.bits, 0xCB); // Previous + 1100 (12 << 4)
|
||||
|
||||
// Test overwriting
|
||||
test8.flag = false;
|
||||
EXPECT_EQ(test8.bits, 0xCA);
|
||||
}
|
||||
|
||||
// Tests for prefix increment operator (operator++())
|
||||
TEST(BitField, PrefixIncrement)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
test8.value3bit = 3;
|
||||
|
||||
u8 result = ++test8.value3bit;
|
||||
EXPECT_EQ(result, 4);
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 4);
|
||||
|
||||
// Test overflow handling
|
||||
test8.value3bit = 7; // Maximum for 3 bits
|
||||
result = ++test8.value3bit;
|
||||
EXPECT_EQ(result, 0); // Should wrap around
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 0);
|
||||
}
|
||||
|
||||
// Tests for postfix increment operator (operator++(int))
|
||||
TEST(BitField, PostfixIncrement)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
test8.value3bit = 3;
|
||||
|
||||
u8 result = test8.value3bit++;
|
||||
EXPECT_EQ(result, 3); // Should return old value
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 4); // Should increment
|
||||
|
||||
// Test overflow handling
|
||||
test8.value3bit = 7; // Maximum for 3 bits
|
||||
result = test8.value3bit++;
|
||||
EXPECT_EQ(result, 7); // Should return old value
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 0); // Should wrap around
|
||||
}
|
||||
|
||||
// Tests for prefix decrement operator (operator--())
|
||||
TEST(BitField, PrefixDecrement)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
test8.value3bit = 4;
|
||||
|
||||
u8 result = --test8.value3bit;
|
||||
EXPECT_EQ(result, 3);
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 3);
|
||||
|
||||
// Test underflow handling
|
||||
test8.value3bit = 0;
|
||||
result = --test8.value3bit;
|
||||
EXPECT_EQ(result, 7); // Should wrap around
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 7);
|
||||
}
|
||||
|
||||
// Tests for postfix decrement operator (operator--(int))
|
||||
TEST(BitField, PostfixDecrement)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
test8.value3bit = 4;
|
||||
|
||||
u8 result = test8.value3bit--;
|
||||
EXPECT_EQ(result, 4); // Should return old value
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 3); // Should decrement
|
||||
|
||||
// Test underflow handling
|
||||
test8.value3bit = 0;
|
||||
result = test8.value3bit--;
|
||||
EXPECT_EQ(result, 0); // Should return old value
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 7); // Should wrap around
|
||||
}
|
||||
|
||||
// Tests for compound assignment operators (+=, -=, *=, /=)
|
||||
TEST(BitField, CompoundArithmeticOperators)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
test8.value4bit = 5;
|
||||
|
||||
// Test +=
|
||||
test8.value4bit += 3;
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 8);
|
||||
|
||||
// Test -=
|
||||
test8.value4bit -= 2;
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 6);
|
||||
|
||||
// Test *=
|
||||
test8.value4bit *= 2;
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 12);
|
||||
|
||||
// Test / =
|
||||
test8.value4bit /= 3;
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 4);
|
||||
|
||||
// Test overflow with +=
|
||||
test8.value4bit = 14;
|
||||
test8.value4bit += 5; // Should overflow and wrap
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 3); // (14 + 5) & 0xF = 19 & 0xF = 3
|
||||
}
|
||||
|
||||
// Tests for compound bitwise operators (&=, |=, ^=, <<=, >>=)
|
||||
TEST(BitField, CompoundBitwiseOperators)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
test8.value4bit = 12; // 1100
|
||||
|
||||
// Test & =
|
||||
test8.value4bit &= 10; // 1100 & 1010 = 1000
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 8);
|
||||
|
||||
// Test |=
|
||||
test8.value4bit |= 3; // 1000 | 0011 = 1011
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 11);
|
||||
|
||||
// Test ^ =
|
||||
test8.value4bit ^= 5; // 1011 ^ 0101 = 1110
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 14);
|
||||
|
||||
// Test << =
|
||||
test8.value4bit = 3; // 0011
|
||||
test8.value4bit <<= 2; // 0011 << 2 = 1100
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 12);
|
||||
|
||||
// Test >> =
|
||||
test8.value4bit >>= 1; // 1100 >> 1 = 0110
|
||||
EXPECT_EQ(static_cast<u8>(test8.value4bit), 6);
|
||||
}
|
||||
|
||||
// Tests for GetValue method with different data types
|
||||
TEST(BitField, GetValue)
|
||||
{
|
||||
// Test unsigned values
|
||||
TestUnion16 test16{};
|
||||
test16.bits = 0xFFFF;
|
||||
EXPECT_EQ(test16.flag.GetValue(), true);
|
||||
EXPECT_EQ(test16.value8bit.GetValue(), 0xFF);
|
||||
EXPECT_EQ(test16.value7bit.GetValue(), 0x7F);
|
||||
|
||||
// Test signed values
|
||||
test16.bits = 0xFF00; // Set upper 8 bits
|
||||
EXPECT_EQ(test16.signed8bit.GetValue(), -1); // Should be sign-extended
|
||||
|
||||
test16.bits = 0x7F00; // Set bit pattern for +127
|
||||
EXPECT_EQ(test16.signed8bit.GetValue(), 127);
|
||||
|
||||
test16.bits = 0x8000; // Set bit pattern for -128
|
||||
EXPECT_EQ(test16.signed8bit.GetValue(), -128);
|
||||
}
|
||||
|
||||
// Tests for SetValue method
|
||||
TEST(BitField, SetValue)
|
||||
{
|
||||
TestUnion16 test16{};
|
||||
test16.bits = 0x0000;
|
||||
|
||||
test16.flag.SetValue(true);
|
||||
EXPECT_EQ(test16.bits, 0x0001);
|
||||
|
||||
test16.value8bit.SetValue(0xAB);
|
||||
EXPECT_EQ(test16.bits, 0x0157); // 0xAB << 1 | 0x0001
|
||||
|
||||
test16.value7bit.SetValue(0x55);
|
||||
EXPECT_EQ(test16.bits, 0xAB57); // 0x55 << 9 | previous
|
||||
|
||||
// Test that setting doesn't affect other fields
|
||||
test16.bits = 0xFFFF;
|
||||
test16.flag.SetValue(false);
|
||||
EXPECT_EQ(test16.bits, 0xFFFE); // Only bit 0 should be cleared
|
||||
}
|
||||
|
||||
// Tests for boolean BitFields (1-bit fields)
|
||||
TEST(BitField, BooleanBitFields)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
|
||||
// Test setting and getting boolean values
|
||||
test8.flag.SetValue(true);
|
||||
EXPECT_TRUE(test8.flag.GetValue());
|
||||
EXPECT_TRUE(static_cast<bool>(test8.flag));
|
||||
|
||||
test8.flag.SetValue(false);
|
||||
EXPECT_FALSE(test8.flag.GetValue());
|
||||
EXPECT_FALSE(static_cast<bool>(test8.flag));
|
||||
|
||||
// Test that any non-zero value in the bit position is treated as true
|
||||
test8.bits = 0x01;
|
||||
EXPECT_TRUE(test8.flag.GetValue());
|
||||
}
|
||||
|
||||
// Tests for signed BitFields with sign extension
|
||||
TEST(BitField, SignedBitFields)
|
||||
{
|
||||
TestUnion32 test32{};
|
||||
test32.bits = 0x00000000;
|
||||
|
||||
// Test positive signed values
|
||||
test32.signed16bit.SetValue(12345);
|
||||
EXPECT_EQ(test32.signed16bit.GetValue(), 12345);
|
||||
|
||||
// Test negative signed values
|
||||
test32.signed16bit.SetValue(-12345);
|
||||
EXPECT_EQ(test32.signed16bit.GetValue(), -12345);
|
||||
|
||||
// Test maximum positive value for 16-bit signed
|
||||
test32.signed16bit.SetValue(32767);
|
||||
EXPECT_EQ(test32.signed16bit.GetValue(), 32767);
|
||||
|
||||
// Test minimum negative value for 16-bit signed
|
||||
test32.signed16bit.SetValue(-32768);
|
||||
EXPECT_EQ(test32.signed16bit.GetValue(), -32768);
|
||||
|
||||
// Test sign extension with manual bit manipulation
|
||||
test32.bits = 0xFFFF0000; // Set all bits in the signed field to 1
|
||||
EXPECT_EQ(test32.signed16bit.GetValue(), -1);
|
||||
}
|
||||
|
||||
// Tests for edge cases and boundary conditions
|
||||
TEST(BitField, EdgeCases)
|
||||
{
|
||||
TestUnion64 test64{};
|
||||
test64.bits = 0x0000000000000000ULL;
|
||||
|
||||
// Test maximum values
|
||||
test64.value32bit.SetValue(0xFFFFFFFF);
|
||||
EXPECT_EQ(test64.value32bit.GetValue(), 0xFFFFFFFFu);
|
||||
|
||||
test64.value31bit.SetValue(0x7FFFFFFF);
|
||||
EXPECT_EQ(test64.value31bit.GetValue(), 0x7FFFFFFFu);
|
||||
|
||||
// Test that fields don't interfere with each other
|
||||
test64.bits = 0x0000000000000000ULL;
|
||||
test64.flag.SetValue(true);
|
||||
test64.value32bit.SetValue(0x12345678);
|
||||
test64.value31bit.SetValue(0x55555555);
|
||||
|
||||
EXPECT_TRUE(test64.flag.GetValue());
|
||||
EXPECT_EQ(test64.value32bit.GetValue(), 0x12345678u);
|
||||
EXPECT_EQ(test64.value31bit.GetValue(), 0x55555555u);
|
||||
}
|
||||
|
||||
// Tests for arithmetic operations that may cause overflow
|
||||
TEST(BitField, OverflowBehavior)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
|
||||
// Test 3-bit field (max value 7)
|
||||
test8.value3bit.SetValue(7);
|
||||
test8.value3bit += 1; // Should overflow
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 0);
|
||||
|
||||
test8.value3bit.SetValue(7);
|
||||
test8.value3bit *= 2; // 7 * 2 = 14, should wrap to 6 (14 & 0x7)
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 6);
|
||||
|
||||
// Test underflow
|
||||
test8.value3bit.SetValue(0);
|
||||
test8.value3bit -= 1; // Should underflow
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 7);
|
||||
}
|
||||
|
||||
// Tests for chaining operations
|
||||
TEST(BitField, OperatorChaining)
|
||||
{
|
||||
TestUnion8 test8{};
|
||||
test8.bits = 0x00;
|
||||
|
||||
// Test chaining assignment operators
|
||||
auto& result = test8.value3bit = 5;
|
||||
EXPECT_EQ(&result, &test8.value3bit);
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 5);
|
||||
|
||||
// Test chaining compound operators
|
||||
auto& result2 = (test8.value3bit += 2);
|
||||
EXPECT_EQ(&result2, &test8.value3bit);
|
||||
EXPECT_EQ(static_cast<u8>(test8.value3bit), 7);
|
||||
}
|
||||
|
||||
// Tests for static_assert conditions
|
||||
TEST(BitField, StaticAssertions)
|
||||
{
|
||||
// These should compile without issues due to the static_assert in BitField
|
||||
|
||||
// Boolean fields should only be 1 bit
|
||||
union TestBoolField
|
||||
{
|
||||
u8 bits;
|
||||
BitField<u8, bool, 0, 1> valid_bool; // This should compile
|
||||
};
|
||||
|
||||
// This would fail to compile:
|
||||
// BitField<u8, bool, 0, 2> invalid_bool; // static_assert should trigger
|
||||
|
||||
TestBoolField test{};
|
||||
test.bits = 0;
|
||||
test.valid_bool = true;
|
||||
EXPECT_TRUE(test.valid_bool);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline constexpr unsigned ManualCountLeadingZeros(T value)
|
||||
{
|
||||
@@ -183,4 +595,275 @@ TEST(BitUtils, ByteSwap)
|
||||
EXPECT_EQ(ByteSwap<s16>(s16(0x1234)), s16(0x3412));
|
||||
EXPECT_EQ(ByteSwap<s32>(s32(0x12345678)), s32(0x78563412));
|
||||
EXPECT_EQ(ByteSwap<s64>(s64(0x0123456789ABCDEFll)), s64(0xEFCDAB8967452301ll));
|
||||
}
|
||||
|
||||
// Alignment function tests
|
||||
TEST(Align, IsAligned)
|
||||
{
|
||||
// Test with various integer types and alignments
|
||||
EXPECT_TRUE(Common::IsAligned(0u, 4u));
|
||||
EXPECT_TRUE(Common::IsAligned(4u, 4u));
|
||||
EXPECT_TRUE(Common::IsAligned(8u, 4u));
|
||||
EXPECT_TRUE(Common::IsAligned(16u, 4u));
|
||||
EXPECT_FALSE(Common::IsAligned(1u, 4u));
|
||||
EXPECT_FALSE(Common::IsAligned(3u, 4u));
|
||||
EXPECT_FALSE(Common::IsAligned(5u, 4u));
|
||||
|
||||
// Test with pointer types
|
||||
EXPECT_TRUE(Common::IsAligned(reinterpret_cast<uintptr_t>(nullptr), 8u));
|
||||
EXPECT_TRUE(Common::IsAligned(0x1000ull, 16u));
|
||||
EXPECT_FALSE(Common::IsAligned(0x1001ull, 16u));
|
||||
|
||||
// Test with different alignments
|
||||
EXPECT_TRUE(Common::IsAligned(15u, 1u));
|
||||
EXPECT_TRUE(Common::IsAligned(16u, 8u));
|
||||
EXPECT_FALSE(Common::IsAligned(15u, 8u));
|
||||
}
|
||||
|
||||
TEST(Align, AlignUp)
|
||||
{
|
||||
// Test basic alignment up
|
||||
EXPECT_EQ(Common::AlignUp(0u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignUp(1u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignUp(3u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignUp(4u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignUp(5u, 4u), 8u);
|
||||
EXPECT_EQ(Common::AlignUp(7u, 4u), 8u);
|
||||
EXPECT_EQ(Common::AlignUp(8u, 4u), 8u);
|
||||
|
||||
// Test with larger values
|
||||
EXPECT_EQ(Common::AlignUp(1000u, 64u), 1024u);
|
||||
EXPECT_EQ(Common::AlignUp(1024u, 64u), 1024u);
|
||||
EXPECT_EQ(Common::AlignUp(1025u, 64u), 1088u);
|
||||
|
||||
// Test with different types
|
||||
EXPECT_EQ(Common::AlignUp(13ull, 8u), 16ull);
|
||||
EXPECT_EQ(Common::AlignUp(size_t(100), 16u), size_t(112));
|
||||
}
|
||||
|
||||
TEST(Align, AlignDown)
|
||||
{
|
||||
// Test basic alignment down
|
||||
EXPECT_EQ(Common::AlignDown(0u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignDown(1u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignDown(3u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignDown(4u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignDown(5u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignDown(7u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignDown(8u, 4u), 8u);
|
||||
|
||||
// Test with larger values
|
||||
EXPECT_EQ(Common::AlignDown(1000u, 64u), 960u);
|
||||
EXPECT_EQ(Common::AlignDown(1024u, 64u), 1024u);
|
||||
EXPECT_EQ(Common::AlignDown(1087u, 64u), 1024u);
|
||||
|
||||
// Test with different types
|
||||
EXPECT_EQ(Common::AlignDown(13ull, 8u), 8ull);
|
||||
EXPECT_EQ(Common::AlignDown(size_t(100), 16u), size_t(96));
|
||||
}
|
||||
|
||||
TEST(Align, IsAlignedPow2)
|
||||
{
|
||||
// Test power-of-2 alignment checks
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(0u, 4u));
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(4u, 4u));
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(8u, 4u));
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(16u, 4u));
|
||||
EXPECT_FALSE(Common::IsAlignedPow2(1u, 4u));
|
||||
EXPECT_FALSE(Common::IsAlignedPow2(3u, 4u));
|
||||
EXPECT_FALSE(Common::IsAlignedPow2(5u, 4u));
|
||||
|
||||
// Test with different power-of-2 alignments
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(32u, 16u));
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(64u, 16u));
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(48u, 16u));
|
||||
EXPECT_FALSE(Common::IsAlignedPow2(56u, 16u));
|
||||
EXPECT_FALSE(Common::IsAlignedPow2(63u, 16u));
|
||||
|
||||
// Test with larger values
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(1024u, 256u));
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(1280u, 256u));
|
||||
EXPECT_FALSE(Common::IsAlignedPow2(1100u, 256u));
|
||||
}
|
||||
|
||||
TEST(Align, AlignUpPow2)
|
||||
{
|
||||
// Test power-of-2 alignment up
|
||||
EXPECT_EQ(Common::AlignUpPow2(0u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(1u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(3u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(4u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(5u, 4u), 8u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(7u, 4u), 8u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(8u, 4u), 8u);
|
||||
|
||||
// Test with larger power-of-2 alignments
|
||||
EXPECT_EQ(Common::AlignUpPow2(1000u, 64u), 1024u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(1024u, 64u), 1024u);
|
||||
EXPECT_EQ(Common::AlignUpPow2(1025u, 64u), 1088u);
|
||||
|
||||
// Test with different types
|
||||
EXPECT_EQ(Common::AlignUpPow2(13ull, 8u), 16ull);
|
||||
EXPECT_EQ(Common::AlignUpPow2(size_t(100), 16u), size_t(112));
|
||||
}
|
||||
|
||||
TEST(Align, AlignDownPow2)
|
||||
{
|
||||
// Test power-of-2 alignment down
|
||||
EXPECT_EQ(Common::AlignDownPow2(0u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(1u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(3u, 4u), 0u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(4u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(5u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(7u, 4u), 4u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(8u, 4u), 8u);
|
||||
|
||||
// Test with larger power-of-2 alignments
|
||||
EXPECT_EQ(Common::AlignDownPow2(1000u, 64u), 960u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(1024u, 64u), 1024u);
|
||||
EXPECT_EQ(Common::AlignDownPow2(1087u, 64u), 1024u);
|
||||
|
||||
// Test with different types
|
||||
EXPECT_EQ(Common::AlignDownPow2(13ull, 8u), 8ull);
|
||||
EXPECT_EQ(Common::AlignDownPow2(size_t(100), 16u), size_t(96));
|
||||
}
|
||||
|
||||
TEST(Align, IsPow2)
|
||||
{
|
||||
// Test power-of-2 detection
|
||||
EXPECT_TRUE(Common::IsPow2(0u));
|
||||
EXPECT_TRUE(Common::IsPow2(1u));
|
||||
EXPECT_TRUE(Common::IsPow2(2u));
|
||||
EXPECT_TRUE(Common::IsPow2(4u));
|
||||
EXPECT_TRUE(Common::IsPow2(8u));
|
||||
EXPECT_TRUE(Common::IsPow2(16u));
|
||||
EXPECT_TRUE(Common::IsPow2(32u));
|
||||
EXPECT_TRUE(Common::IsPow2(64u));
|
||||
EXPECT_TRUE(Common::IsPow2(128u));
|
||||
EXPECT_TRUE(Common::IsPow2(256u));
|
||||
EXPECT_TRUE(Common::IsPow2(1024u));
|
||||
|
||||
// Test non-power-of-2 values
|
||||
EXPECT_FALSE(Common::IsPow2(3u));
|
||||
EXPECT_FALSE(Common::IsPow2(5u));
|
||||
EXPECT_FALSE(Common::IsPow2(6u));
|
||||
EXPECT_FALSE(Common::IsPow2(7u));
|
||||
EXPECT_FALSE(Common::IsPow2(9u));
|
||||
EXPECT_FALSE(Common::IsPow2(15u));
|
||||
EXPECT_FALSE(Common::IsPow2(31u));
|
||||
EXPECT_FALSE(Common::IsPow2(1000u));
|
||||
|
||||
// Test with different types
|
||||
EXPECT_TRUE(Common::IsPow2(512ull));
|
||||
EXPECT_FALSE(Common::IsPow2(513ull));
|
||||
}
|
||||
|
||||
TEST(Align, PreviousPow2)
|
||||
{
|
||||
// Test finding previous power-of-2
|
||||
EXPECT_EQ(Common::PreviousPow2(1u), 1u);
|
||||
EXPECT_EQ(Common::PreviousPow2(2u), 2u);
|
||||
EXPECT_EQ(Common::PreviousPow2(3u), 2u);
|
||||
EXPECT_EQ(Common::PreviousPow2(4u), 4u);
|
||||
EXPECT_EQ(Common::PreviousPow2(5u), 4u);
|
||||
EXPECT_EQ(Common::PreviousPow2(6u), 4u);
|
||||
EXPECT_EQ(Common::PreviousPow2(7u), 4u);
|
||||
EXPECT_EQ(Common::PreviousPow2(8u), 8u);
|
||||
EXPECT_EQ(Common::PreviousPow2(15u), 8u);
|
||||
EXPECT_EQ(Common::PreviousPow2(16u), 16u);
|
||||
EXPECT_EQ(Common::PreviousPow2(31u), 16u);
|
||||
EXPECT_EQ(Common::PreviousPow2(32u), 32u);
|
||||
EXPECT_EQ(Common::PreviousPow2(1000u), 512u);
|
||||
EXPECT_EQ(Common::PreviousPow2(1024u), 1024u);
|
||||
|
||||
// Test with different types
|
||||
EXPECT_EQ(Common::PreviousPow2(100ull), 64ull);
|
||||
EXPECT_EQ(Common::PreviousPow2(static_cast<size_t>(2047)), static_cast<size_t>(1024));
|
||||
EXPECT_EQ(Common::PreviousPow2(static_cast<size_t>(2048)), static_cast<size_t>(2048));
|
||||
}
|
||||
|
||||
TEST(Align, NextPow2)
|
||||
{
|
||||
// Test finding next power-of-2
|
||||
EXPECT_EQ(Common::NextPow2(0u), 0u);
|
||||
EXPECT_EQ(Common::NextPow2(1u), 1u);
|
||||
EXPECT_EQ(Common::NextPow2(2u), 2u);
|
||||
EXPECT_EQ(Common::NextPow2(3u), 4u);
|
||||
EXPECT_EQ(Common::NextPow2(4u), 4u);
|
||||
EXPECT_EQ(Common::NextPow2(5u), 8u);
|
||||
EXPECT_EQ(Common::NextPow2(7u), 8u);
|
||||
EXPECT_EQ(Common::NextPow2(8u), 8u);
|
||||
EXPECT_EQ(Common::NextPow2(9u), 16u);
|
||||
EXPECT_EQ(Common::NextPow2(15u), 16u);
|
||||
EXPECT_EQ(Common::NextPow2(16u), 16u);
|
||||
EXPECT_EQ(Common::NextPow2(17u), 32u);
|
||||
EXPECT_EQ(Common::NextPow2(1000u), 1024u);
|
||||
EXPECT_EQ(Common::NextPow2(1024u), 1024u);
|
||||
|
||||
// Test with different types
|
||||
EXPECT_EQ(Common::NextPow2(100ull), 128ull);
|
||||
EXPECT_EQ(Common::NextPow2(size_t(1025)), size_t(2048));
|
||||
}
|
||||
|
||||
TEST(Align, AlignedMallocAndFree)
|
||||
{
|
||||
// Test aligned memory allocation and deallocation
|
||||
void* ptr1 = Common::AlignedMalloc(128, 16);
|
||||
ASSERT_NE(ptr1, nullptr);
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(reinterpret_cast<uintptr_t>(ptr1), 16u));
|
||||
Common::AlignedFree(ptr1);
|
||||
|
||||
void* ptr2 = Common::AlignedMalloc(256, 32);
|
||||
ASSERT_NE(ptr2, nullptr);
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(reinterpret_cast<uintptr_t>(ptr2), 32u));
|
||||
Common::AlignedFree(ptr2);
|
||||
|
||||
void* ptr3 = Common::AlignedMalloc(1024, 64);
|
||||
ASSERT_NE(ptr3, nullptr);
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(reinterpret_cast<uintptr_t>(ptr3), 64u));
|
||||
Common::AlignedFree(ptr3);
|
||||
|
||||
// Test with zero size (implementation-defined behavior)
|
||||
void* ptr4 = Common::AlignedMalloc(0, 16);
|
||||
Common::AlignedFree(ptr4); // Should not crash even if ptr4 is nullptr
|
||||
|
||||
// Test AlignedFree with nullptr (should not crash)
|
||||
Common::AlignedFree(nullptr);
|
||||
}
|
||||
|
||||
TEST(Align, UniqueAlignedPtr)
|
||||
{
|
||||
// Test make_unique_aligned for arrays
|
||||
auto ptr1 = Common::make_unique_aligned<int[]>(16, 10);
|
||||
ASSERT_NE(ptr1.get(), nullptr);
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(reinterpret_cast<uintptr_t>(ptr1.get()), 16u));
|
||||
|
||||
// Test that we can write to the memory
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
ptr1[i] = i * 2;
|
||||
}
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
EXPECT_EQ(ptr1[i], i * 2);
|
||||
}
|
||||
|
||||
auto ptr2 = Common::make_unique_aligned<u8[]>(32, 100);
|
||||
ASSERT_NE(ptr2.get(), nullptr);
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(reinterpret_cast<uintptr_t>(ptr2.get()), 32u));
|
||||
|
||||
// Test make_unique_aligned_for_overwrite
|
||||
auto ptr3 = Common::make_unique_aligned_for_overwrite<u32[]>(64, 50);
|
||||
ASSERT_NE(ptr3.get(), nullptr);
|
||||
EXPECT_TRUE(Common::IsAlignedPow2(reinterpret_cast<uintptr_t>(ptr3.get()), 64u));
|
||||
|
||||
// Test that we can write to the memory
|
||||
for (u32 i = 0; i < 50; ++i)
|
||||
{
|
||||
ptr3[i] = i + 1000;
|
||||
}
|
||||
for (u32 i = 0; i < 50; ++i)
|
||||
{
|
||||
EXPECT_EQ(ptr3[i], i + 1000);
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,10 @@
|
||||
<ClCompile Include="..\..\dep\googletest\src\gtest_main.cc" />
|
||||
<ClCompile Include="bitutils_tests.cpp" />
|
||||
<ClCompile Include="file_system_tests.cpp" />
|
||||
<ClCompile Include="gsvector_tests.cpp" />
|
||||
<ClCompile Include="path_tests.cpp" />
|
||||
<ClCompile Include="rectangle_tests.cpp" />
|
||||
<ClCompile Include="sha256_tests.cpp" />
|
||||
<ClCompile Include="hash_tests.cpp" />
|
||||
<ClCompile Include="string_tests.cpp" />
|
||||
<ClCompile Include="gsvector_yuvtorgb_test.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<ClCompile Include="path_tests.cpp" />
|
||||
<ClCompile Include="string_tests.cpp" />
|
||||
<ClCompile Include="gsvector_yuvtorgb_test.cpp" />
|
||||
<ClCompile Include="sha256_tests.cpp" />
|
||||
<ClCompile Include="hash_tests.cpp" />
|
||||
<ClCompile Include="gsvector_tests.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
1276
src/common-tests/gsvector_tests.cpp
Normal file
1276
src/common-tests/gsvector_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
374
src/common-tests/hash_tests.cpp
Normal file
374
src/common-tests/hash_tests.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "common/md5_digest.h"
|
||||
#include "common/sha1_digest.h"
|
||||
#include "common/sha256_digest.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(SHA256Digest, Simple)
|
||||
{
|
||||
// https://github.com/B-Con/crypto-algorithms/blob/master/sha256_test.c
|
||||
|
||||
static constexpr const char text1[] = "abc";
|
||||
static constexpr const char text2[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
||||
static constexpr const char text3[] = "aaaaaaaaaa";
|
||||
|
||||
static constexpr SHA256Digest::Digest hash1 = {{0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40,
|
||||
0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17,
|
||||
0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}};
|
||||
static constexpr SHA256Digest::Digest hash2 = {{0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26,
|
||||
0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff,
|
||||
0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}};
|
||||
static constexpr SHA256Digest::Digest hash3 = {{0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7,
|
||||
0xe2, 0x84, 0xd7, 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97,
|
||||
0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0}};
|
||||
|
||||
ASSERT_EQ(SHA256Digest::GetDigest(text1, std::size(text1) - 1), hash1);
|
||||
ASSERT_EQ(SHA256Digest::GetDigest(text2, std::size(text2) - 1), hash2);
|
||||
|
||||
SHA256Digest ldigest;
|
||||
for (u32 i = 0; i < 100000; i++)
|
||||
ldigest.Update(text3, std::size(text3) - 1);
|
||||
|
||||
ASSERT_EQ(ldigest.Final(), hash3);
|
||||
}
|
||||
|
||||
// MD5 Digest Tests
|
||||
TEST(MD5Digest, EmptyString)
|
||||
{
|
||||
// MD5 hash of empty string: d41d8cd98f00b204e9800998ecf8427e
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}};
|
||||
|
||||
const std::string empty_string = "";
|
||||
auto result = MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(empty_string.data()), 0));
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, SingleCharacter)
|
||||
{
|
||||
// MD5 hash of "a": 0cc175b9c0f1b6a831c399e269772661
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61}};
|
||||
|
||||
const std::string test_string = "a";
|
||||
auto result =
|
||||
MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(test_string.data()), test_string.size()));
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, ABC)
|
||||
{
|
||||
// MD5 hash of "abc": 900150983cd24fb0d6963f7d28e17f72
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72}};
|
||||
|
||||
const std::string test_string = "abc";
|
||||
auto result =
|
||||
MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(test_string.data()), test_string.size()));
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, MessageDigest)
|
||||
{
|
||||
// MD5 hash of "message digest": f96b697d7cb7938d525a2f31aaf161d0
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0}};
|
||||
|
||||
const std::string test_string = "message digest";
|
||||
auto result =
|
||||
MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(test_string.data()), test_string.size()));
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, Alphabet)
|
||||
{
|
||||
// MD5 hash of "abcdefghijklmnopqrstuvwxyz": c3fcd3d76192e4007dfb496cca67e13b
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b}};
|
||||
|
||||
const std::string test_string = "abcdefghijklmnopqrstuvwxyz";
|
||||
auto result =
|
||||
MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(test_string.data()), test_string.size()));
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, AlphaNumeric)
|
||||
{
|
||||
// MD5 hash of "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": d174ab98d277d9f5a5611c2c9f419d9f
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f}};
|
||||
|
||||
const std::string test_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
auto result =
|
||||
MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(test_string.data()), test_string.size()));
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, LongString)
|
||||
{
|
||||
// MD5 hash of 1000000 'a' characters: 7707d6ae4e027c70eea2a935c2296f21
|
||||
static constexpr std::array<u8, MD5Digest::DIGEST_SIZE> expected = {
|
||||
{0x77, 0x07, 0xd6, 0xae, 0x4e, 0x02, 0x7c, 0x70, 0xee, 0xa2, 0xa9, 0x35, 0xc2, 0x29, 0x6f, 0x21}};
|
||||
|
||||
MD5Digest digest;
|
||||
const char single_char = 'a';
|
||||
|
||||
for (int i = 0; i < 1000000; i++)
|
||||
{
|
||||
digest.Update(&single_char, 1);
|
||||
}
|
||||
|
||||
std::array<u8, MD5Digest::DIGEST_SIZE> result;
|
||||
digest.Final(result);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, IncrementalUpdate)
|
||||
{
|
||||
// Test that incremental updates produce the same result as a single update
|
||||
const std::string test_string = "The quick brown fox jumps over the lazy dog";
|
||||
|
||||
// Single update
|
||||
auto result1 =
|
||||
MD5Digest::HashData(std::span<const u8>(reinterpret_cast<const u8*>(test_string.data()), test_string.size()));
|
||||
|
||||
// Incremental updates
|
||||
MD5Digest digest;
|
||||
digest.Update("The quick ", 10);
|
||||
digest.Update("brown fox ", 10);
|
||||
digest.Update("jumps over ", 11);
|
||||
digest.Update("the lazy dog", 12);
|
||||
|
||||
std::array<u8, MD5Digest::DIGEST_SIZE> result2;
|
||||
digest.Final(result2);
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, Reset)
|
||||
{
|
||||
MD5Digest digest;
|
||||
const std::string test_string = "test data";
|
||||
|
||||
// First computation
|
||||
digest.Update(test_string.data(), static_cast<u32>(test_string.size()));
|
||||
std::array<u8, MD5Digest::DIGEST_SIZE> result1;
|
||||
digest.Final(result1);
|
||||
|
||||
// Reset and compute again
|
||||
digest.Reset();
|
||||
digest.Update(test_string.data(), static_cast<u32>(test_string.size()));
|
||||
std::array<u8, MD5Digest::DIGEST_SIZE> result2;
|
||||
digest.Final(result2);
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, SpanInterface)
|
||||
{
|
||||
const std::string test_string = "test with span interface";
|
||||
std::span<const u8> test_span(reinterpret_cast<const u8*>(test_string.data()), test_string.size());
|
||||
|
||||
// Test Update with span
|
||||
MD5Digest digest;
|
||||
digest.Update(test_span);
|
||||
std::array<u8, MD5Digest::DIGEST_SIZE> result1;
|
||||
digest.Final(result1);
|
||||
|
||||
// Test HashData with span
|
||||
auto result2 = MD5Digest::HashData(test_span);
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
// SHA1 Digest Tests
|
||||
TEST(SHA1Digest, EmptyString)
|
||||
{
|
||||
// SHA1 hash of empty string: da39a3ee5e6b4b0d3255bfef95601890afd80709
|
||||
static constexpr std::array<u8, SHA1Digest::DIGEST_SIZE> expected = {{0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b,
|
||||
0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
|
||||
0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09}};
|
||||
|
||||
auto result = SHA1Digest::GetDigest("", 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, ABC)
|
||||
{
|
||||
// SHA1 hash of "abc": a9993e364706816aba3e25717850c26c9cd0d89d
|
||||
static constexpr std::array<u8, SHA1Digest::DIGEST_SIZE> expected = {{0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81,
|
||||
0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50,
|
||||
0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}};
|
||||
|
||||
const std::string test_string = "abc";
|
||||
auto result = SHA1Digest::GetDigest(test_string.data(), test_string.size());
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, ABCExtended)
|
||||
{
|
||||
// SHA1 hash of "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq": 84983e441c3bd26ebaae4aa1f95129e5e54670f1
|
||||
static constexpr std::array<u8, SHA1Digest::DIGEST_SIZE> expected = {{0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2,
|
||||
0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51,
|
||||
0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1}};
|
||||
|
||||
const std::string test_string = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
||||
auto result = SHA1Digest::GetDigest(test_string.data(), test_string.size());
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, QuickBrownFox)
|
||||
{
|
||||
// SHA1 hash of "The quick brown fox jumps over the lazy dog": 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
|
||||
static constexpr std::array<u8, SHA1Digest::DIGEST_SIZE> expected = {{0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28,
|
||||
0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76,
|
||||
0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12}};
|
||||
|
||||
const std::string test_string = "The quick brown fox jumps over the lazy dog";
|
||||
auto result = SHA1Digest::GetDigest(test_string.data(), test_string.size());
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, LongString)
|
||||
{
|
||||
// SHA1 hash of 1000000 'a' characters: 34aa973cd4c4daa4f61eeb2bdbad27316534016f
|
||||
static constexpr std::array<u8, SHA1Digest::DIGEST_SIZE> expected = {{0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda,
|
||||
0xa4, 0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad,
|
||||
0x27, 0x31, 0x65, 0x34, 0x01, 0x6f}};
|
||||
|
||||
SHA1Digest digest;
|
||||
const char single_char = 'a';
|
||||
|
||||
for (int i = 0; i < 1000000; i++)
|
||||
{
|
||||
digest.Update(&single_char, 1);
|
||||
}
|
||||
|
||||
u8 result[SHA1Digest::DIGEST_SIZE];
|
||||
digest.Final(result);
|
||||
|
||||
std::array<u8, SHA1Digest::DIGEST_SIZE> result_array;
|
||||
std::copy(std::begin(result), std::end(result), result_array.begin());
|
||||
|
||||
EXPECT_EQ(result_array, expected);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, IncrementalUpdate)
|
||||
{
|
||||
// Test that incremental updates produce the same result as a single update
|
||||
const std::string test_string = "The quick brown fox jumps over the lazy dog";
|
||||
|
||||
// Single update
|
||||
auto result1 = SHA1Digest::GetDigest(test_string.data(), test_string.size());
|
||||
|
||||
// Incremental updates
|
||||
SHA1Digest digest;
|
||||
digest.Update("The quick ", 10);
|
||||
digest.Update("brown fox ", 10);
|
||||
digest.Update("jumps over ", 11);
|
||||
digest.Update("the lazy dog", 12);
|
||||
|
||||
u8 result_raw[SHA1Digest::DIGEST_SIZE];
|
||||
digest.Final(result_raw);
|
||||
|
||||
std::array<u8, SHA1Digest::DIGEST_SIZE> result2;
|
||||
std::copy(std::begin(result_raw), std::end(result_raw), result2.begin());
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, Reset)
|
||||
{
|
||||
SHA1Digest digest;
|
||||
const std::string test_string = "test data for reset";
|
||||
|
||||
// First computation
|
||||
digest.Update(test_string.data(), test_string.size());
|
||||
u8 result1_raw[SHA1Digest::DIGEST_SIZE];
|
||||
digest.Final(result1_raw);
|
||||
|
||||
std::array<u8, SHA1Digest::DIGEST_SIZE> result1;
|
||||
std::copy(std::begin(result1_raw), std::end(result1_raw), result1.begin());
|
||||
|
||||
// Reset and compute again
|
||||
digest.Reset();
|
||||
digest.Update(test_string.data(), test_string.size());
|
||||
u8 result2_raw[SHA1Digest::DIGEST_SIZE];
|
||||
digest.Final(result2_raw);
|
||||
|
||||
std::array<u8, SHA1Digest::DIGEST_SIZE> result2;
|
||||
std::copy(std::begin(result2_raw), std::end(result2_raw), result2.begin());
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, SpanInterface)
|
||||
{
|
||||
const std::string test_string = "test with span interface";
|
||||
std::span<const u8> test_span(reinterpret_cast<const u8*>(test_string.data()), test_string.size());
|
||||
|
||||
// Test Update with span
|
||||
SHA1Digest digest;
|
||||
digest.Update(test_span);
|
||||
u8 result1_raw[SHA1Digest::DIGEST_SIZE];
|
||||
digest.Final(result1_raw);
|
||||
|
||||
std::array<u8, SHA1Digest::DIGEST_SIZE> result1;
|
||||
std::copy(std::begin(result1_raw), std::end(result1_raw), result1.begin());
|
||||
|
||||
// Test GetDigest with span
|
||||
auto result2 = SHA1Digest::GetDigest(test_span);
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, DigestToString)
|
||||
{
|
||||
// Test the DigestToString method
|
||||
const std::string test_string = "abc";
|
||||
auto digest = SHA1Digest::GetDigest(test_string.data(), test_string.size());
|
||||
|
||||
std::span<const u8, SHA1Digest::DIGEST_SIZE> digest_span(digest);
|
||||
std::string hex_string = SHA1Digest::DigestToString(digest_span);
|
||||
|
||||
// Expected: a9993e364706816aba3e25717850c26c9cd0d89d
|
||||
EXPECT_EQ(hex_string, "a9993e364706816aba3e25717850c26c9cd0d89d");
|
||||
}
|
||||
|
||||
TEST(SHA1Digest, BinaryData)
|
||||
{
|
||||
// Test with binary data containing null bytes
|
||||
const u8 binary_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
|
||||
|
||||
auto result1 = SHA1Digest::GetDigest(binary_data, sizeof(binary_data));
|
||||
|
||||
SHA1Digest digest;
|
||||
digest.Update(binary_data, sizeof(binary_data));
|
||||
u8 result2_raw[SHA1Digest::DIGEST_SIZE];
|
||||
digest.Final(result2_raw);
|
||||
|
||||
std::array<u8, SHA1Digest::DIGEST_SIZE> result2;
|
||||
std::copy(std::begin(result2_raw), std::end(result2_raw), result2.begin());
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
|
||||
TEST(MD5Digest, BinaryData)
|
||||
{
|
||||
// Test MD5 with binary data containing null bytes
|
||||
const u8 binary_data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
|
||||
|
||||
auto result1 = MD5Digest::HashData(std::span<const u8>(binary_data, sizeof(binary_data)));
|
||||
|
||||
MD5Digest digest;
|
||||
digest.Update(binary_data, sizeof(binary_data));
|
||||
std::array<u8, MD5Digest::DIGEST_SIZE> result2;
|
||||
digest.Final(result2);
|
||||
|
||||
EXPECT_EQ(result1, result2);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "common/sha256_digest.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(SHA256Digest, Simple)
|
||||
{
|
||||
// https://github.com/B-Con/crypto-algorithms/blob/master/sha256_test.c
|
||||
|
||||
static constexpr const char text1[] = "abc";
|
||||
static constexpr const char text2[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
||||
static constexpr const char text3[] = "aaaaaaaaaa";
|
||||
|
||||
static constexpr SHA256Digest::Digest hash1 = {{0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40,
|
||||
0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17,
|
||||
0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}};
|
||||
static constexpr SHA256Digest::Digest hash2 = {{0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26,
|
||||
0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff,
|
||||
0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}};
|
||||
static constexpr SHA256Digest::Digest hash3 = {{0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7,
|
||||
0xe2, 0x84, 0xd7, 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97,
|
||||
0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0}};
|
||||
|
||||
ASSERT_EQ(SHA256Digest::GetDigest(text1, std::size(text1) - 1), hash1);
|
||||
ASSERT_EQ(SHA256Digest::GetDigest(text2, std::size(text2) - 1), hash2);
|
||||
|
||||
SHA256Digest ldigest;
|
||||
for (u32 i = 0; i < 100000; i++)
|
||||
ldigest.Update(text3, std::size(text3) - 1);
|
||||
|
||||
ASSERT_EQ(ldigest.Final(), hash3);
|
||||
}
|
||||
@@ -127,3 +127,895 @@ TEST(StringUtil, CompareNoCase)
|
||||
ASSERT_EQ(StringUtil::CompareNoCase("A", "a"), 0);
|
||||
ASSERT_EQ(StringUtil::CompareNoCase("z", "Z"), 0);
|
||||
}
|
||||
|
||||
// New tests for methods not already covered
|
||||
|
||||
TEST(StringUtil, ToLowerToUpper)
|
||||
{
|
||||
// Test ToLower
|
||||
ASSERT_EQ(StringUtil::ToLower('A'), 'a');
|
||||
ASSERT_EQ(StringUtil::ToLower('Z'), 'z');
|
||||
ASSERT_EQ(StringUtil::ToLower('M'), 'm');
|
||||
ASSERT_EQ(StringUtil::ToLower('a'), 'a'); // Already lowercase
|
||||
ASSERT_EQ(StringUtil::ToLower('z'), 'z'); // Already lowercase
|
||||
ASSERT_EQ(StringUtil::ToLower('1'), '1'); // Non-alphabetic
|
||||
ASSERT_EQ(StringUtil::ToLower('!'), '!'); // Non-alphabetic
|
||||
ASSERT_EQ(StringUtil::ToLower(' '), ' '); // Space
|
||||
|
||||
// Test ToUpper
|
||||
ASSERT_EQ(StringUtil::ToUpper('a'), 'A');
|
||||
ASSERT_EQ(StringUtil::ToUpper('z'), 'Z');
|
||||
ASSERT_EQ(StringUtil::ToUpper('m'), 'M');
|
||||
ASSERT_EQ(StringUtil::ToUpper('A'), 'A'); // Already uppercase
|
||||
ASSERT_EQ(StringUtil::ToUpper('Z'), 'Z'); // Already uppercase
|
||||
ASSERT_EQ(StringUtil::ToUpper('1'), '1'); // Non-alphabetic
|
||||
ASSERT_EQ(StringUtil::ToUpper('!'), '!'); // Non-alphabetic
|
||||
ASSERT_EQ(StringUtil::ToUpper(' '), ' '); // Space
|
||||
}
|
||||
|
||||
TEST(StringUtil, WildcardMatch)
|
||||
{
|
||||
// Basic wildcard tests
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "test"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "*"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "t*"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "*t"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "te*"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "*st"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "t*t"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "?est"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "t?st"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "tes?"));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("test", "????"));
|
||||
|
||||
// Negative tests
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("test", "best"));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("test", "tests"));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("test", "???"));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("test", "?????"));
|
||||
|
||||
// Case sensitivity tests
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("Test", "test", false));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("Test", "test", true));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("TEST", "*est", false));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("TEST", "*est", true));
|
||||
|
||||
// Empty string tests
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("", ""));
|
||||
ASSERT_TRUE(StringUtil::WildcardMatch("", "*"));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("", "?"));
|
||||
ASSERT_FALSE(StringUtil::WildcardMatch("test", ""));
|
||||
}
|
||||
|
||||
TEST(StringUtil, Strlcpy)
|
||||
{
|
||||
char buffer[10];
|
||||
|
||||
// Normal copy
|
||||
std::size_t result = StringUtil::Strlcpy(buffer, "hello", sizeof(buffer));
|
||||
ASSERT_EQ(result, 5u);
|
||||
ASSERT_STREQ(buffer, "hello");
|
||||
|
||||
// Truncation test
|
||||
result = StringUtil::Strlcpy(buffer, "hello world", sizeof(buffer));
|
||||
ASSERT_EQ(result, 11u); // Should return original string length
|
||||
ASSERT_STREQ(buffer, "hello wor"); // Should be truncated and null-terminated
|
||||
|
||||
// Empty string
|
||||
result = StringUtil::Strlcpy(buffer, "", sizeof(buffer));
|
||||
ASSERT_EQ(result, 0u);
|
||||
ASSERT_STREQ(buffer, "");
|
||||
|
||||
// Buffer size 1 (only null terminator)
|
||||
result = StringUtil::Strlcpy(buffer, "test", 1);
|
||||
ASSERT_EQ(result, 4u);
|
||||
ASSERT_STREQ(buffer, "");
|
||||
|
||||
// Test with string_view
|
||||
std::string_view sv = "test string";
|
||||
result = StringUtil::Strlcpy(buffer, sv, sizeof(buffer));
|
||||
ASSERT_EQ(result, 11u);
|
||||
ASSERT_STREQ(buffer, "test stri");
|
||||
}
|
||||
|
||||
TEST(StringUtil, Strnlen)
|
||||
{
|
||||
const char* str = "hello world";
|
||||
ASSERT_EQ(StringUtil::Strnlen(str, 100), 11u);
|
||||
ASSERT_EQ(StringUtil::Strnlen(str, 5), 5u);
|
||||
ASSERT_EQ(StringUtil::Strnlen(str, 0), 0u);
|
||||
ASSERT_EQ(StringUtil::Strnlen("", 10), 0u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, Strcasecmp)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::Strcasecmp("hello", "hello"), 0);
|
||||
ASSERT_EQ(StringUtil::Strcasecmp("Hello", "hello"), 0);
|
||||
ASSERT_EQ(StringUtil::Strcasecmp("HELLO", "hello"), 0);
|
||||
ASSERT_LT(StringUtil::Strcasecmp("apple", "banana"), 0);
|
||||
ASSERT_GT(StringUtil::Strcasecmp("zebra", "apple"), 0);
|
||||
}
|
||||
|
||||
TEST(StringUtil, Strncasecmp)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::Strncasecmp("hello", "hello", 5), 0);
|
||||
ASSERT_EQ(StringUtil::Strncasecmp("Hello", "hello", 5), 0);
|
||||
ASSERT_EQ(StringUtil::Strncasecmp("hello world", "hello test", 5), 0);
|
||||
ASSERT_NE(StringUtil::Strncasecmp("hello world", "hello test", 10), 0);
|
||||
}
|
||||
|
||||
TEST(StringUtil, EqualNoCase)
|
||||
{
|
||||
ASSERT_TRUE(StringUtil::EqualNoCase("hello", "hello"));
|
||||
ASSERT_TRUE(StringUtil::EqualNoCase("Hello", "hello"));
|
||||
ASSERT_TRUE(StringUtil::EqualNoCase("HELLO", "hello"));
|
||||
ASSERT_TRUE(StringUtil::EqualNoCase("", ""));
|
||||
ASSERT_FALSE(StringUtil::EqualNoCase("hello", "world"));
|
||||
ASSERT_FALSE(StringUtil::EqualNoCase("hello", "hello world"));
|
||||
ASSERT_FALSE(StringUtil::EqualNoCase("hello world", "hello"));
|
||||
}
|
||||
|
||||
TEST(StringUtil, ContainsNoCase)
|
||||
{
|
||||
ASSERT_TRUE(StringUtil::ContainsNoCase("hello world", "world"));
|
||||
ASSERT_TRUE(StringUtil::ContainsNoCase("hello world", "WORLD"));
|
||||
ASSERT_TRUE(StringUtil::ContainsNoCase("Hello World", "lo wo"));
|
||||
ASSERT_TRUE(StringUtil::ContainsNoCase("test", "test"));
|
||||
ASSERT_TRUE(StringUtil::ContainsNoCase("test", ""));
|
||||
ASSERT_FALSE(StringUtil::ContainsNoCase("hello", "world"));
|
||||
ASSERT_FALSE(StringUtil::ContainsNoCase("test", "testing"));
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromCharsIntegral)
|
||||
{
|
||||
// Test integers
|
||||
auto result = StringUtil::FromChars<int>("123");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(*result, 123);
|
||||
|
||||
result = StringUtil::FromChars<int>("-456");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(*result, -456);
|
||||
|
||||
// Test hex
|
||||
auto hex_result = StringUtil::FromChars<int>("FF", 16);
|
||||
ASSERT_TRUE(hex_result.has_value());
|
||||
ASSERT_EQ(*hex_result, 255);
|
||||
|
||||
// Test invalid input
|
||||
auto invalid = StringUtil::FromChars<int>("abc");
|
||||
ASSERT_FALSE(invalid.has_value());
|
||||
|
||||
// Test with endptr
|
||||
std::string_view endptr;
|
||||
auto endptr_result = StringUtil::FromChars<int>("123abc", 10, &endptr);
|
||||
ASSERT_TRUE(endptr_result.has_value());
|
||||
ASSERT_EQ(*endptr_result, 123);
|
||||
ASSERT_EQ(endptr, "abc");
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromCharsWithOptionalBase)
|
||||
{
|
||||
// Test hex prefix
|
||||
auto hex = StringUtil::FromCharsWithOptionalBase<int>("0xFF");
|
||||
ASSERT_TRUE(hex.has_value());
|
||||
ASSERT_EQ(*hex, 255);
|
||||
|
||||
// Test binary prefix
|
||||
auto bin = StringUtil::FromCharsWithOptionalBase<int>("0b1010");
|
||||
ASSERT_TRUE(bin.has_value());
|
||||
ASSERT_EQ(*bin, 10);
|
||||
|
||||
// Test octal prefix
|
||||
auto oct = StringUtil::FromCharsWithOptionalBase<int>("0123");
|
||||
ASSERT_TRUE(oct.has_value());
|
||||
ASSERT_EQ(*oct, 83); // 123 in octal = 83 in decimal
|
||||
|
||||
// Test decimal (no prefix)
|
||||
auto dec = StringUtil::FromCharsWithOptionalBase<int>("123");
|
||||
ASSERT_TRUE(dec.has_value());
|
||||
ASSERT_EQ(*dec, 123);
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromCharsFloatingPoint)
|
||||
{
|
||||
auto result = StringUtil::FromChars<float>("123.45");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_FLOAT_EQ(*result, 123.45f);
|
||||
|
||||
auto double_result = StringUtil::FromChars<double>("-456.789");
|
||||
ASSERT_TRUE(double_result.has_value());
|
||||
ASSERT_DOUBLE_EQ(*double_result, -456.789);
|
||||
|
||||
// Test scientific notation
|
||||
auto sci = StringUtil::FromChars<double>("1.23e-4");
|
||||
ASSERT_TRUE(sci.has_value());
|
||||
ASSERT_DOUBLE_EQ(*sci, 0.000123);
|
||||
|
||||
// Test invalid
|
||||
auto invalid = StringUtil::FromChars<float>("abc");
|
||||
ASSERT_FALSE(invalid.has_value());
|
||||
}
|
||||
|
||||
TEST(StringUtil, FromCharsBool)
|
||||
{
|
||||
// Test true values
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("true", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("TRUE", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("yes", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("YES", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("on", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("ON", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("1", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("enabled", 10).value_or(false));
|
||||
ASSERT_TRUE(StringUtil::FromChars<bool>("ENABLED", 10).value_or(false));
|
||||
|
||||
// Test false values
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("false", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("FALSE", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("no", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("NO", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("off", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("OFF", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("0", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("disabled", 10).value_or(true));
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("DISABLED", 10).value_or(true));
|
||||
|
||||
// Test invalid
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("maybe", 10).has_value());
|
||||
ASSERT_FALSE(StringUtil::FromChars<bool>("2", 10).has_value());
|
||||
}
|
||||
|
||||
TEST(StringUtil, ToCharsIntegral)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::ToChars(123), "123");
|
||||
ASSERT_EQ(StringUtil::ToChars(-456), "-456");
|
||||
ASSERT_EQ(StringUtil::ToChars(255, 16), "ff");
|
||||
ASSERT_EQ(StringUtil::ToChars(15, 2), "1111");
|
||||
}
|
||||
|
||||
TEST(StringUtil, ToCharsFloatingPoint)
|
||||
{
|
||||
std::string result = StringUtil::ToChars(123.45f);
|
||||
ASSERT_FALSE(result.empty());
|
||||
// Just check it's a valid representation, exact format may vary
|
||||
ASSERT_NE(result.find("123"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(StringUtil, ToCharsBool)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::ToChars(true, 10), "true");
|
||||
ASSERT_EQ(StringUtil::ToChars(false, 10), "false");
|
||||
}
|
||||
|
||||
TEST(StringUtil, IsWhitespace)
|
||||
{
|
||||
ASSERT_TRUE(StringUtil::IsWhitespace(' '));
|
||||
ASSERT_TRUE(StringUtil::IsWhitespace('\t'));
|
||||
ASSERT_TRUE(StringUtil::IsWhitespace('\n'));
|
||||
ASSERT_TRUE(StringUtil::IsWhitespace('\r'));
|
||||
ASSERT_TRUE(StringUtil::IsWhitespace('\f'));
|
||||
ASSERT_TRUE(StringUtil::IsWhitespace('\v'));
|
||||
|
||||
ASSERT_FALSE(StringUtil::IsWhitespace('a'));
|
||||
ASSERT_FALSE(StringUtil::IsWhitespace('1'));
|
||||
ASSERT_FALSE(StringUtil::IsWhitespace('!'));
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeHexDigit)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('0'), 0);
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('9'), 9);
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('a'), 10);
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('f'), 15);
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('A'), 10);
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('F'), 15);
|
||||
ASSERT_EQ(StringUtil::DecodeHexDigit('g'), 0); // Invalid should return 0
|
||||
}
|
||||
|
||||
TEST(StringUtil, EncodeHex)
|
||||
{
|
||||
std::vector<u8> data = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
|
||||
std::string hex = StringUtil::EncodeHex(data.data(), data.size());
|
||||
ASSERT_EQ(hex, "0123456789abcdef");
|
||||
|
||||
// Test with span
|
||||
std::string hex_span = StringUtil::EncodeHex(std::span<const u8>(data));
|
||||
ASSERT_EQ(hex_span, "0123456789abcdef");
|
||||
|
||||
// Test empty
|
||||
std::string empty_hex = StringUtil::EncodeHex(nullptr, 0);
|
||||
ASSERT_EQ(empty_hex, "");
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeHex)
|
||||
{
|
||||
// Test buffer version
|
||||
std::vector<u8> buffer(8);
|
||||
size_t decoded = StringUtil::DecodeHex(std::span<u8>(buffer), "0123456789ABCDEF");
|
||||
ASSERT_EQ(decoded, 8u);
|
||||
ASSERT_EQ(buffer[0], 0x01u);
|
||||
ASSERT_EQ(buffer[1], 0x23u);
|
||||
ASSERT_EQ(buffer[7], 0xEFu);
|
||||
|
||||
// Test vector version
|
||||
auto result = StringUtil::DecodeHex("0123456789ABCDEF");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(result->size(), 8u);
|
||||
ASSERT_EQ((*result)[0], 0x01u);
|
||||
ASSERT_EQ((*result)[7], 0xEFu);
|
||||
|
||||
// Test invalid hex
|
||||
auto invalid = StringUtil::DecodeHex("xyz");
|
||||
ASSERT_FALSE(invalid.has_value());
|
||||
}
|
||||
|
||||
TEST(StringUtil, IsHexDigit)
|
||||
{
|
||||
ASSERT_TRUE(StringUtil::IsHexDigit('0'));
|
||||
ASSERT_TRUE(StringUtil::IsHexDigit('9'));
|
||||
ASSERT_TRUE(StringUtil::IsHexDigit('a'));
|
||||
ASSERT_TRUE(StringUtil::IsHexDigit('f'));
|
||||
ASSERT_TRUE(StringUtil::IsHexDigit('A'));
|
||||
ASSERT_TRUE(StringUtil::IsHexDigit('F'));
|
||||
|
||||
ASSERT_FALSE(StringUtil::IsHexDigit('g'));
|
||||
ASSERT_FALSE(StringUtil::IsHexDigit('G'));
|
||||
ASSERT_FALSE(StringUtil::IsHexDigit('!'));
|
||||
ASSERT_FALSE(StringUtil::IsHexDigit(' '));
|
||||
}
|
||||
|
||||
TEST(StringUtil, ParseFixedHexString)
|
||||
{
|
||||
constexpr auto result = StringUtil::ParseFixedHexString<4>("01234567");
|
||||
ASSERT_EQ(result[0], 0x01);
|
||||
ASSERT_EQ(result[1], 0x23);
|
||||
ASSERT_EQ(result[2], 0x45);
|
||||
ASSERT_EQ(result[3], 0x67);
|
||||
}
|
||||
|
||||
TEST(StringUtil, Base64Lengths)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::DecodedBase64Length(""), 0u);
|
||||
ASSERT_EQ(StringUtil::DecodedBase64Length("SGVsbG8="), 5u);
|
||||
ASSERT_EQ(StringUtil::DecodedBase64Length("SGVsbG8h"), 6u);
|
||||
ASSERT_EQ(StringUtil::DecodedBase64Length("abc"), 0u); // Invalid length
|
||||
|
||||
std::vector<u8> data = {1, 2, 3, 4, 5};
|
||||
ASSERT_EQ(StringUtil::EncodedBase64Length(std::span<const u8>(data)), 8u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, StartsWithNoCase)
|
||||
{
|
||||
ASSERT_TRUE(StringUtil::StartsWithNoCase("Hello World", "hello"));
|
||||
ASSERT_TRUE(StringUtil::StartsWithNoCase("Hello World", "HELLO"));
|
||||
ASSERT_TRUE(StringUtil::StartsWithNoCase("test", "test"));
|
||||
ASSERT_TRUE(StringUtil::StartsWithNoCase("test", ""));
|
||||
ASSERT_FALSE(StringUtil::StartsWithNoCase("Hello", "world"));
|
||||
ASSERT_FALSE(StringUtil::StartsWithNoCase("Hi", "Hello"));
|
||||
ASSERT_FALSE(StringUtil::StartsWithNoCase("", "test"));
|
||||
}
|
||||
|
||||
TEST(StringUtil, EndsWithNoCase)
|
||||
{
|
||||
ASSERT_TRUE(StringUtil::EndsWithNoCase("Hello World", "world"));
|
||||
ASSERT_TRUE(StringUtil::EndsWithNoCase("Hello World", "WORLD"));
|
||||
ASSERT_TRUE(StringUtil::EndsWithNoCase("test", "test"));
|
||||
ASSERT_TRUE(StringUtil::EndsWithNoCase("test", ""));
|
||||
ASSERT_FALSE(StringUtil::EndsWithNoCase("Hello", "world"));
|
||||
ASSERT_FALSE(StringUtil::EndsWithNoCase("Hi", "Hello"));
|
||||
ASSERT_FALSE(StringUtil::EndsWithNoCase("", "test"));
|
||||
}
|
||||
|
||||
TEST(StringUtil, StripWhitespace)
|
||||
{
|
||||
// Test string_view version
|
||||
ASSERT_EQ(StringUtil::StripWhitespace(" hello "), "hello");
|
||||
ASSERT_EQ(StringUtil::StripWhitespace("\t\n hello world \r\f"), "hello world");
|
||||
ASSERT_EQ(StringUtil::StripWhitespace(" "), "");
|
||||
ASSERT_EQ(StringUtil::StripWhitespace(""), "");
|
||||
ASSERT_EQ(StringUtil::StripWhitespace("hello"), "hello");
|
||||
ASSERT_EQ(StringUtil::StripWhitespace(" hello"), "hello");
|
||||
ASSERT_EQ(StringUtil::StripWhitespace("hello "), "hello");
|
||||
|
||||
// Test in-place version
|
||||
std::string s = " hello world ";
|
||||
StringUtil::StripWhitespace(&s);
|
||||
ASSERT_EQ(s, "hello world");
|
||||
|
||||
s = "\t\n test \r\f";
|
||||
StringUtil::StripWhitespace(&s);
|
||||
ASSERT_EQ(s, "test");
|
||||
|
||||
s = " ";
|
||||
StringUtil::StripWhitespace(&s);
|
||||
ASSERT_EQ(s, "");
|
||||
}
|
||||
|
||||
TEST(StringUtil, SplitString)
|
||||
{
|
||||
auto result = StringUtil::SplitString("a,b,c", ',');
|
||||
ASSERT_EQ(result.size(), 3u);
|
||||
ASSERT_EQ(result[0], "a");
|
||||
ASSERT_EQ(result[1], "b");
|
||||
ASSERT_EQ(result[2], "c");
|
||||
|
||||
// Test with empty parts
|
||||
result = StringUtil::SplitString("a,,c", ',', false);
|
||||
ASSERT_EQ(result.size(), 3u);
|
||||
ASSERT_EQ(result[1], "");
|
||||
|
||||
// Test skip empty
|
||||
result = StringUtil::SplitString("a,,c", ',', true);
|
||||
ASSERT_EQ(result.size(), 2u);
|
||||
ASSERT_EQ(result[0], "a");
|
||||
ASSERT_EQ(result[1], "c");
|
||||
|
||||
// Test empty string
|
||||
result = StringUtil::SplitString("", ',');
|
||||
ASSERT_EQ(result.size(), 0u);
|
||||
|
||||
// Test no delimiter
|
||||
result = StringUtil::SplitString("hello", ',');
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
ASSERT_EQ(result[0], "hello");
|
||||
}
|
||||
|
||||
TEST(StringUtil, SplitNewString)
|
||||
{
|
||||
auto result = StringUtil::SplitNewString("a,b,c", ',');
|
||||
ASSERT_EQ(result.size(), 3u);
|
||||
ASSERT_EQ(result[0], "a");
|
||||
ASSERT_EQ(result[1], "b");
|
||||
ASSERT_EQ(result[2], "c");
|
||||
|
||||
// Test empty string
|
||||
result = StringUtil::SplitNewString("", ',');
|
||||
ASSERT_EQ(result.size(), 0u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, IsInStringList)
|
||||
{
|
||||
std::vector<std::string> list = {"apple", "banana", "cherry"};
|
||||
ASSERT_TRUE(StringUtil::IsInStringList(list, "apple"));
|
||||
ASSERT_TRUE(StringUtil::IsInStringList(list, "banana"));
|
||||
ASSERT_FALSE(StringUtil::IsInStringList(list, "grape"));
|
||||
ASSERT_FALSE(StringUtil::IsInStringList(list, ""));
|
||||
|
||||
std::vector<std::string> empty_list;
|
||||
ASSERT_FALSE(StringUtil::IsInStringList(empty_list, "apple"));
|
||||
}
|
||||
|
||||
TEST(StringUtil, AddToStringList)
|
||||
{
|
||||
std::vector<std::string> list = {"apple", "banana"};
|
||||
|
||||
// Add new item
|
||||
ASSERT_TRUE(StringUtil::AddToStringList(list, "cherry"));
|
||||
ASSERT_EQ(list.size(), 3u);
|
||||
ASSERT_EQ(list[2], "cherry");
|
||||
|
||||
// Try to add existing item
|
||||
ASSERT_FALSE(StringUtil::AddToStringList(list, "apple"));
|
||||
ASSERT_EQ(list.size(), 3u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, RemoveFromStringList)
|
||||
{
|
||||
std::vector<std::string> list = {"apple", "banana", "apple", "cherry"};
|
||||
|
||||
// Remove existing item (should remove all occurrences)
|
||||
ASSERT_TRUE(StringUtil::RemoveFromStringList(list, "apple"));
|
||||
ASSERT_EQ(list.size(), 2u);
|
||||
ASSERT_EQ(list[0], "banana");
|
||||
ASSERT_EQ(list[1], "cherry");
|
||||
|
||||
// Try to remove non-existing item
|
||||
ASSERT_FALSE(StringUtil::RemoveFromStringList(list, "grape"));
|
||||
ASSERT_EQ(list.size(), 2u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, JoinString)
|
||||
{
|
||||
std::vector<std::string> list = {"apple", "banana", "cherry"};
|
||||
|
||||
// Test with char delimiter
|
||||
ASSERT_EQ(StringUtil::JoinString(list, ','), "apple,banana,cherry");
|
||||
ASSERT_EQ(StringUtil::JoinString(list, ' '), "apple banana cherry");
|
||||
|
||||
// Test with string delimiter
|
||||
ASSERT_EQ(StringUtil::JoinString(list, ", "), "apple, banana, cherry");
|
||||
ASSERT_EQ(StringUtil::JoinString(list, " and "), "apple and banana and cherry");
|
||||
|
||||
// Test with iterator range
|
||||
ASSERT_EQ(StringUtil::JoinString(list.begin(), list.end(), ','), "apple,banana,cherry");
|
||||
|
||||
// Test empty list
|
||||
std::vector<std::string> empty_list;
|
||||
ASSERT_EQ(StringUtil::JoinString(empty_list, ','), "");
|
||||
|
||||
// Test single item
|
||||
std::vector<std::string> single = {"apple"};
|
||||
ASSERT_EQ(StringUtil::JoinString(single, ','), "apple");
|
||||
}
|
||||
|
||||
TEST(StringUtil, ReplaceAll)
|
||||
{
|
||||
// Test string return version
|
||||
ASSERT_EQ(StringUtil::ReplaceAll("hello world", "world", "universe"), "hello universe");
|
||||
ASSERT_EQ(StringUtil::ReplaceAll("test test test", "test", "exam"), "exam exam exam");
|
||||
ASSERT_EQ(StringUtil::ReplaceAll("abcdef", "xyz", "123"), "abcdef"); // No match
|
||||
ASSERT_EQ(StringUtil::ReplaceAll("", "test", "exam"), "");
|
||||
ASSERT_EQ(StringUtil::ReplaceAll("test", "", "exam"), "test"); // Empty search
|
||||
|
||||
// Test in-place version
|
||||
std::string s = "hello world";
|
||||
StringUtil::ReplaceAll(&s, "world", "universe");
|
||||
ASSERT_EQ(s, "hello universe");
|
||||
|
||||
// Test char versions
|
||||
ASSERT_EQ(StringUtil::ReplaceAll("a,b,c", ',', ';'), "a;b;c");
|
||||
|
||||
s = "a,b,c";
|
||||
StringUtil::ReplaceAll(&s, ',', ';');
|
||||
ASSERT_EQ(s, "a;b;c");
|
||||
}
|
||||
|
||||
TEST(StringUtil, ParseAssignmentString)
|
||||
{
|
||||
std::string_view key, value;
|
||||
|
||||
// Test normal assignment
|
||||
ASSERT_TRUE(StringUtil::ParseAssignmentString("key=value", &key, &value));
|
||||
ASSERT_EQ(key, "key");
|
||||
ASSERT_EQ(value, "value");
|
||||
|
||||
// Test with spaces
|
||||
ASSERT_TRUE(StringUtil::ParseAssignmentString(" key = value ", &key, &value));
|
||||
ASSERT_EQ(key, "key");
|
||||
ASSERT_EQ(value, "value");
|
||||
|
||||
// Test empty value
|
||||
ASSERT_TRUE(StringUtil::ParseAssignmentString("key=", &key, &value));
|
||||
ASSERT_EQ(key, "key");
|
||||
ASSERT_EQ(value, "");
|
||||
|
||||
// Test no equals sign
|
||||
ASSERT_FALSE(StringUtil::ParseAssignmentString("keyvalue", &key, &value));
|
||||
|
||||
// Test empty string
|
||||
ASSERT_FALSE(StringUtil::ParseAssignmentString("", &key, &value));
|
||||
|
||||
// Test only equals
|
||||
ASSERT_TRUE(StringUtil::ParseAssignmentString("=", &key, &value));
|
||||
ASSERT_EQ(key, "");
|
||||
ASSERT_EQ(value, "");
|
||||
}
|
||||
|
||||
TEST(StringUtil, GetNextToken)
|
||||
{
|
||||
std::string_view caret = "a,b,c,d";
|
||||
|
||||
auto token = StringUtil::GetNextToken(caret, ',');
|
||||
ASSERT_TRUE(token.has_value());
|
||||
ASSERT_EQ(*token, "a");
|
||||
ASSERT_EQ(caret, "b,c,d");
|
||||
|
||||
token = StringUtil::GetNextToken(caret, ',');
|
||||
ASSERT_TRUE(token.has_value());
|
||||
ASSERT_EQ(*token, "b");
|
||||
ASSERT_EQ(caret, "c,d");
|
||||
|
||||
token = StringUtil::GetNextToken(caret, ',');
|
||||
ASSERT_TRUE(token.has_value());
|
||||
ASSERT_EQ(*token, "c");
|
||||
ASSERT_EQ(caret, "d");
|
||||
|
||||
token = StringUtil::GetNextToken(caret, ',');
|
||||
ASSERT_FALSE(token.has_value());
|
||||
ASSERT_EQ(caret, "d");
|
||||
}
|
||||
|
||||
TEST(StringUtil, EncodeAndAppendUTF8)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
// Test ASCII character
|
||||
StringUtil::EncodeAndAppendUTF8(s, U'A');
|
||||
ASSERT_EQ(s, "A");
|
||||
|
||||
// Test 2-byte UTF-8
|
||||
s.clear();
|
||||
StringUtil::EncodeAndAppendUTF8(s, U'ñ'); // U+00F1
|
||||
ASSERT_EQ(s.size(), 2u);
|
||||
|
||||
// Test 3-byte UTF-8
|
||||
s.clear();
|
||||
StringUtil::EncodeAndAppendUTF8(s, U'€'); // U+20AC
|
||||
ASSERT_EQ(s.size(), 3u);
|
||||
|
||||
// Test 4-byte UTF-8
|
||||
s.clear();
|
||||
StringUtil::EncodeAndAppendUTF8(s, U'💖'); // U+1F496
|
||||
ASSERT_EQ(s.size(), 4u);
|
||||
|
||||
// Test invalid character (should encode replacement character)
|
||||
s.clear();
|
||||
StringUtil::EncodeAndAppendUTF8(s, 0x110000); // Invalid
|
||||
ASSERT_EQ(s.size(), 3u); // Replacement character is 3 bytes
|
||||
|
||||
// Test buffer version
|
||||
u8 buffer[10] = {0};
|
||||
size_t written = StringUtil::EncodeAndAppendUTF8(buffer, 0, sizeof(buffer), U'A');
|
||||
ASSERT_EQ(written, 1u);
|
||||
ASSERT_EQ(buffer[0], 'A');
|
||||
|
||||
written = StringUtil::EncodeAndAppendUTF8(buffer, 1, sizeof(buffer), U'€');
|
||||
ASSERT_EQ(written, 3u);
|
||||
|
||||
// Test buffer overflow
|
||||
written = StringUtil::EncodeAndAppendUTF8(buffer, 9, sizeof(buffer), U'💖');
|
||||
ASSERT_EQ(written, 0u); // Should fail due to insufficient space
|
||||
}
|
||||
|
||||
TEST(StringUtil, GetEncodedUTF8Length)
|
||||
{
|
||||
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'A'), 1u); // ASCII
|
||||
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'ñ'), 2u); // 2-byte
|
||||
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'€'), 3u); // 3-byte
|
||||
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(U'💖'), 4u); // 4-byte
|
||||
ASSERT_EQ(StringUtil::GetEncodedUTF8Length(0x110000), 3u); // Invalid -> replacement
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeUTF8)
|
||||
{
|
||||
// Test ASCII
|
||||
char32_t ch;
|
||||
size_t len = StringUtil::DecodeUTF8("A", 0, &ch);
|
||||
ASSERT_EQ(len, 1u);
|
||||
ASSERT_EQ(ch, U'A');
|
||||
|
||||
// Test 2-byte UTF-8 (ñ = C3 B1)
|
||||
std::string utf8_2byte = "\xC3\xB1";
|
||||
len = StringUtil::DecodeUTF8(utf8_2byte, 0, &ch);
|
||||
ASSERT_EQ(len, 2u);
|
||||
ASSERT_EQ(ch, U'ñ');
|
||||
|
||||
// Test 3-byte UTF-8 (€ = E2 82 AC)
|
||||
std::string utf8_3byte = "\xE2\x82\xAC";
|
||||
len = StringUtil::DecodeUTF8(utf8_3byte, 0, &ch);
|
||||
ASSERT_EQ(len, 3u);
|
||||
ASSERT_EQ(ch, U'€');
|
||||
|
||||
// Test void* version
|
||||
len = StringUtil::DecodeUTF8(utf8_3byte.data(), utf8_3byte.size(), &ch);
|
||||
ASSERT_EQ(len, 3u);
|
||||
ASSERT_EQ(ch, U'€');
|
||||
|
||||
// Test invalid UTF-8 sequence
|
||||
std::string invalid_utf8 = "\xFF\xFE";
|
||||
len = StringUtil::DecodeUTF8(invalid_utf8.data(), invalid_utf8.size(), &ch);
|
||||
ASSERT_EQ(len, 1u);
|
||||
ASSERT_EQ(ch, StringUtil::UNICODE_REPLACEMENT_CHARACTER);
|
||||
}
|
||||
|
||||
TEST(StringUtil, EncodeAndAppendUTF16)
|
||||
{
|
||||
// Test ASCII character
|
||||
u16 buffer[10] = {0};
|
||||
size_t written = StringUtil::EncodeAndAppendUTF16(buffer, 0, 10, U'A');
|
||||
ASSERT_EQ(written, 1u);
|
||||
ASSERT_EQ(buffer[0], u16('A'));
|
||||
|
||||
// Test basic multi-byte character
|
||||
written = StringUtil::EncodeAndAppendUTF16(buffer, 1, 10, U'€'); // U+20AC
|
||||
ASSERT_EQ(written, 1u);
|
||||
ASSERT_EQ(buffer[1], u16(0x20AC));
|
||||
|
||||
// Test surrogate pair (4-byte UTF-8 character)
|
||||
written = StringUtil::EncodeAndAppendUTF16(buffer, 2, 10, U'💖'); // U+1F496
|
||||
ASSERT_EQ(written, 2u);
|
||||
// Should encode as surrogate pair: High surrogate D83D, Low surrogate DC96
|
||||
ASSERT_EQ(buffer[2], u16(0xD83D));
|
||||
ASSERT_EQ(buffer[3], u16(0xDC96));
|
||||
|
||||
// Test invalid surrogate range (should become replacement character)
|
||||
written = StringUtil::EncodeAndAppendUTF16(buffer, 4, 10, 0xD800); // In surrogate range
|
||||
ASSERT_EQ(written, 1u);
|
||||
ASSERT_EQ(buffer[4], u16(StringUtil::UNICODE_REPLACEMENT_CHARACTER));
|
||||
|
||||
// Test invalid codepoint (should become replacement character)
|
||||
written = StringUtil::EncodeAndAppendUTF16(buffer, 5, 10, 0x110000); // Invalid codepoint
|
||||
ASSERT_EQ(written, 1u);
|
||||
ASSERT_EQ(buffer[5], u16(StringUtil::UNICODE_REPLACEMENT_CHARACTER));
|
||||
|
||||
// Test buffer overflow
|
||||
written = StringUtil::EncodeAndAppendUTF16(buffer, 9, 10, U'💖'); // Needs 2 units but only 1 available
|
||||
ASSERT_EQ(written, 0u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeUTF16)
|
||||
{
|
||||
// Test ASCII character
|
||||
u16 ascii_data[] = {u16('A')};
|
||||
char32_t ch;
|
||||
size_t len = StringUtil::DecodeUTF16(ascii_data, 0, 1, &ch);
|
||||
ASSERT_EQ(len, 1u);
|
||||
ASSERT_EQ(ch, U'A');
|
||||
|
||||
// Test basic multi-byte character
|
||||
u16 euro_data[] = {u16(0x20AC)}; // €
|
||||
len = StringUtil::DecodeUTF16(euro_data, 0, 1, &ch);
|
||||
ASSERT_EQ(len, 1u);
|
||||
ASSERT_EQ(ch, U'€');
|
||||
|
||||
// Test surrogate pair
|
||||
u16 emoji_data[] = {u16(0xD83D), u16(0xDC96)}; // 💖
|
||||
len = StringUtil::DecodeUTF16(emoji_data, 0, 2, &ch);
|
||||
ASSERT_EQ(len, 2u);
|
||||
ASSERT_EQ(ch, U'💖');
|
||||
|
||||
// Test invalid high surrogate (missing low surrogate)
|
||||
u16 invalid_high[] = {u16(0xD83D)};
|
||||
len = StringUtil::DecodeUTF16(invalid_high, 0, 1, &ch);
|
||||
ASSERT_EQ(len, 1u);
|
||||
ASSERT_EQ(ch, StringUtil::UNICODE_REPLACEMENT_CHARACTER);
|
||||
|
||||
// Test invalid high surrogate followed by invalid low surrogate
|
||||
u16 invalid_surrogates[] = {u16(0xD83D), u16(0x0041)}; // High surrogate followed by 'A'
|
||||
len = StringUtil::DecodeUTF16(invalid_surrogates, 0, 2, &ch);
|
||||
ASSERT_EQ(len, 2u);
|
||||
ASSERT_EQ(ch, StringUtil::UNICODE_REPLACEMENT_CHARACTER);
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeUTF16BE)
|
||||
{
|
||||
// Test with byte-swapped data (big-endian)
|
||||
alignas(alignof(u16)) static constexpr const u8 be_data[] = {0x20, 0xAC}; // 0x20AC (€) byte-swapped
|
||||
char32_t ch;
|
||||
size_t len = StringUtil::DecodeUTF16BE(be_data, 0, sizeof(be_data), &ch);
|
||||
ASSERT_EQ(len, 1u);
|
||||
ASSERT_EQ(ch, U'€');
|
||||
|
||||
// Test surrogate pair with byte swapping
|
||||
alignas(alignof(u16)) static constexpr const u8 be_emoji_data[] = {0xD8, 0x3D, 0xDC, 0x96}; // D83D DC96 byte-swapped
|
||||
len = StringUtil::DecodeUTF16BE(be_emoji_data, 0, 2, &ch);
|
||||
ASSERT_EQ(len, 2u);
|
||||
ASSERT_EQ(ch, U'💖');
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeUTF16String)
|
||||
{
|
||||
// Test simple ASCII string
|
||||
u16 ascii_utf16[] = {u16('H'), u16('e'), u16('l'), u16('l'), u16('o')};
|
||||
std::string result = StringUtil::DecodeUTF16String(ascii_utf16, sizeof(ascii_utf16));
|
||||
ASSERT_EQ(result, "Hello");
|
||||
|
||||
// Test string with multi-byte characters
|
||||
u16 mixed_utf16[] = {u16('H'), u16('e'), u16('l'), u16('l'), u16('o'), u16(0x20AC)}; // Hello€
|
||||
result = StringUtil::DecodeUTF16String(mixed_utf16, sizeof(mixed_utf16));
|
||||
ASSERT_EQ(result.size(), 8u); // 5 ASCII + 3 bytes for €
|
||||
ASSERT_TRUE(result.starts_with("Hello"));
|
||||
|
||||
// Test with surrogate pairs
|
||||
u16 emoji_utf16[] = {u16('H'), u16('i'), u16(0xD83D), u16(0xDC96)}; // Hi💖
|
||||
result = StringUtil::DecodeUTF16String(emoji_utf16, sizeof(emoji_utf16));
|
||||
ASSERT_EQ(result.size(), 6u); // 2 ASCII + 4 bytes for 💖
|
||||
ASSERT_TRUE(result.starts_with("Hi"));
|
||||
}
|
||||
|
||||
TEST(StringUtil, DecodeUTF16BEString)
|
||||
{
|
||||
// Test with byte-swapped data
|
||||
u16 be_utf16[] = {0x4800, 0x6500}; // "He" in big-endian
|
||||
std::string result = StringUtil::DecodeUTF16BEString(be_utf16, sizeof(be_utf16));
|
||||
ASSERT_EQ(result, "He");
|
||||
|
||||
// Test with multi-byte character
|
||||
u16 be_euro[] = {0x3D20}; // € in big-endian
|
||||
result = StringUtil::DecodeUTF16BEString(be_euro, sizeof(be_euro));
|
||||
ASSERT_EQ(result.size(), 3u); // € is 3 bytes in UTF-8
|
||||
}
|
||||
|
||||
TEST(StringUtil, BytePatternSearch)
|
||||
{
|
||||
std::vector<u8> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||
|
||||
// Test exact match
|
||||
auto result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 02 03");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(result.value(), 0u);
|
||||
|
||||
// Test match in middle
|
||||
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "03 04 05");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(result.value(), 2u);
|
||||
|
||||
// Test with wildcards
|
||||
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 ?? 03");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(result.value(), 0u);
|
||||
|
||||
// Test no match
|
||||
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "FF FF FF");
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
// Test empty pattern
|
||||
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "");
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
// Test lowercase hex
|
||||
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 02 03");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(result.value(), 0u);
|
||||
|
||||
// Test mixed case
|
||||
result = StringUtil::BytePatternSearch(std::span<const u8>(data), "01 ?? 03");
|
||||
ASSERT_TRUE(result.has_value());
|
||||
ASSERT_EQ(result.value(), 0u);
|
||||
}
|
||||
|
||||
TEST(StringUtil, StrideMemCpy)
|
||||
{
|
||||
static constexpr const u8 src[] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
u8 dst[8] = {0};
|
||||
|
||||
// Test normal memcpy (same stride and copy size)
|
||||
StringUtil::StrideMemCpy(dst, 2, src, 2, 2, 4);
|
||||
ASSERT_EQ(dst[0], 1);
|
||||
ASSERT_EQ(dst[1], 2);
|
||||
ASSERT_EQ(dst[2], 3);
|
||||
ASSERT_EQ(dst[3], 4);
|
||||
|
||||
// Reset and test different strides
|
||||
memset(dst, 0, sizeof(dst));
|
||||
StringUtil::StrideMemCpy(dst, 3, src, 2, 1, 3);
|
||||
ASSERT_EQ(dst[0], 1);
|
||||
ASSERT_EQ(dst[3], 3);
|
||||
ASSERT_EQ(dst[6], 5);
|
||||
}
|
||||
|
||||
TEST(StringUtil, StrideMemCmp)
|
||||
{
|
||||
static constexpr const u8 data1[] = {1, 0, 2, 0, 3, 0};
|
||||
u8 data2[] = {1, 2, 3};
|
||||
|
||||
// Test equal comparison with different strides
|
||||
int result = StringUtil::StrideMemCmp(data1, 2, data2, 1, 1, 3);
|
||||
ASSERT_EQ(result, 0);
|
||||
|
||||
// Test unequal comparison
|
||||
data2[1] = 4;
|
||||
result = StringUtil::StrideMemCmp(data1, 2, data2, 1, 1, 3);
|
||||
ASSERT_NE(result, 0);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(StringUtil, UTF8StringToWideString)
|
||||
{
|
||||
std::wstring result = StringUtil::UTF8StringToWideString("Hello");
|
||||
ASSERT_EQ(result, L"Hello");
|
||||
|
||||
// Test with UTF-8 characters
|
||||
std::wstring utf8_result = StringUtil::UTF8StringToWideString("Héllo");
|
||||
ASSERT_FALSE(utf8_result.empty());
|
||||
|
||||
// Test bool version
|
||||
std::wstring dest;
|
||||
bool success = StringUtil::UTF8StringToWideString(dest, "Hello");
|
||||
ASSERT_TRUE(success);
|
||||
ASSERT_EQ(dest, L"Hello");
|
||||
}
|
||||
|
||||
TEST(StringUtil, WideStringToUTF8String)
|
||||
{
|
||||
std::string result = StringUtil::WideStringToUTF8String(L"Hello");
|
||||
ASSERT_EQ(result, "Hello");
|
||||
|
||||
// Test bool version
|
||||
std::string dest;
|
||||
bool success = StringUtil::WideStringToUTF8String(dest, L"Hello");
|
||||
ASSERT_TRUE(success);
|
||||
ASSERT_EQ(dest, "Hello");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1846,26 +1846,6 @@ public:
|
||||
return GSVector4i(vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vreinterpretq_s64_s32(v.v4s))));
|
||||
}
|
||||
|
||||
template<int i>
|
||||
ALWAYS_INLINE GSVector4i sra64() const
|
||||
{
|
||||
return GSVector4i(vreinterpretq_s32_s64(vshrq_n_s64(vreinterpretq_s64_s32(v4s), i)));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE GSVector4i sra64(s32 i) const
|
||||
{
|
||||
return GSVector4i(vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vdupq_n_s16(-i))));
|
||||
}
|
||||
|
||||
#ifdef CPU_ARCH_ARM64
|
||||
// not on arm32, hopefully we can do without
|
||||
ALWAYS_INLINE GSVector4i srav64(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(
|
||||
vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vnegq_s64(vreinterpretq_s64_s32(v.v4s)))));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<int i>
|
||||
ALWAYS_INLINE GSVector4i srl64() const
|
||||
{
|
||||
|
||||
@@ -1377,16 +1377,6 @@ public:
|
||||
|
||||
GSVector4i srlv64(const GSVector4i& v) const { ALL_LANES_64(ret.U64[i] = U64[i] >> v.U64[i]); }
|
||||
|
||||
template<s64 v>
|
||||
GSVector4i sra64() const
|
||||
{
|
||||
ALL_LANES_64(ret.S64[i] = S64[i] >> v);
|
||||
}
|
||||
|
||||
GSVector4i sra64(s32 v) const { ALL_LANES_64(ret.S64[i] = S64[i] >> v); }
|
||||
|
||||
GSVector4i srav64(const GSVector4i& v) const { ALL_LANES_64(ret.S64[i] = S64[i] >> v.S64[i]); }
|
||||
|
||||
GSVector4i add8(const GSVector4i& v) const { ALL_LANES_8(ret.S8[i] = S8[i] + v.S8[i]); }
|
||||
|
||||
GSVector4i add16(const GSVector4i& v) const { ALL_LANES_16(ret.S16[i] = S16[i] + v.S16[i]); }
|
||||
|
||||
@@ -1538,18 +1538,6 @@ public:
|
||||
ALWAYS_INLINE GSVector4i srlv64(const GSVector4i& v) const { return GSVector4i(_mm_srlv_epi64(m, v.m)); }
|
||||
#endif
|
||||
|
||||
template<s64 i>
|
||||
ALWAYS_INLINE GSVector4i sra64() const
|
||||
{
|
||||
return GSVector4i(_mm_srai_epi64(m, i));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE GSVector4i sra64(s32 i) const { return GSVector4i(_mm_sra_epi64(m, _mm_cvtsi32_si128(i))); }
|
||||
|
||||
#ifdef CPU_ARCH_AVX2
|
||||
ALWAYS_INLINE GSVector4i srav64(const GSVector4i& v) const { return GSVector4i(_mm_srav_epi64(m, v.m)); }
|
||||
#endif
|
||||
|
||||
ALWAYS_INLINE GSVector4i add8(const GSVector4i& v) const { return GSVector4i(_mm_add_epi8(m, v.m)); }
|
||||
ALWAYS_INLINE GSVector4i add16(const GSVector4i& v) const { return GSVector4i(_mm_add_epi16(m, v.m)); }
|
||||
ALWAYS_INLINE GSVector4i add32(const GSVector4i& v) const { return GSVector4i(_mm_add_epi32(m, v.m)); }
|
||||
@@ -2529,6 +2517,19 @@ public:
|
||||
return GSVector4(_mm_broadcastss_ps(_mm_load_ss(static_cast<const float*>(f))));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
ALWAYS_INLINE GSVector4 broadcast32() const { return GSVector4(_mm_shuffle_ps(m, m, _MM_SHUFFLE(0, 0, 0, 0))); }
|
||||
ALWAYS_INLINE static GSVector4 broadcast32(const GSVector4& v)
|
||||
{
|
||||
return GSVector4(_mm_shuffle_ps(v.m, v.m, _MM_SHUFFLE(0, 0, 0, 0)));
|
||||
}
|
||||
ALWAYS_INLINE static GSVector4 broadcast32(const void* f)
|
||||
{
|
||||
const __m128 v = _mm_load_ss(static_cast<const float*>(f));
|
||||
return GSVector4(_mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ALWAYS_INLINE static GSVector4 broadcast64(const void* d)
|
||||
|
||||
Reference in New Issue
Block a user