Просмотр исходного кода

Improving unit test coverage for Math functions (#1380)

Jorrit Rouwe 8 месяцев назад
Родитель
Сommit
c10d9b2a8e

+ 1 - 1
Jolt/Math/Math.h

@@ -72,7 +72,7 @@ JPH_INLINE constexpr T Sign(T inV)
 template <typename T>
 constexpr bool IsPowerOf2(T inV)
 {
-	return (inV & (inV - 1)) == 0;
+	return inV > 0 && (inV & (inV - 1)) == 0;
 }
 
 /// Align inV up to the next inAlignment bytes

+ 1 - 0
UnitTests/Math/BVec16Tests.cpp

@@ -35,6 +35,7 @@ TEST_SUITE("BVec16Tests")
 		CHECK(v != BVec16(1, 2, 3, 4, 5, 6, 7, 8, 10, 9, 11, 12, 13, 14, 15, 16));
 
 		// Check element modification
+		CHECK(const_cast<const BVec16 &>(v)[15] == 16); // Check const operator
 		v[15] = 17;
 		CHECK(v[15] == 17);
 		CHECK(v == BVec16(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17));

+ 79 - 0
UnitTests/Math/DMat44Tests.cpp

@@ -4,6 +4,7 @@
 
 #include "UnitTestFramework.h"
 #include <Jolt/Math/DMat44.h>
+#include <Jolt/Core/StringTools.h>
 
 TEST_SUITE("DMat44Tests")
 {
@@ -11,6 +12,7 @@ TEST_SUITE("DMat44Tests")
 	{
 		DMat44 zero = DMat44::sZero();
 
+		CHECK(zero == DMat44(Vec4(0, 0, 0, 0), Vec4(0, 0, 0, 0), Vec4(0, 0, 0, 0), DVec3(0, 0, 0)));
 		CHECK(zero.GetAxisX() == Vec3::sZero());
 		CHECK(zero.GetAxisY() == Vec3::sZero());
 		CHECK(zero.GetAxisZ() == Vec3::sZero());
@@ -22,6 +24,12 @@ TEST_SUITE("DMat44Tests")
 		DMat44 identity = DMat44::sIdentity();
 
 		CHECK(identity == DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), DVec3(0, 0, 0)));
+
+		// Check non-equality
+		CHECK(identity != DMat44(Vec4(0, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), DVec3(0, 0, 0)));
+		CHECK(identity != DMat44(Vec4(1, 0, 0, 0), Vec4(0, 0, 0, 0), Vec4(0, 0, 1, 0), DVec3(0, 0, 0)));
+		CHECK(identity != DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 0, 0), DVec3(0, 0, 0)));
+		CHECK(identity != DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), DVec3(1, 0, 0)));
 	}
 
 	TEST_CASE("TestDMat44Construct")
@@ -61,6 +69,23 @@ TEST_SUITE("DMat44Tests")
 		CHECK(mat == DMat44(Vec4(17, 18, 19, 20), Vec4(21, 22, 23, 24), Vec4(25, 26, 27, 28), DVec3(13, 14, 15)));
 	}
 
+	TEST_CASE("TestDMat44Rotation")
+	{
+		Quat q = Quat::sRotation(Vec3(1, 1, 1).Normalized(), 0.2f * JPH_PI);
+		CHECK(DMat44::sRotation(q).ToMat44() == Mat44::sRotation(q));
+	}
+
+	TEST_CASE("TestDMat44Translation")
+	{
+		CHECK(DMat44::sTranslation(DVec3(1, 2, 3)) == DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), DVec3(1, 2, 3)));
+	}
+
+	TEST_CASE("TestDMat44RotationTranslation")
+	{
+		Quat q = Quat::sRotation(Vec3(1, 1, 1).Normalized(), 0.2f * JPH_PI);
+		CHECK(DMat44::sRotationTranslation(q, DVec3(1, 2, 3)).ToMat44() == Mat44::sRotationTranslation(q, Vec3(1, 2, 3)));
+	}
+
 	TEST_CASE("TestDMat44MultiplyMat44")
 	{
 		DMat44 mat(Vec4(1, 2, 3, 0), Vec4(5, 6, 7, 0), Vec4(9, 10, 11, 0), DVec3(13, 14, 15));
@@ -164,4 +189,58 @@ TEST_SUITE("DMat44Tests")
 		CHECK_APPROX_EQUAL(rotation_translation, m2);
 		CHECK_APPROX_EQUAL(scale, scale_out);
 	}
