Enhance testing; add more generators; add testing for approxEqual

This commit is contained in:
varon 2017-03-19 15:35:46 +02:00
parent 38efb29af0
commit 79ec4c89a3
5 changed files with 177 additions and 43 deletions

View 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)

View 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

View 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))

View file

@ -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>

View file

@ -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)
let r = a / f
Assert.ApproximatelyEqual(a.X / f,r.X)
Assert.ApproximatelyEqual(a.Y / f,r.Y)