Enhance testing; add more generators; add testing for approxEqual
This commit is contained in:
parent
38efb29af0
commit
79ec4c89a3
5 changed files with 177 additions and 43 deletions
31
tests/OpenTK.Tests/Assertions.fs
Normal file
31
tests/OpenTK.Tests/Assertions.fs
Normal file
|
@ -0,0 +1,31 @@
|
|||
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)
|
76
tests/OpenTK.Tests/Generators.fs
Normal file
76
tests/OpenTK.Tests/Generators.fs
Normal file
|
@ -0,0 +1,76 @@
|
|||
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
|
55
tests/OpenTK.Tests/MathHelper.fs
Normal file
55
tests/OpenTK.Tests/MathHelper.fs
Normal file
|
@ -0,0 +1,55 @@
|
|||
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))
|
|
@ -1483,6 +1483,9 @@
|
|||
<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>
|
||||
|
|
|
@ -6,38 +6,8 @@ open FsCheck.Xunit
|
|||
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>]
|
||||
|
@ -50,28 +20,28 @@ module Vector2 =
|
|||
//
|
||||
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>]
|
||||
|
@ -92,7 +62,7 @@ module Vector2 =
|
|||
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>]
|
||||
|
@ -101,12 +71,11 @@ module Vector2 =
|
|||
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)
|
||||
Assert.ApproximatelyEqual(a.X / f,r.X)
|
||||
Assert.ApproximatelyEqual(a.Y / f,r.Y)
|
||||
|
|
Loading…
Reference in a new issue