--- /dev/null
+namespace OpenTK.Tests
+
+open Xunit
+open FsCheck
+open FsCheck.Xunit
+open System
+open OpenTK
+
+[<AutoOpen>]
+module private AssertHelpers =
+ [<Literal>]
+ 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.
+[<Sealed>]
+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)
--- /dev/null
+namespace OpenTK.Tests
+
+open Xunit
+open FsCheck
+open FsCheck.Xunit
+open System
+open OpenTK
+
+[<AutoOpen>]
+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
--- /dev/null
+namespace OpenTK.Tests
+
+open Xunit
+open FsCheck
+open FsCheck.Xunit
+open System
+open OpenTK
+
+[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
+module MathHelper =
+ /// This test ensures that approximately equal can never get it 'wrong' about the values.
+ [<Property>]
+ 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)
+
+ [<Property>]
+ 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)
+
+ [<Fact>]
+ 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)))
+
+ [<Fact>]
+ 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))
+
+ [<Fact>]
+ 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))
+
+ [<Fact>]
+ 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))
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<None Include="paket.references" />
+ <Compile Include="Assertions.fs" />
+ <Compile Include="Generators.fs" />
+ <Compile Include="MathHelper.fs" />
<Compile Include="Vectors.fs" />
<Content Include="App.config" />
</ItemGroup>
open System
open OpenTK
-[<RequireQualifiedAccess>]
-module internal Generators =
- let private isValidFloat f = not (Single.IsNaN f || Single.IsInfinity f)
-
- let Vec2 =
- Arb.generate<float32>
- |> Gen.filter isValidFloat
- |> Gen.two
- |> Gen.map Vector2
- |> Arb.fromGen
-
- let Vec3 =
- Arb.generate<float32>
- |> Gen.filter isValidFloat
- |> Gen.three
- |> Gen.map Vector3
- |> Arb.fromGen
-
- let Vec4 =
- Arb.generate<float32>
- |> 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 =
- [<Properties(Arbitrary = [| typeof<VectorGen> |])>]
+ [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``Simple Properties`` =
//
[<Property>]
//
Assert.True(a.Length >= 0.0f)
- [<Properties(Arbitrary = [| typeof<VectorGen> |])>]
+ [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Addition =
//
[<Property>]
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)
[<Property>]
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)
[<Property>]
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)
- [<Properties(Arbitrary = [| typeof<VectorGen> |])>]
+ [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Multiplication =
//
[<Property>]
Assert.Equal(a.X * f,r.X)
Assert.Equal(a.Y * f,r.Y)
- [<Properties(Arbitrary = [| typeof<VectorGen> |])>]
+ [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Subtraction =
//
[<Property>]
Assert.Equal(a.X - b.X,c.X)
Assert.Equal(a.Y - b.Y,c.Y)
- [<Properties(Arbitrary = [| typeof<VectorGen> |])>]
+ [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Division =
//
[<Property>]
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)