+
+	TEST_CASE("TestDMat44ToMat44")
+	{
+		DMat44 mat(Vec4(1, 2, 3, 4), Vec4(5, 6, 7, 8), Vec4(9, 10, 11, 12), DVec3(13, 14, 15));
+		CHECK(mat.ToMat44() == Mat44(Vec4(1, 2, 3, 4), Vec4(5, 6, 7, 8), Vec4(9, 10, 11, 12), Vec4(13, 14, 15, 1)));
+	}
+
+	TEST_CASE("TestDMat44Column")
+	{
+		DMat44 mat = DMat44::sZero();
+		mat.SetColumn4(0, Vec4(1, 2, 3, 4));
+		CHECK(mat.GetColumn4(0) == Vec4(1, 2, 3, 4));
+		mat.SetColumn3(0, Vec3(5, 6, 7));
+		CHECK(mat.GetColumn3(0) == Vec3(5, 6, 7));
+		CHECK(mat.GetColumn4(0) == Vec4(5, 6, 7, 0));
+
+		mat.SetAxisX(Vec3(8, 9, 10));
+		mat.SetAxisY(Vec3(11, 12, 13));
+		mat.SetAxisZ(Vec3(14, 15, 16));
+		mat.SetTranslation(DVec3(17, 18, 19));
+		CHECK(mat.GetAxisX() == Vec3(8, 9, 10));
+		CHECK(mat.GetAxisY() == Vec3(11, 12, 13));
+		CHECK(mat.GetAxisZ() == Vec3(14, 15, 16));
+		CHECK(mat.GetTranslation() == DVec3(17, 18, 19));
+	}
+
+	TEST_CASE("TestDMat44Transposed")
+	{
+		DMat44 mat(Vec4(1, 2, 3, 4), Vec4(5, 6, 7, 8), Vec4(9, 10, 11, 12), DVec3(13, 14, 15));
+		Mat44 result = mat.Transposed3x3();
+		CHECK(result == Mat44(Vec4(1, 5, 9, 0), Vec4(2, 6, 10, 0), Vec4(3, 7, 11, 0), Vec4(0, 0, 0, 1)));
+	}
+
+	TEST_CASE("TestDMat44GetQuaternion")
+	{
+		Quat rot = Quat::sRotation(Vec3(1, 1, 1).Normalized(), 0.2f * JPH_PI);
+		DMat44 mat = DMat44::sRotation(rot);
+		CHECK_APPROX_EQUAL(mat.GetQuaternion(), rot);
+	}
+
+	TEST_CASE("TestDMat44PrePostTranslated")
+	{
+		DMat44 m(Vec4(2, 3, 4, 0), Vec4(5, 6, 7, 0), Vec4(8, 9, 10, 0), DVec3(11, 12, 13));
+		DVec3 v(14, 15, 16);
+
+		CHECK(m.PreTranslated(v) == m * DMat44::sTranslation(v));
+		CHECK(m.PostTranslated(v) == DMat44::sTranslation(v) * m);
+	}
+
+	TEST_CASE("TestDMat44ConvertToString")
+	{
+		DMat44 v(Vec4(1, 2, 3, 4), Vec4(5, 6, 7, 8), Vec4(9, 10, 11, 12), DVec3(13, 14, 15));
+		CHECK(ConvertToString(v) == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15");
+	}
 }

+ 24 - 0
UnitTests/Math/DVec3Tests.cpp

@@ -4,6 +4,7 @@
 
 #include "UnitTestFramework.h"
 #include <Jolt/Math/DVec3.h>
+#include <Jolt/Core/StringTools.h>
 
 TEST_SUITE("DVec3Tests")
 {
@@ -16,6 +17,13 @@ TEST_SUITE("DVec3Tests")
 		CHECK(v.GetZ() == 0);
 	}
 
