Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
726
lib/All/JoltPhysics/UnitTests/Core/ArrayTest.cpp
Normal file
726
lib/All/JoltPhysics/UnitTests/Core/ArrayTest.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
|
||||
TEST_SUITE("ArrayTest")
|
||||
{
|
||||
// A test class that is non-trivially copyable to test if the Array class correctly constructs/destructs/copies and moves elements.
|
||||
class NonTriv
|
||||
{
|
||||
public:
|
||||
NonTriv() : mValue(0) { ++sNumConstructors; }
|
||||
explicit NonTriv(int inValue) : mValue(inValue) { ++sNumConstructors; }
|
||||
NonTriv(const NonTriv &inValue) : mValue(inValue.mValue) { ++sNumCopyConstructors; }
|
||||
NonTriv(NonTriv &&inValue) : mValue(inValue.mValue) { inValue.mValue = 0; ++sNumMoveConstructors; }
|
||||
~NonTriv() { ++sNumDestructors; }
|
||||
|
||||
NonTriv & operator = (const NonTriv &inRHS) { mValue = inRHS.mValue; return *this; }
|
||||
|
||||
bool operator == (const NonTriv &inRHS) const { return mValue == inRHS.mValue; }
|
||||
bool operator != (const NonTriv &inRHS) const { return mValue != inRHS.mValue; }
|
||||
|
||||
int Value() const { return mValue; }
|
||||
|
||||
static void sReset() { sNumConstructors = 0; sNumCopyConstructors = 0; sNumMoveConstructors = 0; sNumDestructors = 0; }
|
||||
|
||||
static inline int sNumConstructors = 0;
|
||||
static inline int sNumCopyConstructors = 0;
|
||||
static inline int sNumMoveConstructors = 0;
|
||||
static inline int sNumDestructors = 0;
|
||||
|
||||
int mValue;
|
||||
};
|
||||
|
||||
TEST_CASE("TestConstructLength")
|
||||
{
|
||||
Array<int> arr(55);
|
||||
CHECK(arr.size() == 55);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructLengthNonTriv")
|
||||
{
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr(55);
|
||||
CHECK(arr.size() == 55);
|
||||
CHECK(NonTriv::sNumConstructors == 55);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructValue")
|
||||
{
|
||||
Array<int> arr(5, 55);
|
||||
CHECK(arr.size() == 5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(arr[i] == 55);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructValueNonTriv")
|
||||
{
|
||||
NonTriv v(55);
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr(5, v);
|
||||
CHECK(arr.size() == 5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(arr[i].Value() == 55);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 5);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructIterator")
|
||||
{
|
||||
int values[] = { 1, 2, 3 };
|
||||
|
||||
Array<int> arr(values, values + 3);
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(arr[0] == 1);
|
||||
CHECK(arr[1] == 2);
|
||||
CHECK(arr[2] == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructIteratorNonTriv")
|
||||
{
|
||||
NonTriv values[] = { NonTriv(1), NonTriv(2), NonTriv(3) };
|
||||
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr(values, values + 3);
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(arr[0].Value() == 1);
|
||||
CHECK(arr[1].Value() == 2);
|
||||
CHECK(arr[2].Value() == 3);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 3);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructInitializerList")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(arr[0] == 1);
|
||||
CHECK(arr[1] == 2);
|
||||
CHECK(arr[2] == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructInitializerListNonTriv")
|
||||
{
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr({ NonTriv(1), NonTriv(2), NonTriv(3) });
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(arr[0].Value() == 1);
|
||||
CHECK(arr[1].Value() == 2);
|
||||
CHECK(arr[2].Value() == 3);
|
||||
CHECK(NonTriv::sNumConstructors == 3); // For the initializer list
|
||||
CHECK(NonTriv::sNumCopyConstructors == 3); // Initializing the array
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 3); // For the initializer list
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructFromArray")
|
||||
{
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
Array<int> arr2(arr);
|
||||
CHECK(arr2.size() == 3);
|
||||
CHECK(arr2[0] == 1);
|
||||
CHECK(arr2[1] == 2);
|
||||
CHECK(arr2[2] == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstructFromArrayNonTriv")
|
||||
{
|
||||
Array<NonTriv> arr = { NonTriv(1), NonTriv(2), NonTriv(3) };
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr2(arr);
|
||||
CHECK(arr2.size() == 3);
|
||||
CHECK(arr2[0].Value() == 1);
|
||||
CHECK(arr2[1].Value() == 2);
|
||||
CHECK(arr2[2].Value() == 3);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 3);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestMoveFromArray")
|
||||
{
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
Array<int> arr2(std::move(arr));
|
||||
CHECK(arr2.size() == 3);
|
||||
CHECK(arr2[0] == 1);
|
||||
CHECK(arr2[1] == 2);
|
||||
CHECK(arr2[2] == 3);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestMoveFromArrayNonTriv")
|
||||
{
|
||||
Array<NonTriv> arr = { NonTriv(1), NonTriv(2), NonTriv(3) };
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr2(std::move(arr)); // This just updates the mElements pointer so should not call any constructors/destructors etc.
|
||||
CHECK(arr2.size() == 3);
|
||||
CHECK(arr2[0].Value() == 1);
|
||||
CHECK(arr2[1].Value() == 2);
|
||||
CHECK(arr2[2].Value() == 3);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestClear")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
CHECK(arr.size() == 3);
|
||||
arr.clear();
|
||||
CHECK(arr.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestClearNonTriv")
|
||||
{
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr({ NonTriv(1), NonTriv(2), NonTriv(3) });
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(NonTriv::sNumConstructors == 3); // For initializer list
|
||||
CHECK(NonTriv::sNumCopyConstructors == 3); // To move into array
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 3); // For initializer list
|
||||
NonTriv::sReset();
|
||||
arr.clear();
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestPushBack")
|
||||
{
|
||||
Array<int> arr;
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
|
||||
arr.push_back(1);
|
||||
CHECK(arr.size() == 1);
|
||||
CHECK(arr[0] == 1);
|
||||
|
||||
arr.push_back(2);
|
||||
CHECK(arr.size() == 2);
|
||||
CHECK(arr[0] == 1);
|
||||
CHECK(arr[1] == 2);
|
||||
|
||||
arr.pop_back();
|
||||
CHECK(arr.size() == 1);
|
||||
|
||||
arr.pop_back();
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("TestPushBackNonTriv")
|
||||
{
|
||||
NonTriv v1(1);
|
||||
NonTriv v2(2);
|
||||
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr;
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.push_back(v1);
|
||||
CHECK(arr.size() == 1);
|
||||
CHECK(arr[0].Value() == 1);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 1);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.push_back(v2);
|
||||
CHECK(arr.size() == 2);
|
||||
CHECK(arr[0].Value() == 1);
|
||||
CHECK(arr[1].Value() == 2);
|
||||
#ifndef JPH_USE_STD_VECTOR
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 1);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 1); // Array resizes from 1 to 2
|
||||
CHECK(NonTriv::sNumDestructors == 1); // Array resizes from 1 to 2
|
||||
#endif // JPH_USE_STD_VECTOR
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.pop_back();
|
||||
CHECK(arr.size() == 1);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 1);
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.pop_back();
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.empty());
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("TestPushBackMove")
|
||||
{
|
||||
Array<Array<int>> arr;
|
||||
Array<int> arr2 = { 1, 2, 3 };
|
||||
arr.push_back(std::move(arr2));
|
||||
CHECK(arr2.size() == 0);
|
||||
CHECK(arr[0] == Array<int>({ 1, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEmplaceBack")
|
||||
{
|
||||
struct Test
|
||||
{
|
||||
Test(int inA, int inB) : mA(inA), mB(inB) { }
|
||||
|
||||
int mA;
|
||||
int mB;
|
||||
};
|
||||
|
||||
Array<Test> arr;
|
||||
arr.emplace_back(1, 2);
|
||||
CHECK(arr.size() == 1);
|
||||
CHECK(arr[0].mA == 1);
|
||||
CHECK(arr[0].mB == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("TestReserve")
|
||||
{
|
||||
Array<int> arr;
|
||||
CHECK(arr.capacity() == 0);
|
||||
|
||||
arr.reserve(123);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 123);
|
||||
|
||||
arr.reserve(456);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 456);
|
||||
}
|
||||
|
||||
TEST_CASE("TestReserveNonTriv")
|
||||
{
|
||||
NonTriv::sReset();
|
||||
|
||||
Array<NonTriv> arr;
|
||||
CHECK(arr.capacity() == 0);
|
||||
|
||||
arr.reserve(123);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 123);
|
||||
|
||||
arr.reserve(456);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 456);
|
||||
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestResize")
|
||||
{
|
||||
Array<int> arr;
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(arr.data() == nullptr);
|
||||
|
||||
arr.resize(0);
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(arr.data() == nullptr);
|
||||
|
||||
arr.resize(123);
|
||||
CHECK(arr.size() == 123);
|
||||
CHECK(arr.capacity() == 123);
|
||||
for (int i = 0; i < 123; ++i)
|
||||
arr[i] = i;
|
||||
|
||||
arr.resize(456);
|
||||
CHECK(arr.size() == 456);
|
||||
CHECK(arr.capacity() == 456);
|
||||
for (int i = 0; i < 123; ++i)
|
||||
CHECK(arr[i] == i);
|
||||
|
||||
arr.resize(10);
|
||||
CHECK(arr.size() == 10);
|
||||
CHECK(arr.capacity() >= 10);
|
||||
}
|
||||
|
||||
TEST_CASE("TestResizeNonTriv")
|
||||
{
|
||||
NonTriv::sReset();
|
||||
Array<NonTriv> arr;
|
||||
CHECK(arr.size() == 0);
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.resize(123);
|
||||
CHECK(arr.size() == 123);
|
||||
CHECK(arr.capacity() == 123);
|
||||
CHECK(NonTriv::sNumConstructors == 123);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
for (int i = 0; i < 123; ++i)
|
||||
arr[i] = NonTriv(i);
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.resize(456);
|
||||
CHECK(arr.size() == 456);
|
||||
CHECK(arr.capacity() == 456);
|
||||
for (int i = 0; i < 123; ++i)
|
||||
CHECK(arr[i].Value() == i);
|
||||
#ifndef JPH_USE_STD_VECTOR
|
||||
CHECK(NonTriv::sNumConstructors == 456 - 123);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 123);
|
||||
CHECK(NonTriv::sNumDestructors == 123); // Switched to a new block, all old elements are destroyed after being moved
|
||||
#endif // JPH_USE_STD_VECTOR
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.resize(10);
|
||||
CHECK(arr.size() == 10);
|
||||
CHECK(arr.capacity() >= 10);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 456 - 10);
|
||||
}
|
||||
|
||||
TEST_CASE("TestResizeWithValue")
|
||||
{
|
||||
Array<int> arr;
|
||||
arr.resize(10, 55);
|
||||
CHECK(arr.size() == 10);
|
||||
CHECK(arr.capacity() == 10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
CHECK(arr[i] == 55);
|
||||
}
|
||||
|
||||
TEST_CASE("TestResizeWithValueNonTriv")
|
||||
{
|
||||
NonTriv v(55);
|
||||
Array<NonTriv> arr;
|
||||
NonTriv::sReset();
|
||||
arr.resize(10, v);
|
||||
CHECK(arr.size() == 10);
|
||||
CHECK(arr.capacity() == 10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
CHECK(arr[i].Value() == 55);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 10);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 0);
|
||||
CHECK(NonTriv::sNumDestructors == 0);
|
||||
}
|
||||
|
||||
#ifndef JPH_USE_STD_VECTOR // std::vector can choose to not shrink the array when calling shrink_to_fit so we can't test this
|
||||
TEST_CASE("TestShrinkToFit")
|
||||
{
|
||||
Array<int> arr;
|
||||
for (int i = 0; i < 5; ++i)
|
||||
arr.push_back(i);
|
||||
CHECK(arr.capacity() > 5);
|
||||
CHECK(arr.size() == 5);
|
||||
|
||||
arr.shrink_to_fit();
|
||||
CHECK(arr.capacity() == 5);
|
||||
CHECK(arr.size() == 5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(arr[i] == i);
|
||||
|
||||
arr.clear();
|
||||
CHECK(arr.capacity() == 5);
|
||||
CHECK(arr.size() == 0);
|
||||
|
||||
arr.shrink_to_fit();
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(arr.data() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("TestShrinkToFitNonTriv")
|
||||
{
|
||||
Array<NonTriv> arr;
|
||||
for (int i = 0; i < 5; ++i)
|
||||
arr.push_back(NonTriv(i));
|
||||
CHECK(arr.capacity() > 5);
|
||||
CHECK(arr.size() == 5);
|
||||
|
||||
NonTriv::sReset();
|
||||
arr.shrink_to_fit();
|
||||
CHECK(arr.capacity() == 5);
|
||||
CHECK(arr.size() == 5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(arr[i].Value() == i);
|
||||
CHECK(NonTriv::sNumConstructors == 0);
|
||||
CHECK(NonTriv::sNumCopyConstructors == 0);
|
||||
CHECK(NonTriv::sNumMoveConstructors == 5);
|
||||
CHECK(NonTriv::sNumDestructors == 5); // Switched to a new block, all old elements are destroyed after being moved
|
||||
}
|
||||
#endif // JPH_USE_STD_VECTOR
|
||||
|
||||
TEST_CASE("TestAssignIterator")
|
||||
{
|
||||
int values[] = { 1, 2, 3 };
|
||||
|
||||
Array<int> arr({ 4, 5, 6 });
|
||||
arr.assign(values, values + 3);
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(arr[0] == 1);
|
||||
CHECK(arr[1] == 2);
|
||||
CHECK(arr[2] == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestAssignInitializerList")
|
||||
{
|
||||
Array<int> arr({ 4, 5, 6 });
|
||||
arr.assign({ 1, 2, 3 });
|
||||
CHECK(arr.size() == 3);
|
||||
CHECK(arr[0] == 1);
|
||||
CHECK(arr[1] == 2);
|
||||
CHECK(arr[2] == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestSwap")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
Array<int> arr2({ 4, 5, 6 });
|
||||
arr.swap(arr2);
|
||||
CHECK(arr == Array<int>({ 4, 5, 6 }));
|
||||
CHECK(arr2 == Array<int>({ 1, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestInsertBegin")
|
||||
{
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
arr.insert(arr.begin(), 4);
|
||||
CHECK(arr == Array<int>({ 4, 1, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestInsertMid")
|
||||
{
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
arr.insert(arr.begin() + 1, 4);
|
||||
CHECK(arr == Array<int>({ 1, 4, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestInsertEnd")
|
||||
{
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
arr.insert(arr.begin() + 3, 4);
|
||||
CHECK(arr == Array<int>({ 1, 2, 3, 4 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestInsertMultipleBegin")
|
||||
{
|
||||
Array<int> values_to_insert = { 4, 5, 6, 7 };
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
arr.insert(arr.begin(), values_to_insert.begin(), values_to_insert.end());
|
||||
CHECK(arr == Array<int>({ 4, 5, 6, 7, 1, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestInsertMultipleMid")
|
||||
{
|
||||
Array<int> values_to_insert = { 4, 5, 6, 7 };
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
arr.insert(arr.begin() + 1, values_to_insert.begin(), values_to_insert.end());
|
||||
CHECK(arr == Array<int>({ 1, 4, 5, 6, 7, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestInsertMultipleEnd")
|
||||
{
|
||||
Array<int> values_to_insert = { 4, 5, 6, 7 };
|
||||
Array<int> arr = { 1, 2, 3 };
|
||||
arr.insert(arr.begin() + 3, values_to_insert.begin(), values_to_insert.end());
|
||||
CHECK(arr == Array<int>({ 1, 2, 3, 4, 5, 6, 7 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestFrontBack")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
CHECK(arr.front() == 1);
|
||||
CHECK(arr.back() == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestAssign")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
Array<int> arr2({ 4, 5, 6 });
|
||||
arr = arr2;
|
||||
CHECK(arr == Array<int>({ 4, 5, 6 }));
|
||||
Array<int> &arr3 = arr; // Avoid compiler warning
|
||||
arr = arr3;
|
||||
CHECK(arr == Array<int>({ 4, 5, 6 }));
|
||||
arr = { 7, 8, 9 };
|
||||
CHECK(arr == Array<int>({ 7, 8, 9 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestAssignMove")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
Array<int> arr2({ 4, 5, 6 });
|
||||
arr = std::move(arr2);
|
||||
CHECK(arr == Array<int>({ 4, 5, 6 }));
|
||||
CHECK(arr2.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("TestEraseBegin")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
arr.erase(arr.begin());
|
||||
CHECK(arr == Array<int>({ 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEraseMid")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
arr.erase(arr.begin() + 1);
|
||||
CHECK(arr == Array<int>({ 1, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEraseEnd")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
arr.erase(arr.begin() + 2);
|
||||
CHECK(arr == Array<int>({ 1, 2 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEraseMultipleBegin")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3, 4, 5 });
|
||||
arr.erase(arr.begin(), arr.begin() + 2);
|
||||
CHECK(arr == Array<int>({ 3, 4, 5 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEraseMultipleMid")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3, 4, 5 });
|
||||
arr.erase(arr.begin() + 2, arr.begin() + 4);
|
||||
CHECK(arr == Array<int>({ 1, 2, 5 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEraseMultipleEnd")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3, 4, 5 });
|
||||
arr.erase(arr.begin() + 3, arr.begin() + 5);
|
||||
CHECK(arr == Array<int>({ 1, 2, 3 }));
|
||||
}
|
||||
|
||||
TEST_CASE("TestEquals")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3 });
|
||||
Array<int> arr2({ 4, 5, 6 });
|
||||
CHECK(arr == arr);
|
||||
CHECK(!(arr == arr2));
|
||||
CHECK(!(arr != arr));
|
||||
CHECK(arr != arr2);
|
||||
}
|
||||
|
||||
TEST_CASE("TestReverseIterator")
|
||||
{
|
||||
Array<int> arr({ 1, 2, 3, 4, 5, 6 });
|
||||
Array<int>::reverse_iterator r = arr.rbegin();
|
||||
CHECK(*r == 6);
|
||||
|
||||
int v = *(++r);
|
||||
CHECK(v == 5);
|
||||
CHECK(*r == 5);
|
||||
|
||||
v = *(--r);
|
||||
CHECK(v == 6);
|
||||
CHECK(*r == 6);
|
||||
|
||||
v = *(r++);
|
||||
CHECK(v == 6);
|
||||
CHECK(*r == 5);
|
||||
|
||||
v = *(r--);
|
||||
CHECK(v == 5);
|
||||
CHECK(*r == 6);
|
||||
|
||||
v = *(r += 2);
|
||||
CHECK(v == 4);
|
||||
CHECK(*r == 4);
|
||||
|
||||
v = *(r -= 2);
|
||||
CHECK(v == 6);
|
||||
CHECK(*r == 6);
|
||||
|
||||
CHECK(r == arr.rbegin());
|
||||
CHECK(r != arr.rend());
|
||||
|
||||
r += 6;
|
||||
CHECK(r == arr.rend());
|
||||
|
||||
CHECK(*(arr.rbegin() + 3) == 3);
|
||||
CHECK(*(arr.rend() - 3) == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("TestConstReverseIterator")
|
||||
{
|
||||
const Array<int> arr({ 1, 2, 3, 4, 5, 6 });
|
||||
Array<int>::const_reverse_iterator r = arr.rbegin();
|
||||
CHECK(*r == 6);
|
||||
|
||||
int v = *(++r);
|
||||
CHECK(v == 5);
|
||||
CHECK(*r == 5);
|
||||
|
||||
v = *(--r);
|
||||
CHECK(v == 6);
|
||||
CHECK(*r == 6);
|
||||
|
||||
v = *(r++);
|
||||
CHECK(v == 6);
|
||||
CHECK(*r == 5);
|
||||
|
||||
v = *(r--);
|
||||
CHECK(v == 5);
|
||||
CHECK(*r == 6);
|
||||
|
||||
v = *(r += 2);
|
||||
CHECK(v == 4);
|
||||
CHECK(*r == 4);
|
||||
|
||||
v = *(r -= 2);
|
||||
CHECK(v == 6);
|
||||
CHECK(*r == 6);
|
||||
|
||||
CHECK(r == arr.rbegin());
|
||||
CHECK(r == arr.crbegin());
|
||||
CHECK(r != arr.rend());
|
||||
CHECK(r != arr.crend());
|
||||
|
||||
r += 6;
|
||||
CHECK(r == arr.rend());
|
||||
CHECK(r == arr.crend());
|
||||
|
||||
CHECK(*(arr.rbegin() + 3) == 3);
|
||||
CHECK(*(arr.rend() - 3) == 3);
|
||||
}
|
||||
}
|
||||
47
lib/All/JoltPhysics/UnitTests/Core/BinaryHeapTest.cpp
Normal file
47
lib/All/JoltPhysics/UnitTests/Core/BinaryHeapTest.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/BinaryHeap.h>
|
||||
|
||||
TEST_SUITE("BinaryHeapTest")
|
||||
{
|
||||
TEST_CASE("TestBinaryHeap")
|
||||
{
|
||||
// Add some numbers
|
||||
Array<int> array;
|
||||
array.reserve(1100);
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
array.push_back(i);
|
||||
|
||||
// Ensure we have some duplicates
|
||||
for (int i = 0; i < 1000; i += 10)
|
||||
array.push_back(i);
|
||||
|
||||
// Shuffle the array
|
||||
UnitTestRandom random(123);
|
||||
std::shuffle(array.begin(), array.end(), random);
|
||||
|
||||
// Add the numbers to the heap
|
||||
Array<int> heap;
|
||||
for (int i : array)
|
||||
{
|
||||
heap.push_back(i);
|
||||
BinaryHeapPush(heap.begin(), heap.end(), std::less<int> { });
|
||||
}
|
||||
|
||||
// Check that the heap is sorted
|
||||
int last = INT_MAX;
|
||||
int seen[1000] { 0 };
|
||||
while (!heap.empty())
|
||||
{
|
||||
BinaryHeapPop(heap.begin(), heap.end(), std::less<int> { });
|
||||
int current = heap.back();
|
||||
CHECK(++seen[current] <= (current % 10 == 0? 2 : 1));
|
||||
heap.pop_back();
|
||||
CHECK(current <= last);
|
||||
last = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
lib/All/JoltPhysics/UnitTests/Core/FPFlushDenormalsTest.cpp
Normal file
44
lib/All/JoltPhysics/UnitTests/Core/FPFlushDenormalsTest.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/FPFlushDenormals.h>
|
||||
#include <atomic>
|
||||
|
||||
#if !defined(JPH_CPU_WASM) && !defined(JPH_CPU_RISCV) && !defined(JPH_CPU_PPC) && !defined(JPH_CPU_LOONGARCH)
|
||||
|
||||
// Implemented as a global atomic so the compiler can't optimize it to a constant
|
||||
extern atomic<float> TestFltMin;
|
||||
atomic<float> TestFltMin = FLT_MIN;
|
||||
|
||||
TEST_SUITE("FlushDenormalsTests")
|
||||
{
|
||||
TEST_CASE("TestFlushDenormals")
|
||||
{
|
||||
// By default flush denormals should be off
|
||||
{
|
||||
float value = TestFltMin * 0.1f;
|
||||
CHECK(value > 0.0f);
|
||||
}
|
||||
|
||||
// Turn flush denormal on
|
||||
{
|
||||
FPFlushDenormals flush_denormals;
|
||||
|
||||
float value = TestFltMin * 0.1f;
|
||||
CHECK(value == 0.0f);
|
||||
}
|
||||
|
||||
// Check if state was properly restored
|
||||
{
|
||||
float value = TestFltMin * 0.1f;
|
||||
CHECK(value > 0.0f);
|
||||
}
|
||||
|
||||
// Update TestFltMin to prevent the compiler from optimizing away TestFltMin and replace all calculations above with 0
|
||||
TestFltMin = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // JPH_CPU_WASM
|
||||
51
lib/All/JoltPhysics/UnitTests/Core/HashCombineTest.cpp
Normal file
51
lib/All/JoltPhysics/UnitTests/Core/HashCombineTest.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/HashCombine.h>
|
||||
|
||||
TEST_SUITE("HashCombineTest")
|
||||
{
|
||||
TEST_CASE("TestHashBytes")
|
||||
{
|
||||
CHECK(HashBytes("This is a test", 14) == 2733878766136413408UL);
|
||||
}
|
||||
|
||||
TEST_CASE("TestHashString")
|
||||
{
|
||||
CHECK(HashString("This is a test") == 2733878766136413408UL);
|
||||
}
|
||||
|
||||
TEST_CASE("TestHashStruct")
|
||||
{
|
||||
const char *char_test = "This is a test";
|
||||
CHECK(Hash<const char *> { } (char_test) == 2733878766136413408UL);
|
||||
|
||||
std::string_view str_view_test = "This is a test";
|
||||
CHECK(Hash<std::string_view> { } (str_view_test) == 2733878766136413408UL);
|
||||
|
||||
String str_test = "This is a test";
|
||||
CHECK(Hash<String> { } (str_test) == 2733878766136413408UL);
|
||||
}
|
||||
|
||||
TEST_CASE("TestHashCombine")
|
||||
{
|
||||
int val1 = 0;
|
||||
uint64 val1_hash = Hash<int> { } (val1);
|
||||
int val2 = 1;
|
||||
uint64 val2_hash = Hash<int> { } (val2);
|
||||
|
||||
// Check non-commutative
|
||||
uint64 seed1 = val1_hash;
|
||||
HashCombine(seed1, val2);
|
||||
uint64 seed2 = val2_hash;
|
||||
HashCombine(seed2, val1);
|
||||
CHECK(seed1 != seed2);
|
||||
|
||||
// Check that adding a 0 changes the hash
|
||||
uint64 seed3 = val1_hash;
|
||||
HashCombine(seed3, val1);
|
||||
CHECK(seed3 != val1_hash);
|
||||
}
|
||||
}
|
||||
96
lib/All/JoltPhysics/UnitTests/Core/InsertionSortTest.cpp
Normal file
96
lib/All/JoltPhysics/UnitTests/Core/InsertionSortTest.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
|
||||
#include <Jolt/Core/InsertionSort.h>
|
||||
|
||||
TEST_SUITE("InsertionSortTest")
|
||||
{
|
||||
TEST_CASE("TestOrderedArray")
|
||||
{
|
||||
Array<int> array;
|
||||
for (int i = 0; i < 10; i++)
|
||||
array.push_back(i);
|
||||
|
||||
InsertionSort(array.begin(), array.end());
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
CHECK(array[i] == i);
|
||||
}
|
||||
|
||||
TEST_CASE("TestOrderedArrayComparator")
|
||||
{
|
||||
Array<int> array;
|
||||
for (int i = 0; i < 100; i++)
|
||||
array.push_back(i);
|
||||
|
||||
InsertionSort(array.begin(), array.end(), greater<int> {});
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
CHECK(array[i] == 99 - i);
|
||||
}
|
||||
|
||||
TEST_CASE("TestReversedArray")
|
||||
{
|
||||
Array<int> array;
|
||||
for (int i = 0; i < 10; i++)
|
||||
array.push_back(9 - i);
|
||||
|
||||
InsertionSort(array.begin(), array.end());
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
CHECK(array[i] == i);
|
||||
}
|
||||
|
||||
TEST_CASE("TestRandomArray")
|
||||
{
|
||||
UnitTestRandom random;
|
||||
|
||||
Array<UnitTestRandom::result_type> array;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
UnitTestRandom::result_type value = random();
|
||||
|
||||
// Insert value at beginning
|
||||
array.insert(array.begin(), value);
|
||||
|
||||
// Insert value at end
|
||||
array.push_back(value);
|
||||
}
|
||||
|
||||
InsertionSort(array.begin(), array.end());
|
||||
|
||||
for (Array<int>::size_type i = 0; i < array.size() - 2; i += 2)
|
||||
{
|
||||
// We inserted the same value twice so these elements should be the same
|
||||
CHECK(array[i] == array[i + 1]);
|
||||
|
||||
// The next element should be bigger or equal
|
||||
CHECK(array[i] <= array[i + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("TestEmptyArray")
|
||||
{
|
||||
Array<int> array;
|
||||
InsertionSort(array.begin(), array.end());
|
||||
CHECK(array.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Test1ElementArray")
|
||||
{
|
||||
Array<int> array { 1 };
|
||||
InsertionSort(array.begin(), array.end());
|
||||
CHECK(array[0] == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Test2ElementArray")
|
||||
{
|
||||
Array<int> array { 2, 1 };
|
||||
InsertionSort(array.begin(), array.end());
|
||||
CHECK(array[0] == 1);
|
||||
CHECK(array[1] == 2);
|
||||
}
|
||||
}
|
||||
91
lib/All/JoltPhysics/UnitTests/Core/JobSystemTest.cpp
Normal file
91
lib/All/JoltPhysics/UnitTests/Core/JobSystemTest.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/JobSystemThreadPool.h>
|
||||
|
||||
TEST_SUITE("JobSystemTest")
|
||||
{
|
||||
TEST_CASE("TestJobSystemRunJobs")
|
||||
{
|
||||
// Create job system
|
||||
const int cMaxJobs = 128;
|
||||
const int cMaxBarriers = 10;
|
||||
const int cMaxThreads = 10;
|
||||
JobSystemThreadPool system(cMaxJobs, cMaxBarriers, cMaxThreads);
|
||||
|
||||
// Create array of zeros
|
||||
atomic<uint32> values[cMaxJobs];
|
||||
for (int i = 0; i < cMaxJobs; ++i)
|
||||
values[i] = 0;
|
||||
|
||||
// Create a barrier
|
||||
JobSystem::Barrier *barrier = system.CreateBarrier();
|
||||
|
||||
// Create jobs that will increment all values
|
||||
for (int i = 0; i < cMaxJobs; ++i)
|
||||
{
|
||||
JobHandle handle = system.CreateJob("JobTest", Color::sRed, [&values, i] { values[i]++; });
|
||||
barrier->AddJob(handle);
|
||||
}
|
||||
|
||||
// Wait for the barrier to complete
|
||||
system.WaitForJobs(barrier);
|
||||
|
||||
// Destroy our barrier
|
||||
system.DestroyBarrier(barrier);
|
||||
|
||||
// Test all values are 1
|
||||
for (int i = 0; i < cMaxJobs; ++i)
|
||||
CHECK(values[i] == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("TestJobSystemRunChain")
|
||||
{
|
||||
// Create job system
|
||||
const int cMaxJobs = 128;
|
||||
const int cMaxBarriers = 10;
|
||||
JobSystemThreadPool system(cMaxJobs, cMaxBarriers);
|
||||
|
||||
// Create a barrier
|
||||
JobSystem::Barrier *barrier = system.CreateBarrier();
|
||||
|
||||
// Counter that keeps track of order in which jobs ran
|
||||
atomic<uint32> counter = 1;
|
||||
|
||||
// Create array of zeros
|
||||
atomic<uint32> values[cMaxJobs];
|
||||
for (int i = 0; i < cMaxJobs; ++i)
|
||||
values[i] = 0;
|
||||
|
||||
// Create jobs that will set sequence number
|
||||
JobHandle handles[cMaxJobs];
|
||||
for (int i = 0; i < cMaxJobs; ++i)
|
||||
{
|
||||
handles[i] = system.CreateJob("JobTestChain", Color::sRed, [&values, &counter, &handles, i] {
|
||||
// Set sequence number
|
||||
values[i] = counter++;
|
||||
|
||||
// Start previous job
|
||||
if (i > 0)
|
||||
handles[i - 1].RemoveDependency();
|
||||
}, 1);
|
||||
|
||||
barrier->AddJob(handles[i]);
|
||||
}
|
||||
|
||||
// Start the last job
|
||||
handles[cMaxJobs - 1].RemoveDependency();
|
||||
|
||||
// Wait for the barrier to complete
|
||||
system.WaitForJobs(barrier);
|
||||
|
||||
// Destroy our barrier
|
||||
system.DestroyBarrier(barrier);
|
||||
|
||||
// Test jobs were executed in reverse order
|
||||
for (int i = cMaxJobs - 1; i >= 0; --i)
|
||||
CHECK(values[i] == cMaxJobs - i);
|
||||
}
|
||||
}
|
||||
57
lib/All/JoltPhysics/UnitTests/Core/LinearCurveTest.cpp
Normal file
57
lib/All/JoltPhysics/UnitTests/Core/LinearCurveTest.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/LinearCurve.h>
|
||||
|
||||
TEST_SUITE("LinearCurveTest")
|
||||
{
|
||||
TEST_CASE("Test0PointCurve")
|
||||
{
|
||||
LinearCurve curve;
|
||||
|
||||
CHECK(curve.GetValue(1.0f) == 0.0f);
|
||||
}
|
||||
|
||||
TEST_CASE("Test1PointCurve")
|
||||
{
|
||||
LinearCurve curve;
|
||||
curve.AddPoint(1.0f, 20.0f);
|
||||
|
||||
CHECK(curve.GetValue(0.9f) == 20.0f);
|
||||
CHECK(curve.GetValue(1.0f) == 20.0f);
|
||||
CHECK(curve.GetValue(1.1f) == 20.0f);
|
||||
}
|
||||
|
||||
TEST_CASE("Test2PointCurve")
|
||||
{
|
||||
LinearCurve curve;
|
||||
curve.AddPoint(-1.0f, 40.0f);
|
||||
curve.AddPoint(-3.0f, 20.0f);
|
||||
curve.Sort();
|
||||
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(-3.1f), 20.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(-3.0f), 20.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(-2.0f), 30.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(-1.0f), 40.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(-0.9f), 40.0f);
|
||||
}
|
||||
|
||||
TEST_CASE("Test3PointCurve")
|
||||
{
|
||||
LinearCurve curve;
|
||||
curve.AddPoint(1.0f, 20.0f);
|
||||
curve.AddPoint(5.0f, 60.0f);
|
||||
curve.AddPoint(3.0f, 40.0f);
|
||||
curve.Sort();
|
||||
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(0.9f), 20.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(1.0f), 20.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(2.0f), 30.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(3.0f), 40.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(4.0f), 50.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(5.0f), 60.0f);
|
||||
CHECK_APPROX_EQUAL(curve.GetValue(5.1f), 60.0f);
|
||||
}
|
||||
}
|
||||
29
lib/All/JoltPhysics/UnitTests/Core/PreciseMathTest.cpp
Normal file
29
lib/All/JoltPhysics/UnitTests/Core/PreciseMathTest.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <atomic>
|
||||
|
||||
// Implemented as a global atomic so the compiler can't optimize it to a constant
|
||||
extern atomic<float> One, OneTenth, Ten;
|
||||
atomic<float> One = 1.0f, OneTenth = 0.1f /* Actually: 0.100000001f */, Ten = 10.0f;
|
||||
|
||||
JPH_PRECISE_MATH_ON
|
||||
|
||||
TEST_SUITE("PreciseMathTest")
|
||||
{
|
||||
TEST_CASE("CheckNoFMA")
|
||||
{
|
||||
// Should not use the FMA instruction and end up being zero
|
||||
// If FMA is active, a * b will not be rounded to 1.0f so the result will be a small positive number
|
||||
float a = OneTenth, b = Ten, c = One;
|
||||
float result = a * b - c;
|
||||
CHECK(result == 0.0f);
|
||||
|
||||
// Compiler should not optimize these variables away
|
||||
One = OneTenth = Ten = 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
JPH_PRECISE_MATH_OFF
|
||||
96
lib/All/JoltPhysics/UnitTests/Core/QuickSortTest.cpp
Normal file
96
lib/All/JoltPhysics/UnitTests/Core/QuickSortTest.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
|
||||
#include <Jolt/Core/QuickSort.h>
|
||||
|
||||
TEST_SUITE("QuickSortTest")
|
||||
{
|
||||
TEST_CASE("TestOrderedArray")
|
||||
{
|
||||
Array<int> array;
|
||||
for (int i = 0; i < 100; i++)
|
||||
array.push_back(i);
|
||||
|
||||
QuickSort(array.begin(), array.end());
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
CHECK(array[i] == i);
|
||||
}
|
||||
|
||||
TEST_CASE("TestOrderedArrayComparator")
|
||||
{
|
||||
Array<int> array;
|
||||
for (int i = 0; i < 100; i++)
|
||||
array.push_back(i);
|
||||
|
||||
QuickSort(array.begin(), array.end(), greater<int> {});
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
CHECK(array[i] == 99 - i);
|
||||
}
|
||||
|
||||
TEST_CASE("TestReversedArray")
|
||||
{
|
||||
Array<int> array;
|
||||
for (int i = 0; i < 100; i++)
|
||||
array.push_back(99 - i);
|
||||
|
||||
QuickSort(array.begin(), array.end());
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
CHECK(array[i] == i);
|
||||
}
|
||||
|
||||
TEST_CASE("TestRandomArray")
|
||||
{
|
||||
UnitTestRandom random;
|
||||
|
||||
Array<UnitTestRandom::result_type> array;
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
UnitTestRandom::result_type value = random();
|
||||
|
||||
// Insert value at beginning
|
||||
array.insert(array.begin(), value);
|
||||
|
||||
// Insert value at end
|
||||
array.push_back(value);
|
||||
}
|
||||
|
||||
QuickSort(array.begin(), array.end());
|
||||
|
||||
for (Array<int>::size_type i = 0; i < array.size() - 2; i += 2)
|
||||
{
|
||||
// We inserted the same value twice so these elements should be the same
|
||||
CHECK(array[i] == array[i + 1]);
|
||||
|
||||
// The next element should be bigger or equal
|
||||
CHECK(array[i] <= array[i + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("TestEmptyArray")
|
||||
{
|
||||
Array<int> array;
|
||||
QuickSort(array.begin(), array.end());
|
||||
CHECK(array.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Test1ElementArray")
|
||||
{
|
||||
Array<int> array { 1 };
|
||||
QuickSort(array.begin(), array.end());
|
||||
CHECK(array[0] == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Test2ElementArray")
|
||||
{
|
||||
Array<int> array { 2, 1 };
|
||||
QuickSort(array.begin(), array.end());
|
||||
CHECK(array[0] == 1);
|
||||
CHECK(array[1] == 2);
|
||||
}
|
||||
}
|
||||
215
lib/All/JoltPhysics/UnitTests/Core/STLLocalAllocatorTest.cpp
Normal file
215
lib/All/JoltPhysics/UnitTests/Core/STLLocalAllocatorTest.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
|
||||
#include <Jolt/Core/STLLocalAllocator.h>
|
||||
|
||||
TEST_SUITE("STLLocalAllocatorTest")
|
||||
{
|
||||
/// The number of elements in the local buffer
|
||||
static constexpr size_t N = 20;
|
||||
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
template <class ArrayType>
|
||||
static bool sIsLocal(ArrayType &inArray)
|
||||
{
|
||||
#ifdef JPH_USE_STD_VECTOR
|
||||
// Check that the data pointer is within the array.
|
||||
// Note that when using std::vector we cannot use get_allocator as that makes a copy of the allocator internally
|
||||
// and we've disabled the copy constructor since our allocator cannot be copied.
|
||||
const uint8 *data = reinterpret_cast<const uint8 *>(inArray.data());
|
||||
const uint8 *array = reinterpret_cast<const uint8 *>(&inArray);
|
||||
return data >= array && data < array + sizeof(inArray);
|
||||
#else
|
||||
return inArray.get_allocator().is_local(inArray.data());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class ArrayType, bool NonTrivial>
|
||||
static void sTestArray()
|
||||
{
|
||||
// Allocate so that we will run out of local memory and reallocate from heap at least once
|
||||
ArrayType arr;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
arr.push_back(i);
|
||||
CHECK(arr.size() == 64);
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
CHECK(arr[i] == i);
|
||||
#if !defined(JPH_USE_STD_VECTOR) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR)
|
||||
// We only have to move elements once we run out of the local buffer, this happens as we resize
|
||||
// from 16 to 32 elements, we'll reallocate again at 32 and 64
|
||||
if constexpr (NonTrivial)
|
||||
CHECK(arr[i].GetNonTriv() == (i < 16? 3 : (i < 32? 2 : 1)));
|
||||
#endif
|
||||
}
|
||||
CHECK(IsAligned(arr.data(), alignof(typename ArrayType::value_type)));
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
CHECK(!sIsLocal(arr));
|
||||
#endif
|
||||
|
||||
// Check that we can copy the array to another array
|
||||
ArrayType arr2;
|
||||
arr2 = arr;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
CHECK(arr2[i] == i);
|
||||
if constexpr (NonTrivial)
|
||||
CHECK(arr2[i].GetNonTriv() == -999);
|
||||
}
|
||||
CHECK(IsAligned(arr2.data(), alignof(typename ArrayType::value_type)));
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
CHECK(!sIsLocal(arr2));
|
||||
#endif
|
||||
|
||||
// Clear the array
|
||||
arr.clear();
|
||||
arr.shrink_to_fit();
|
||||
CHECK(arr.size() == 0);
|
||||
#ifndef JPH_USE_STD_VECTOR // Some implementations of std::vector ignore shrink_to_fit
|
||||
CHECK(arr.capacity() == 0);
|
||||
CHECK(arr.data() == nullptr);
|
||||
#endif
|
||||
|
||||
// Allocate so we stay within the local buffer
|
||||
for (int i = 0; i < 10; ++i)
|
||||
arr.push_back(i);
|
||||
CHECK(arr.size() == 10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
CHECK(arr[i] == i);
|
||||
#if !defined(JPH_USE_STD_VECTOR) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR)
|
||||
// We never need to move elements as they stay within the local buffer
|
||||
if constexpr (NonTrivial)
|
||||
CHECK(arr[i].GetNonTriv() == 1);
|
||||
#endif
|
||||
}
|
||||
CHECK(IsAligned(arr.data(), alignof(typename ArrayType::value_type)));
|
||||
#if !defined(JPH_USE_STD_VECTOR) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR) // Doesn't work with std::vector since it doesn't use the reallocate function and runs out of space
|
||||
CHECK(sIsLocal(arr));
|
||||
#endif
|
||||
|
||||
// Check that we can copy the array to the local buffer
|
||||
ArrayType arr3;
|
||||
arr3 = arr;
|
||||
CHECK(arr3.size() == 10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
CHECK(arr3[i] == i);
|
||||
if constexpr (NonTrivial)
|
||||
CHECK(arr3[i].GetNonTriv() == -999);
|
||||
}
|
||||
CHECK(IsAligned(arr3.data(), alignof(typename ArrayType::value_type)));
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
CHECK(sIsLocal(arr3));
|
||||
#endif
|
||||
|
||||
// Check that if we reserve the memory, that we can fully fill the array in local memory
|
||||
ArrayType arr4;
|
||||
arr4.reserve(N);
|
||||
for (int i = 0; i < int(N); ++i)
|
||||
arr4.push_back(i);
|
||||
CHECK(arr4.size() == N);
|
||||
CHECK(arr4.capacity() == N);
|
||||
for (int i = 0; i < int(N); ++i)
|
||||
{
|
||||
CHECK(arr4[i] == i);
|
||||
if constexpr (NonTrivial)
|
||||
CHECK(arr4[i].GetNonTriv() == 1);
|
||||
}
|
||||
CHECK(IsAligned(arr4.data(), alignof(typename ArrayType::value_type)));
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
CHECK(sIsLocal(arr4));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("TestAllocation")
|
||||
{
|
||||
using Allocator = STLLocalAllocator<int, N>;
|
||||
using ArrayType = Array<int, Allocator>;
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
static_assert(AllocatorHasReallocate<Allocator>::sValue);
|
||||
#endif
|
||||
|
||||
sTestArray<ArrayType, false>();
|
||||
}
|
||||
|
||||
TEST_CASE("TestAllocationAligned")
|
||||
{
|
||||
// Force the need for an aligned allocation
|
||||
struct alignas(64) Aligned
|
||||
{
|
||||
Aligned(int inValue) : mValue(inValue) { }
|
||||
operator int() const { return mValue; }
|
||||
|
||||
private:
|
||||
int mValue;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable<Aligned>());
|
||||
|
||||
using Allocator = STLLocalAllocator<Aligned, N>;
|
||||
using ArrayType = Array<Aligned, Allocator>;
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
static_assert(AllocatorHasReallocate<Allocator>::sValue);
|
||||
#endif
|
||||
|
||||
sTestArray<ArrayType, false>();
|
||||
}
|
||||
|
||||
TEST_CASE("TestAllocationNonTrivial")
|
||||
{
|
||||
// Force non trivial copy constructor
|
||||
struct NonTriv
|
||||
{
|
||||
NonTriv(int inValue) : mValue(inValue) { }
|
||||
NonTriv(const NonTriv &inRHS) : mValue(inRHS.mValue), mMakeNonTriv(-999) { }
|
||||
NonTriv(NonTriv &&inRHS) : mValue(inRHS.mValue), mMakeNonTriv(inRHS.mMakeNonTriv + 1) { }
|
||||
NonTriv & operator = (const NonTriv &inRHS) { mValue = inRHS.mValue; mMakeNonTriv = -9999; return *this; }
|
||||
operator int() const { return mValue; }
|
||||
int GetNonTriv() const { return mMakeNonTriv; }
|
||||
|
||||
private:
|
||||
int mValue;
|
||||
int mMakeNonTriv = 0;
|
||||
};
|
||||
static_assert(!std::is_trivially_copyable<NonTriv>());
|
||||
|
||||
using Allocator = STLLocalAllocator<NonTriv, N>;
|
||||
using ArrayType = Array<NonTriv, Allocator>;
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
static_assert(AllocatorHasReallocate<Allocator>::sValue);
|
||||
#endif
|
||||
|
||||
sTestArray<ArrayType, true>();
|
||||
}
|
||||
|
||||
TEST_CASE("TestAllocationAlignedNonTrivial")
|
||||
{
|
||||
// Force non trivial copy constructor
|
||||
struct alignas(64) AlNonTriv
|
||||
{
|
||||
AlNonTriv(int inValue) : mValue(inValue) { }
|
||||
AlNonTriv(const AlNonTriv &inRHS) : mValue(inRHS.mValue), mMakeNonTriv(-999) { }
|
||||
AlNonTriv(AlNonTriv &&inRHS) : mValue(inRHS.mValue), mMakeNonTriv(inRHS.mMakeNonTriv + 1) { }
|
||||
AlNonTriv & operator = (const AlNonTriv &inRHS) { mValue = inRHS.mValue; mMakeNonTriv = -9999; return *this; }
|
||||
operator int() const { return mValue; }
|
||||
int GetNonTriv() const { return mMakeNonTriv; }
|
||||
|
||||
private:
|
||||
int mValue;
|
||||
int mMakeNonTriv = 0;
|
||||
};
|
||||
static_assert(!std::is_trivially_copyable<AlNonTriv>());
|
||||
|
||||
using Allocator = STLLocalAllocator<AlNonTriv, N>;
|
||||
using ArrayType = Array<AlNonTriv, Allocator>;
|
||||
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
||||
static_assert(AllocatorHasReallocate<Allocator>::sValue);
|
||||
#endif
|
||||
|
||||
sTestArray<ArrayType, true>();
|
||||
}
|
||||
}
|
||||
46
lib/All/JoltPhysics/UnitTests/Core/ScopeExitTest.cpp
Normal file
46
lib/All/JoltPhysics/UnitTests/Core/ScopeExitTest.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/ScopeExit.h>
|
||||
|
||||
TEST_SUITE("ScopeExitTest")
|
||||
{
|
||||
TEST_CASE("TestScopeExitOrder")
|
||||
{
|
||||
int value = 0;
|
||||
{
|
||||
// Last created should be first destroyed
|
||||
JPH_SCOPE_EXIT([&value]{ CHECK(value == 1); value = 2; });
|
||||
JPH_SCOPE_EXIT([&value]{ CHECK(value == 0); value = 1; });
|
||||
CHECK(value == 0);
|
||||
}
|
||||
CHECK(value == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("TestScopeExitRelease")
|
||||
{
|
||||
int value = 0;
|
||||
{
|
||||
ScopeExit scope_exit([&value]{ value++; });
|
||||
CHECK(value == 0);
|
||||
// Don't call the exit function anymore
|
||||
scope_exit.Release();
|
||||
}
|
||||
CHECK(value == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TestScopeExitInvoke")
|
||||
{
|
||||
int value = 0;
|
||||
{
|
||||
ScopeExit scope_exit([&value]{ value++; });
|
||||
CHECK(value == 0);
|
||||
scope_exit.Invoke();
|
||||
CHECK(value == 1);
|
||||
// Should not call again on exit
|
||||
}
|
||||
CHECK(value == 1);
|
||||
}
|
||||
}
|
||||
93
lib/All/JoltPhysics/UnitTests/Core/StringToolsTest.cpp
Normal file
93
lib/All/JoltPhysics/UnitTests/Core/StringToolsTest.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
|
||||
TEST_SUITE("StringToolsTest")
|
||||
{
|
||||
TEST_CASE("TestStringFormat")
|
||||
{
|
||||
CHECK(StringFormat("Test: %d", 1234) == "Test: 1234");
|
||||
}
|
||||
|
||||
TEST_CASE("TestConvertToString")
|
||||
{
|
||||
CHECK(ConvertToString(1234) == "1234");
|
||||
CHECK(ConvertToString(-1) == "-1");
|
||||
CHECK(ConvertToString(0x7fffffffffffffffUL) == "9223372036854775807");
|
||||
}
|
||||
|
||||
TEST_CASE("StringReplace")
|
||||
{
|
||||
JPH::String value = "Hello this si si a test";
|
||||
StringReplace(value, "si", "is");
|
||||
CHECK(value == "Hello this is is a test");
|
||||
StringReplace(value, "is is", "is");
|
||||
CHECK(value == "Hello this is a test");
|
||||
StringReplace(value, "Hello", "Bye");
|
||||
CHECK(value == "Bye this is a test");
|
||||
StringReplace(value, "a test", "complete");
|
||||
CHECK(value == "Bye this is complete");
|
||||
}
|
||||
|
||||
TEST_CASE("StringToVector")
|
||||
{
|
||||
Array<JPH::String> value;
|
||||
StringToVector("", value);
|
||||
CHECK(value.empty());
|
||||
|
||||
StringToVector("a,b,c", value);
|
||||
CHECK(value[0] == "a");
|
||||
CHECK(value[1] == "b");
|
||||
CHECK(value[2] == "c");
|
||||
|
||||
StringToVector("a,.b,.c,", value, ".");
|
||||
CHECK(value[0] == "a,");
|
||||
CHECK(value[1] == "b,");
|
||||
CHECK(value[2] == "c,");
|
||||
}
|
||||
|
||||
TEST_CASE("VectorToString")
|
||||
{
|
||||
Array<JPH::String> input;
|
||||
JPH::String value;
|
||||
VectorToString(input, value);
|
||||
CHECK(value.empty());
|
||||
|
||||
input = { "a", "b", "c" };
|
||||
VectorToString(input, value);
|
||||
CHECK(value == "a,b,c");
|
||||
|
||||
VectorToString(input, value, ", ");
|
||||
CHECK(value == "a, b, c");
|
||||
}
|
||||
|
||||
TEST_CASE("ToLower")
|
||||
{
|
||||
CHECK(ToLower("123 HeLlO!") == "123 hello!");
|
||||
}
|
||||
|
||||
TEST_CASE("NibbleToBinary")
|
||||
{
|
||||
CHECK(strcmp(NibbleToBinary(0b0000), "0000") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0001), "0001") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0010), "0010") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0011), "0011") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0100), "0100") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0101), "0101") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0110), "0110") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b0111), "0111") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1000), "1000") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1001), "1001") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1010), "1010") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1011), "1011") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1100), "1100") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1101), "1101") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1110), "1110") == 0);
|
||||
CHECK(strcmp(NibbleToBinary(0b1111), "1111") == 0);
|
||||
|
||||
CHECK(strcmp(NibbleToBinary(0xfffffff0), "0000") == 0);
|
||||
}
|
||||
}
|
||||
129
lib/All/JoltPhysics/UnitTests/Core/UnorderedMapTest.cpp
Normal file
129
lib/All/JoltPhysics/UnitTests/Core/UnorderedMapTest.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
TEST_SUITE("UnorderedMapTest")
|
||||
{
|
||||
TEST_CASE("TestUnorderedMap")
|
||||
{
|
||||
UnorderedMap<int, int> map;
|
||||
map.reserve(10);
|
||||
|
||||
// Insert some entries
|
||||
CHECK(map.insert({ 1, 2 }).first->first == 1);
|
||||
CHECK(map.insert({ 3, 4 }).second);
|
||||
CHECK(!map.insert({ 3, 5 }).second);
|
||||
CHECK(map.size() == 2);
|
||||
CHECK(map.find(1)->second == 2);
|
||||
CHECK(map.find(3)->second == 4);
|
||||
CHECK(map.find(5) == map.end());
|
||||
|
||||
// Use operator []
|
||||
map[5] = 6;
|
||||
CHECK(map.size() == 3);
|
||||
CHECK(map.find(5)->second == 6);
|
||||
map[5] = 7;
|
||||
CHECK(map.size() == 3);
|
||||
CHECK(map.find(5)->second == 7);
|
||||
|
||||
// Validate all elements are visited by a visitor
|
||||
int count = 0;
|
||||
bool visited[10] = { false };
|
||||
for (UnorderedMap<int, int>::const_iterator i = map.begin(); i != map.end(); ++i)
|
||||
{
|
||||
visited[i->first] = true;
|
||||
++count;
|
||||
}
|
||||
CHECK(count == 3);
|
||||
CHECK(visited[1]);
|
||||
CHECK(visited[3]);
|
||||
CHECK(visited[5]);
|
||||
for (UnorderedMap<int, int>::iterator i = map.begin(); i != map.end(); ++i)
|
||||
{
|
||||
visited[i->first] = false;
|
||||
--count;
|
||||
}
|
||||
CHECK(count == 0);
|
||||
CHECK(!visited[1]);
|
||||
CHECK(!visited[3]);
|
||||
CHECK(!visited[5]);
|
||||
|
||||
// Copy the map
|
||||
UnorderedMap<int, int> map2;
|
||||
map2 = map;
|
||||
CHECK(map2.find(1)->second == 2);
|
||||
CHECK(map2.find(3)->second == 4);
|
||||
CHECK(map2.find(5)->second == 7);
|
||||
CHECK(map2.find(7) == map2.end());
|
||||
|
||||
// Try emplace
|
||||
map.try_emplace(7, 8);
|
||||
CHECK(map.size() == 4);
|
||||
CHECK(map.find(7)->second == 8);
|
||||
|
||||
// Swap
|
||||
UnorderedMap<int, int> map3;
|
||||
map3.swap(map);
|
||||
CHECK(map3.find(1)->second == 2);
|
||||
CHECK(map3.find(3)->second == 4);
|
||||
CHECK(map3.find(5)->second == 7);
|
||||
CHECK(map3.find(7)->second == 8);
|
||||
CHECK(map3.find(9) == map3.end());
|
||||
CHECK(map.empty());
|
||||
|
||||
// Move construct
|
||||
UnorderedMap<int, int> map4(std::move(map3));
|
||||
CHECK(map4.find(1)->second == 2);
|
||||
CHECK(map4.find(3)->second == 4);
|
||||
CHECK(map4.find(5)->second == 7);
|
||||
CHECK(map4.find(7)->second == 8);
|
||||
CHECK(map4.find(9) == map4.end());
|
||||
CHECK(map3.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("TestUnorderedMapGrow")
|
||||
{
|
||||
UnorderedMap<int, int> map;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
CHECK(map.try_emplace(i, ~i).second);
|
||||
|
||||
CHECK(map.size() == 10000);
|
||||
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
CHECK(map.find(i)->second == ~i);
|
||||
|
||||
CHECK(map.find(10001) == map.end());
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(map.erase(i) == 1);
|
||||
|
||||
CHECK(map.size() == 5000);
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(map.find(i) == map.end());
|
||||
|
||||
for (int i = 5000; i < 10000; ++i)
|
||||
CHECK(map.find(i)->second == ~i);
|
||||
|
||||
CHECK(map.find(10001) == map.end());
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(map.try_emplace(i, i + 1).second);
|
||||
|
||||
CHECK(!map.try_emplace(0, 0).second);
|
||||
|
||||
CHECK(map.size() == 10000);
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(map.find(i)->second == i + 1);
|
||||
|
||||
for (int i = 5000; i < 10000; ++i)
|
||||
CHECK(map.find(i)->second == ~i);
|
||||
|
||||
CHECK(map.find(10001) == map.end());
|
||||
}
|
||||
}
|
||||
333
lib/All/JoltPhysics/UnitTests/Core/UnorderedSetTest.cpp
Normal file
333
lib/All/JoltPhysics/UnitTests/Core/UnorderedSetTest.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "UnitTestFramework.h"
|
||||
|
||||
#include <Jolt/Core/UnorderedSet.h>
|
||||
|
||||
TEST_SUITE("UnorderedSetTest")
|
||||
{
|
||||
TEST_CASE("TestUnorderedSet")
|
||||
{
|
||||
UnorderedSet<int> set;
|
||||
CHECK(set.bucket_count() == 0);
|
||||
set.reserve(10);
|
||||
CHECK(set.bucket_count() == 16);
|
||||
|
||||
// Check system limits
|
||||
CHECK(set.max_bucket_count() == 0x80000000);
|
||||
CHECK(set.max_size() == uint64(0x80000000) * 7 / 8);
|
||||
|
||||
// Insert some entries
|
||||
CHECK(*set.insert(1).first == 1);
|
||||
CHECK(set.insert(3).second);
|
||||
CHECK(!set.insert(3).second);
|
||||
CHECK(set.size() == 2);
|
||||
CHECK(*set.find(1) == 1);
|
||||
CHECK(*set.find(3) == 3);
|
||||
CHECK(set.find(5) == set.cend());
|
||||
|
||||
// Validate all elements are visited by a visitor
|
||||
int count = 0;
|
||||
bool visited[10] = { false };
|
||||
for (UnorderedSet<int>::const_iterator i = set.begin(); i != set.end(); ++i)
|
||||
{
|
||||
visited[*i] = true;
|
||||
++count;
|
||||
}
|
||||
CHECK(count == 2);
|
||||
CHECK(visited[1]);
|
||||
CHECK(visited[3]);
|
||||
for (UnorderedSet<int>::iterator i = set.begin(); i != set.end(); ++i)
|
||||
{
|
||||
visited[*i] = false;
|
||||
--count;
|
||||
}
|
||||
CHECK(count == 0);
|
||||
CHECK(!visited[1]);
|
||||
CHECK(!visited[3]);
|
||||
|
||||
// Copy the set
|
||||
UnorderedSet<int> set2;
|
||||
set2 = set;
|
||||
CHECK(*set2.find(1) == 1);
|
||||
CHECK(*set2.find(3) == 3);
|
||||
CHECK(set2.find(5) == set2.cend());
|
||||
|
||||
// Swap
|
||||
UnorderedSet<int> set3;
|
||||
set3.swap(set);
|
||||
CHECK(*set3.find(1) == 1);
|
||||
CHECK(*set3.find(3) == 3);
|
||||
CHECK(set3.find(5) == set3.end());
|
||||
CHECK(set.empty());
|
||||
|
||||
// Move construct
|
||||
UnorderedSet<int> set4(std::move(set3));
|
||||
CHECK(*set4.find(1) == 1);
|
||||
CHECK(*set4.find(3) == 3);
|
||||
CHECK(set4.find(5) == set4.end());
|
||||
CHECK(set3.empty());
|
||||
|
||||
// Move assign
|
||||
UnorderedSet<int> set5;
|
||||
set5.insert(999);
|
||||
CHECK(*set5.find(999) == 999);
|
||||
set5 = std::move(set4);
|
||||
CHECK(set5.find(999) == set5.end());
|
||||
CHECK(*set5.find(1) == 1);
|
||||
CHECK(*set5.find(3) == 3);
|
||||
CHECK(set4.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("TestUnorderedSetGrow")
|
||||
{
|
||||
UnorderedSet<int> set;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
CHECK(set.insert(i).second);
|
||||
|
||||
CHECK(set.size() == 10000);
|
||||
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
CHECK(set.find(10001) == set.cend());
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(set.erase(i) == 1);
|
||||
|
||||
CHECK(set.size() == 5000);
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(set.find(i) == set.end());
|
||||
|
||||
for (int i = 5000; i < 10000; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
CHECK(set.find(10001) == set.cend());
|
||||
|
||||
for (int i = 0; i < 5000; ++i)
|
||||
CHECK(set.insert(i).second);
|
||||
|
||||
CHECK(!set.insert(0).second);
|
||||
|
||||
CHECK(set.size() == 10000);
|
||||
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
CHECK(set.find(10001) == set.cend());
|
||||
}
|
||||
|
||||
TEST_CASE("TestUnorderedSetHashCollision")
|
||||
{
|
||||
// A hash function that's guaranteed to collide
|
||||
class MyBadHash
|
||||
{
|
||||
public:
|
||||
size_t operator () (int inValue) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
UnorderedSet<int, MyBadHash> set;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
CHECK(set.insert(i).second);
|
||||
|
||||
CHECK(set.size() == 10);
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
CHECK(set.find(11) == set.cend());
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(set.erase(i) == 1);
|
||||
|
||||
CHECK(set.size() == 5);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(set.find(i) == set.end());
|
||||
|
||||
for (int i = 5; i < 10; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
CHECK(set.find(11) == set.cend());
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
CHECK(set.insert(i).second);
|
||||
|
||||
CHECK(!set.insert(0).second);
|
||||
|
||||
CHECK(set.size() == 10);
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
CHECK(set.find(11) == set.cend());
|
||||
}
|
||||
|
||||
TEST_CASE("TestUnorderedSetAddRemoveCyles")
|
||||
{
|
||||
UnorderedSet<int> set;
|
||||
constexpr int cBucketCount = 64;
|
||||
set.reserve(int(set.max_load_factor() * cBucketCount));
|
||||
CHECK(set.bucket_count() == cBucketCount);
|
||||
|
||||
// Repeatedly add and remove elements to see if the set cleans up tombstones
|
||||
constexpr int cNumElements = 64 * 6 / 8; // We make sure that the map is max 6/8 full to ensure that we never grow the map but rehash it instead
|
||||
int add_counter = 0;
|
||||
int remove_counter = 0;
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
for (int j = 0; j < cNumElements; ++j)
|
||||
{
|
||||
CHECK(set.find(add_counter) == set.end());
|
||||
CHECK(set.insert(add_counter).second);
|
||||
CHECK(set.find(add_counter) != set.end());
|
||||
++add_counter;
|
||||
}
|
||||
|
||||
CHECK(set.size() == cNumElements);
|
||||
|
||||
for (int j = 0; j < cNumElements; ++j)
|
||||
{
|
||||
CHECK(set.find(remove_counter) != set.end());
|
||||
CHECK(set.erase(remove_counter) == 1);
|
||||
CHECK(set.erase(remove_counter) == 0);
|
||||
CHECK(set.find(remove_counter) == set.end());
|
||||
++remove_counter;
|
||||
}
|
||||
|
||||
CHECK(set.size() == 0);
|
||||
CHECK(set.empty());
|
||||
}
|
||||
|
||||
// Test that adding and removing didn't resize the set
|
||||
CHECK(set.bucket_count() == cBucketCount);
|
||||
}
|
||||
|
||||
TEST_CASE("TestUnorderedSetManyTombStones")
|
||||
{
|
||||
// A hash function that makes sure that consecutive ints end up in consecutive buckets starting at bucket 63
|
||||
class MyBadHash
|
||||
{
|
||||
public:
|
||||
size_t operator () (int inValue) const
|
||||
{
|
||||
return (inValue + 63) << 7;
|
||||
}
|
||||
};
|
||||
|
||||
UnorderedSet<int, MyBadHash> set;
|
||||
constexpr int cBucketCount = 64;
|
||||
set.reserve(int(set.max_load_factor() * cBucketCount));
|
||||
CHECK(set.bucket_count() == cBucketCount);
|
||||
|
||||
// Fill 32 buckets
|
||||
int add_counter = 0;
|
||||
for (int i = 0; i < 32; ++i)
|
||||
CHECK(set.insert(add_counter++).second);
|
||||
|
||||
// Since we control the hash, we know in which order we'll visit the elements
|
||||
// The first element was inserted in bucket 63, so we start at 1
|
||||
int expected = 1;
|
||||
for (int i : set)
|
||||
{
|
||||
CHECK(i == expected);
|
||||
expected = (expected + 1) & 31;
|
||||
}
|
||||
expected = 1;
|
||||
for (int i : set)
|
||||
{
|
||||
CHECK(i == expected);
|
||||
expected = (expected + 1) & 31;
|
||||
}
|
||||
|
||||
// Remove a bucket in the middle with so that the number of occupied slots
|
||||
// surrounding the bucket exceed 16 to force creating a tombstone,
|
||||
// then add one at the end
|
||||
int remove_counter = 16;
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
CHECK(set.find(remove_counter) != set.end());
|
||||
CHECK(set.erase(remove_counter) == 1);
|
||||
CHECK(set.find(remove_counter) == set.end());
|
||||
|
||||
CHECK(set.find(add_counter) == set.end());
|
||||
CHECK(set.insert(add_counter).second);
|
||||
CHECK(set.find(add_counter) != set.end());
|
||||
|
||||
++add_counter;
|
||||
++remove_counter;
|
||||
}
|
||||
|
||||
// Check that the elements we inserted are still there
|
||||
CHECK(set.size() == 32);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
CHECK(*set.find(add_counter - 1 - i) == add_counter - 1 - i);
|
||||
|
||||
// Test that adding and removing didn't resize the set
|
||||
CHECK(set.bucket_count() == cBucketCount);
|
||||
}
|
||||
|
||||
static bool sReversedHash = false;
|
||||
|
||||
TEST_CASE("TestUnorderedSetRehash")
|
||||
{
|
||||
// A hash function for which we can switch the hashing algorithm
|
||||
class MyBadHash
|
||||
{
|
||||
public:
|
||||
size_t operator () (int inValue) const
|
||||
{
|
||||
return (sReversedHash? 127 - inValue : inValue) << 7;
|
||||
}
|
||||
};
|
||||
|
||||
using Set = UnorderedSet<int, MyBadHash>;
|
||||
Set set;
|
||||
constexpr int cBucketCount = 128;
|
||||
set.reserve(int(set.max_load_factor() * cBucketCount));
|
||||
CHECK(set.bucket_count() == cBucketCount);
|
||||
|
||||
// Fill buckets
|
||||
sReversedHash = false;
|
||||
constexpr int cNumElements = 96;
|
||||
for (int i = 0; i < cNumElements; ++i)
|
||||
CHECK(set.insert(i).second);
|
||||
|
||||
// Check that we get the elements in the expected order
|
||||
int expected = 0;
|
||||
for (int i : set)
|
||||
CHECK(i == expected++);
|
||||
|
||||
// Change the hashing algorithm so that a rehash is forced to move elements.
|
||||
// The test is designed in such a way that it will both need to move elements to empty slots
|
||||
// and to move elements to slots that currently already have another element.
|
||||
sReversedHash = true;
|
||||
set.rehash(0);
|
||||
|
||||
// Check that all elements are still there
|
||||
for (int i = 0; i < cNumElements; ++i)
|
||||
CHECK(*set.find(i) == i);
|
||||
|
||||
// The hash went from filling buckets 0 .. 95 with values 0 .. 95 to bucket 127 .. 31 with values 0 .. 95
|
||||
// However, we don't move elements if they still fall within the same batch, this means that the first 8
|
||||
// elements didn't move
|
||||
Set::const_iterator it = set.begin();
|
||||
for (int i = 0; i < 8; ++i, ++it)
|
||||
CHECK(*it == i);
|
||||
|
||||
// The rest will have been reversed
|
||||
for (int i = 95; i > 7; --i, ++it)
|
||||
CHECK(*it == i);
|
||||
|
||||
// Test that adding and removing didn't resize the set
|
||||
CHECK(set.bucket_count() == cBucketCount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user