diff --git a/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj b/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj index 9b2ccec8..07882b1d 100644 --- a/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj +++ b/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj @@ -47,6 +47,7 @@ + diff --git a/tests/OpenTK.Tests.Math/QuaternionTests.cs b/tests/OpenTK.Tests.Math/QuaternionTests.cs new file mode 100644 index 00000000..27f0a28b --- /dev/null +++ b/tests/OpenTK.Tests.Math/QuaternionTests.cs @@ -0,0 +1,214 @@ +using Xunit; +using System; +using System.Collections.Generic; + +namespace OpenTK.Tests.Math +{ + /// + /// Generates Quaternion test data. + /// + public class QuaternionTestDataGenerator + { + /// + /// Returns the single axis test cases. + /// 1. param: rotation in euler angles + /// 2. param: expected result of xyz-component of quaternion + /// 3. param: test name (Don't found a working way how to pass this to xUnit runner). I let it here for test documentation + /// + /// The single axis test cases. + public static IEnumerable SingleAxisTestCases() + { + yield return new object[] { new Vector3(1, 0, 0), Vector3.UnitX, "Rotate around x axis" }; + yield return new object[] { new Vector3(0, 1, 0), Vector3.UnitY, "Rotate around y axis" }; + yield return new object[] { new Vector3(0, 0, 1), Vector3.UnitZ, "Rotate around z axis" }; + } + + /// + /// Returns the single ToAxisAngle test cases. + /// 1. param: Quaternion which a definied value of xyz-component. + /// 2. param: expected result of xyz-component of quaternion + /// 3. param: test name (Don't found a working way how to pass this to xUnit runner). I let it here for test documentation + /// + /// The single axis test cases. + public static IEnumerable ToAxisAngleTestCases() + { + yield return new object[] { new Quaternion(Vector3.UnitX, 0), Vector3.UnitX, "Rotate around x axis" }; + yield return new object[] { new Quaternion(Vector3.UnitY, 0), Vector3.UnitY, "Rotate around y axis" }; + yield return new object[] { new Quaternion(Vector3.UnitZ, 0), Vector3.UnitZ, "Rotate around z axis" }; + } + } + + /// + /// Provides some methods which helps to verify test results + /// + internal static class QuaternionTestHelper + { + /// + /// Verifies the direction of an given . + /// + /// false: When does contain xyz values, when it should be 0, + /// or does not contain 0 when it should be + /// To test + /// Expected directions. Values getting only 0 checked + public static bool VerifyEuqalSignleDirection(Vector3 toTest, Vector3 expected) + { + //To verify the direction of an vector, just respect the 0 values and check against these. + //The length of the vectors are ignored. + if (expected.X == 0) + { + if (toTest.X != 0) + return false; + } + else + { + if (toTest.X == 0) + return false; + } + + if (expected.Y == 0) + { + if (toTest.Y != 0) + return false; + } + else + { + if (toTest.Y == 0) + return false; + } + + if (expected.Z == 0) + { + if (toTest.Z != 0) + return false; + } + else + { + if (toTest.Z == 0) + return false; + } + + return true; + } + } + + public class Quaternion_Tests + { + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void CtorEulerAnglesFloat_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = new Quaternion(eulerValues.X, eulerValues.Y, eulerValues.Z); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void CtorEulerAnglesVector3_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = new Quaternion(eulerValues.X, eulerValues.Y, eulerValues.Z); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void FromEulerAnglesFloat_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = Quaternion.FromEulerAngles(eulerValues.X, eulerValues.Y, eulerValues.Z); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void FromEulerAnglesVector3_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = Quaternion.FromEulerAngles(eulerValues); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void FromEulerAnglesOut_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = Quaternion.Identity; + Quaternion.FromEulerAngles(ref eulerValues, out cut); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Check if a quaternion returns a a rotation about the correct coordinate axis + /// + /// Prepared Quaternion + /// Expected result. + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.ToAxisAngleTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void ToAxisAngle_SingleAxisSetAndAngleIgnored_RotateCorrectAxis(Quaternion cut, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with rotation about X/Y/Z axis + Vector3 resultXYZ; + float dontCare; + cut.ToAxisAngle(out resultXYZ, out dontCare); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + //TODO: Make also checks with rotation angle + } +} +