+	TEST_CASE("TestDVec3Axis")
+	{
+		CHECK(DVec3::sAxisX() == DVec3(1, 0, 0));
+		CHECK(DVec3::sAxisY() == DVec3(0, 1, 0));
+		CHECK(DVec3::sAxisZ() == DVec3(0, 0, 1));
+	}
+
 	TEST_CASE("TestVec3NaN")
 	{
 		DVec3 v = DVec3::sNaN();
@@ -56,6 +64,16 @@ TEST_SUITE("DVec3Tests")
 		v.SetComponent(1, 5);
 		v.SetComponent(2, 6);
 		CHECK(v == DVec3(4, 5, 6));
+
+		// Set the components again
+		v.SetX(7);
+		v.SetY(8);
+		v.SetZ(9);
+		CHECK(v == DVec3(7, 8, 9));
+
+		// Set all components
+		v.Set(10, 11, 12);
+		CHECK(v == DVec3(10, 11, 12));
 	}
 
 	TEST_CASE("TestVec4ToDVec3")
@@ -281,4 +299,10 @@ TEST_SUITE("DVec3Tests")
 		CHECK(DVec3(1.2345, -6.7891, 0).GetSign() == DVec3(1, -1, 1));
 		CHECK(DVec3(0, 2.3456, -7.8912).GetSign() == DVec3(1, 1, -1));
 	}
+
+	TEST_CASE("TestDVec3ConvertToString")
+	{
+		DVec3 v(1, 2, 3);
+		CHECK(ConvertToString(v) == "1, 2, 3");
+	}
 }

+ 46 - 0
UnitTests/Math/Mat44Tests.cpp

@@ -4,6 +4,7 @@
 
 #include "UnitTestFramework.h"
 #include <Jolt/Math/Mat44.h>
+#include <Jolt/Core/StringTools.h>
 
 TEST_SUITE("Mat44Tests")
 {
@@ -16,6 +17,38 @@ TEST_SUITE("Mat44Tests")
 				CHECK(zero(row, col) == 0.0f);
 	}
 
+	TEST_CASE("TestMat44Column")
+	{
+		Mat44 mat = Mat44::sZero();
+		mat.SetColumn4(0, Vec4(1, 2, 3, 4));
+		CHECK(mat.GetColumn4(0) == Vec4(1, 2, 3, 4));
+		mat.SetColumn3(0, Vec3(5, 6, 7));
+		CHECK(mat.GetColumn3(0) == Vec3(5, 6, 7));
+		CHECK(mat.GetColumn4(0) == Vec4(5, 6, 7, 0));
+
+		mat.SetAxisX(Vec3(8, 9, 10));
+		mat.SetAxisY(Vec3(11, 12, 13));
+		mat.SetAxisZ(Vec3(14, 15, 16));
+		mat.SetTranslation(Vec3(17, 18, 19));
+		CHECK(mat.GetAxisX() == Vec3(8, 9, 10));
+		CHECK(mat.GetAxisY() == Vec3(11, 12, 13));
+		CHECK(mat.GetAxisZ() == Vec3(14, 15, 16));
+		CHECK(mat.GetTranslation() == Vec3(17, 18, 19));
+
+		mat.SetDiagonal3(Vec3(20, 21, 22));
+		CHECK(mat.GetDiagonal3() == Vec3(20, 21, 22));
+		CHECK(mat.GetAxisX() == Vec3(20, 9, 10));
+		CHECK(mat.GetAxisY() == Vec3(11, 21, 13));
+		CHECK(mat.GetAxisZ() == Vec3(14, 15, 22));
+
+		mat.SetDiagonal4(Vec4(23, 24, 25, 26));
+		CHECK(mat.GetDiagonal4() == Vec4(23, 24, 25, 26));
+		CHECK(mat.GetAxisX() == Vec3(23, 9, 10));
+		CHECK(mat.GetAxisY() == Vec3(11, 24, 13));
+		CHECK(mat.GetAxisZ() == Vec3(14, 15, 25));
+		CHECK(mat.GetColumn4(3) == Vec4(17, 18, 19, 26));
+	}
+
 	TEST_CASE("TestMat44NaN")
 	{
 		Mat44 nan = Mat44::sNaN();
@@ -521,4 +554,17 @@ TEST_SUITE("Mat44Tests")
 		CHECK_APPROX_EQUAL(m2.GetAxisX().Cross(m2.GetAxisY()).Dot(m2.GetAxisZ()), 1.0f); // Check perpendicular
 		CHECK_APPROX_EQUAL(scale, scale_out, 0.05f); // Scale may change a bit
 	}
