diff --git a/tests/OpenTK.Tests/Assertions.fs b/tests/OpenTK.Tests/Assertions.fs new file mode 100644 index 00000000..93baa61a --- /dev/null +++ b/tests/OpenTK.Tests/Assertions.fs @@ -0,0 +1,31 @@ +namespace OpenTK.Tests + +open Xunit +open FsCheck +open FsCheck.Xunit +open System +open OpenTK + +[] +module private AssertHelpers = + [] + let private BitAccuracy = 4 + + let approxEq a b = MathHelper.ApproximatelyEqual(a,b,BitAccuracy) + +/// We use a full type here instead of a module, as the overloading semantics are more suitable for our desired goal. +[] +type internal Assert = + + static member ApproximatelyEqual(a : Vector2,b : Vector2) = + if not <| approxEq a.X b.X && approxEq a.Y b.Y then raise <| new Xunit.Sdk.EqualException(a,b) + + static member ApproximatelyEqual(a : Vector3,b : Vector3) = + if not <| approxEq a.X b.X && approxEq a.Y b.Y && approxEq a.Z b.Z then raise <| new Xunit.Sdk.EqualException(a,b) + + static member ApproximatelyEqual(a : Vector4,b : Vector4) = + if not <| approxEq a.X b.X && approxEq a.Y b.Y && approxEq a.Z b.Z && approxEq a.W b.W then + raise <| new Xunit.Sdk.EqualException(a,b) + + static member ApproximatelyEqual(a : float32,b : float32) = + if not <| approxEq a b then raise <| new Xunit.Sdk.EqualException(a,b) diff --git a/tests/OpenTK.Tests/Generators.fs b/tests/OpenTK.Tests/Generators.fs new file mode 100644 index 00000000..a57424f8 --- /dev/null +++ b/tests/OpenTK.Tests/Generators.fs @@ -0,0 +1,76 @@ +namespace OpenTK.Tests + +open Xunit +open FsCheck +open FsCheck.Xunit +open System +open OpenTK + +[] +module private Generators = + let private isValidFloat f = not (Single.IsNaN f || Single.IsInfinity f || Single.IsInfinity (f * f) || f = Single.MinValue || f = Single.MaxValue ) + let private isValidDouble d = not (Double.IsNaN d || Double.IsInfinity d || Double.IsInfinity (d * d)|| d = Double.MinValue || d = Double.MaxValue) + let singleArb = Arb.Default.Float32() |> Arb.toGen |> Gen.filter isValidFloat + let single = singleArb |> Arb.fromGen + + let double = + Arb.Default.Float() |> Arb.toGen + |> Gen.filter isValidDouble + |> Arb.fromGen + + let vec2 = + singleArb + |> Gen.two + |> Gen.map Vector2 + |> Arb.fromGen + + let vec3 = + singleArb + |> Gen.three + |> Gen.map Vector3 + |> Arb.fromGen + + let vec4 = + singleArb + |> Gen.four + |> Gen.map Vector4 + |> Arb.fromGen + + let quat = + singleArb + |> Gen.four + |> Gen.map Quaternion + |> Arb.fromGen + + let mat2 = + singleArb + |> Gen.four + |> Gen.map Matrix2 + |> Arb.fromGen + + let mat3 = + vec3 + |> Arb.toGen + |> Gen.three + |> Gen.map Matrix3 + |> Arb.fromGen + + let mat4 = + vec4 + |> Arb.toGen + |> Gen.four + |> Gen.map Matrix4 + |> Arb.fromGen + +type OpenTKGen = + static member Single() = single + static member float32() = single + static member Double() = double + static member float() = double + static member Vector2() = vec2 + static member Vector3() = vec3 + static member Vector4() = vec4 + static member Quaternion() = quat + static member Matrix2() = mat2 + static member Matrix3() = mat3 + static member Matrix4() = mat4 diff --git a/tests/OpenTK.Tests/MathHelper.fs b/tests/OpenTK.Tests/MathHelper.fs new file mode 100644 index 00000000..7c37a25f --- /dev/null +++ b/tests/OpenTK.Tests/MathHelper.fs @@ -0,0 +1,55 @@ +namespace OpenTK.Tests + +open Xunit +open FsCheck +open FsCheck.Xunit +open System +open OpenTK + +[ |])>] +module MathHelper = + /// This test ensures that approximately equal can never get it 'wrong' about the values. + [] + let ``ApproximatelyEqual is never incorrect`` (a : float32,b : float32,bits : int32) = + let clamped = max 0 (min bits 24) + let areApproxEqual = MathHelper.ApproximatelyEqual(a,b,clamped) + let areExactlyEqual = a = b + let isWrong = areExactlyEqual && not areApproxEqual + Assert.False(isWrong) + + [] + let ``ApproximatelyEqual can return true if some values are not exactly equal`` (a : float32,b : float32,bits : int32) = + let clamped = max 0 (min bits 24) + let areApproxEqual = MathHelper.ApproximatelyEqual(a,b,clamped) + let areExactlyEqual = a = b + let isWrong = areExactlyEqual && not areApproxEqual + let p = new PropertyAttribute() + Assert.False(isWrong) + + [] + let ``ApproximatelyEqual correctly approximates equality``() = + let a = 0.000000001f + let b = 0.0000000010000001f + Assert.NotEqual(a,b) + [ 1..24 ] |> List.iter (fun i -> Assert.True(MathHelper.ApproximatelyEqual(a,b,i))) + + [] + let ``ApproximatelyEqual reports very different values as non-equal even with high bit count``() = + let a = 2.0f + let b = 1.0f + Assert.NotEqual(a,b) + Assert.False(MathHelper.ApproximatelyEqual(a,b,10)) + + [] + let ``ApproximatelyEqual works with single zero value``() = + let a = 1.0f + let b = 0.0f + Assert.NotEqual(a,b) + Assert.False(MathHelper.ApproximatelyEqual(a,b,0)) + + [] + let ``ApproximatelyEqual works with both zero values``() = + let a = 0.0f + let b = 0.0f + Assert.Equal(a,b) + Assert.True(MathHelper.ApproximatelyEqual(a,b,0)) diff --git a/tests/OpenTK.Tests/OpenTK.Tests.fsproj b/tests/OpenTK.Tests/OpenTK.Tests.fsproj index 46190f28..c57ae0c8 100644 --- a/tests/OpenTK.Tests/OpenTK.Tests.fsproj +++ b/tests/OpenTK.Tests/OpenTK.Tests.fsproj @@ -1483,6 +1483,9 @@ + + + diff --git a/tests/OpenTK.Tests/Vectors.fs b/tests/OpenTK.Tests/Vectors.fs index 90ba1b1c..13e6a750 100644 --- a/tests/OpenTK.Tests/Vectors.fs +++ b/tests/OpenTK.Tests/Vectors.fs @@ -6,38 +6,8 @@ open FsCheck.Xunit open System open OpenTK -[] -module internal Generators = - let private isValidFloat f = not (Single.IsNaN f || Single.IsInfinity f) - - let Vec2 = - Arb.generate - |> Gen.filter isValidFloat - |> Gen.two - |> Gen.map Vector2 - |> Arb.fromGen - - let Vec3 = - Arb.generate - |> Gen.filter isValidFloat - |> Gen.three - |> Gen.map Vector3 - |> Arb.fromGen - - let Vec4 = - Arb.generate - |> Gen.filter isValidFloat - |> Gen.four - |> Gen.map Vector4 - |> Arb.fromGen - -type VectorGen = - static member Vector2() = Generators.Vec2 - static member Vector3() = Generators.Vec3 - static member Vector4() = Generators.Vec4 - module Vector2 = - [ |])>] + [ |])>] module ``Simple Properties`` = // [] @@ -50,28 +20,28 @@ module Vector2 = // Assert.True(a.Length >= 0.0f) - [ |])>] + [ |])>] module Addition = // [] let ``Vector addition is the same as component addition`` (a : Vector2,b : Vector2) = let c = a + b - Assert.Equal(a.X + b.X,c.X) - Assert.Equal(a.Y + b.Y,c.Y) + Assert.ApproximatelyEqual(a.X + b.X,c.X) + Assert.ApproximatelyEqual(a.Y + b.Y,c.Y) [] let ``Vector addition is commutative`` (a : Vector2,b : Vector2) = let c = a + b let c2 = b + a - Assert.Equal(c,c2) + Assert.ApproximatelyEqual(c,c2) [] let ``Vector addition is associative`` (a : Vector2,b : Vector2,c : Vector2) = let r1 = (a + b) + c let r2 = a + (b + c) - Assert.Equal(r1,r2) + Assert.ApproximatelyEqual(r1,r2) - [ |])>] + [ |])>] module Multiplication = // [] @@ -92,7 +62,7 @@ module Vector2 = Assert.Equal(a.X * f,r.X) Assert.Equal(a.Y * f,r.Y) - [ |])>] + [ |])>] module Subtraction = // [] @@ -101,12 +71,11 @@ module Vector2 = Assert.Equal(a.X - b.X,c.X) Assert.Equal(a.Y - b.Y,c.Y) - [ |])>] + [ |])>] module Division = // [] let ``Vector-float division is the same as component-float division`` (a : Vector2,f : float32) = - if f <> 0.0f then - let r = a / f - Assert.Equal(a.X / f,r.X) - Assert.Equal(a.Y / f,r.Y) + let r = a / f + Assert.ApproximatelyEqual(a.X / f,r.X) + Assert.ApproximatelyEqual(a.Y / f,r.Y)