+
+	TEST_CASE("TestDMat44GetQuaternion")
+	{
+		Quat rot = Quat::sRotation(Vec3(1, 1, 1).Normalized(), 0.2f * JPH_PI);
+		Mat44 mat = Mat44::sRotation(rot);
+		CHECK_APPROX_EQUAL(mat.GetQuaternion(), rot);
+	}
+
+	TEST_CASE("TestDMat44ConvertToString")
+	{
+		Mat44 v(Vec4(1, 2, 3, 4), Vec4(5, 6, 7, 8), Vec4(9, 10, 11, 12), Vec4(13, 14, 15, 16));
+		CHECK(ConvertToString(v) == "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16");
+	}
 }

+ 28 - 0
UnitTests/Math/MathTests.cpp

@@ -53,4 +53,32 @@ TEST_SUITE("Mat44Tests")
 		CHECK(GetNextPowerOf2(0x8000000U - 1) == 0x8000000U);
 		CHECK(GetNextPowerOf2(0x8000000U) == 0x8000000U);
 	}
+
+	TEST_CASE("TestCenterAngleAroundZero")
+	{
+		for (int i = 0; i < 10; i += 2)
+		{
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero(i * JPH_PI), 0, 1.0e-5f);
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero((0.5f + i) * JPH_PI), 0.5f * JPH_PI, 1.0e-5f);
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero((1.5f + i) * JPH_PI), -0.5f * JPH_PI, 1.0e-5f);
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero(-(0.5f + i) * JPH_PI), -0.5f * JPH_PI, 1.0e-5f);
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero(-(1.5f + i) * JPH_PI), 0.5f * JPH_PI, 1.0e-5f);
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero(-(0.99f + i) * JPH_PI), -0.99f * JPH_PI, 1.0e-5f);
+			CHECK_APPROX_EQUAL(CenterAngleAroundZero((0.99f + i) * JPH_PI), 0.99f * JPH_PI, 1.0e-5f);
+		}
+	}
+
+	TEST_CASE("TestIsPowerOf2")
+	{
+		for (int i = 0; i < 63; ++i)
+			CHECK(IsPowerOf2(uint64(1) << 1));
+		CHECK(!IsPowerOf2(-2));
+		CHECK(!IsPowerOf2(0));
+		CHECK(!IsPowerOf2(3));
+		CHECK(!IsPowerOf2(5));
+		CHECK(!IsPowerOf2(15));
+		CHECK(!IsPowerOf2(17));
+		CHECK(!IsPowerOf2(65535));
+		CHECK(!IsPowerOf2(65537));
+	}
 }

+ 37 - 0
UnitTests/Math/QuatTests.cpp

@@ -5,6 +5,7 @@
 #include "UnitTestFramework.h"
 #include <Jolt/Math/Mat44.h>
 #include <Jolt/Math/Quat.h>
+#include <Jolt/Core/StringTools.h>
 #include <random>
 
 TEST_SUITE("QuatTests")
@@ -64,6 +65,16 @@ TEST_SUITE("QuatTests")
 		CHECK(Quat(1, 2, 3, 4) * 5.0f == Quat(5, 10, 15, 20));
 		CHECK(5.0f * Quat(1, 2, 3, 4) == Quat(5, 10, 15, 20));
 		CHECK(Quat(2, 4, 6, 8) / 2.0f == Quat(1, 2, 3, 4));
+
+		Quat v(1, 2, 3, 4);
+		v += Quat(5, 6, 7, 8);
+		CHECK(v == Quat(6, 8, 10, 12));
+		v -= Quat(4, 3, 2, 1);
+		CHECK(v == Quat(2, 5, 8, 11));
+		v *= 2.0f;
+		CHECK(v == Quat(4, 10, 16, 22));
+		v /= 2.0f;
+		CHECK(v == Quat(2, 5, 8, 11));
 	}
 
 	TEST_CASE("TestQuatPerpendicular")
@@ -342,6 +353,8 @@ TEST_SUITE("QuatTests")
 		CHECK_APPROX_EQUAL(twist2, q2);
 		Quat swing2 = twist2.Inversed() * swing1;
 		CHECK_APPROX_EQUAL(swing2, Quat::sIdentity());
+
+		CHECK(Quat::sZero().GetTwist(Vec3::sAxisX()) == Quat::sIdentity());
 	}
 
 	TEST_CASE("TestQuatGetRotationAngle")
@@ -449,4 +462,28 @@ TEST_SUITE("QuatTests")
 			CHECK_APPROX_EQUAL(v2t, v1t, 1.0e-5f);
 		}
 	}
+
+	TEST_CASE("TestQuatConvertToString")
+	{
+		Quat v(1, 2, 3, 4);
+		CHECK(ConvertToString(v) == "1, 2, 3, 4");
+	}
+
+	TEST_CASE("TestQuatLERP")
+	{
+		Quat v1(1, 2, 3, 4);
+		Quat v2(5, 6, 7, 8);
+		CHECK(v1.LERP(v2, 0.25f) == Quat(2, 3, 4, 5));
+	}
+
+	TEST_CASE("TestQuatSLERP")
+	{
+		Quat v1 = Quat::sIdentity();
+		Quat v2 = Quat::sRotation(Vec3::sAxisX(), 0.99f * JPH_PI);
+		CHECK_APPROX_EQUAL(v1.SLERP(v2, 0.25f), Quat::sRotation(Vec3::sAxisX(), 0.25f * 0.99f * JPH_PI));
+
+		// Check that we ignore the sign
+		Quat v3 = Quat(1, 2, 3, 4).Normalized();
+		CHECK_APPROX_EQUAL(v3.SLERP(-v3, 0.5f), v3);
+	}
 }

+ 17 - 0
UnitTests/Math/UVec4Tests.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: MIT
 
 #include "UnitTestFramework.h"
+#include <Jolt/Core/StringTools.h>
 
 TEST_SUITE("UVec4Tests")
 {
@@ -25,6 +26,16 @@ TEST_SUITE("UVec4Tests")
 		CHECK(v != UVec4(1, 2, 4, 3));
 	}
 
+	TEST_CASE("TestUVec4Components")
+	{
+		UVec4 v(1, 2, 3, 4);
+		v.SetX(5);
+		v.SetY(6);
+		v.SetZ(7);
+		v.SetW(8);
+		CHECK(v == UVec4(5, 6, 7, 8));
+	}
+
 	TEST_CASE("TestUVec4LoadStoreInt4")
 	{
 		alignas(16) uint32 i4[] = { 1, 2, 3, 4 };
@@ -544,4 +555,10 @@ TEST_SUITE("UVec4Tests")
 		CHECK(UVec4::sSort4True(UVec4(0x00000000U, 0xffffffffU, 0xffffffffU, 0xffffffffU), UVec4(1, 2, 3, 4)) == UVec4(2, 3, 4, 4));
 		CHECK(UVec4::sSort4True(UVec4(0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU), UVec4(1, 2, 3, 4)) == UVec4(1, 2, 3, 4));
 	}
+
+	TEST_CASE("TestUVec4ConvertToString")
+	{
+		UVec4 v(1, 2, 3, 4);
+		CHECK(ConvertToString(v) == "1, 2, 3, 4");
+	}
 }

+ 17 - 0
UnitTests/Math/Vec3Tests.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: MIT
 
 #include "UnitTestFramework.h"
+#include <Jolt/Core/StringTools.h>
 
 TEST_SUITE("Vec3Tests")
 {
@@ -29,6 +30,16 @@ TEST_SUITE("Vec3Tests")
 		v.SetComponent(1, 5);
 		v.SetComponent(2, 6);
 		CHECK(v == Vec3(4, 5, 6));
+
+		// Set the components
+		v.SetX(7);
+		v.SetY(8);
+		v.SetZ(9);
+		CHECK(v == Vec3(7, 8, 9));		
+
+		// Set all components
+		v.Set(10, 11, 12);
+		CHECK(v == Vec3(10, 11, 12));
 	}
 
 	TEST_CASE("TestVec3LoadStoreFloat3")
@@ -359,4 +370,10 @@ TEST_SUITE("Vec3Tests")
 		}
 	}
 #endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
+
+	TEST_CASE("TestVec3ConvertToString")
+	{
+		Vec3 v(1, 2, 3);
+		CHECK(ConvertToString(v) == "1, 2, 3");
+	}
 }

+ 7 - 0
UnitTests/Math/Vec4Tests.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: MIT
 
 #include "UnitTestFramework.h"
+#include <Jolt/Core/StringTools.h>
 
 TEST_SUITE("Vec4Tests")
 {
@@ -724,4 +725,10 @@ TEST_SUITE("Vec4Tests")
 
 		CHECK(ma < 3.0e-7);
 	}
+
+	TEST_CASE("TestVec4ConvertToString")
+	{
+		Vec4 v(1, 2, 3, 4);
+		CHECK(ConvertToString(v) == "1, 2, 3, 4");
+	}
 }