diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index e8ae5120..4c7f7b1b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "paket": { - "version": "8.0.3", + "version": "9.0.2", "commands": [ "paket" ] diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets index c66062b2..712cd771 100644 --- a/.paket/Paket.Restore.targets +++ b/.paket/Paket.Restore.targets @@ -235,14 +235,15 @@ $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',').Length) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0]) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1]) + $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[2]) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4]) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5]) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[6]) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[7]) $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[8]) - - %(PaketReferencesFileLinesInfo.PackageVersion) + + %(PaketReferencesFileLinesInfo.PackageVersion) All runtime $(ExcludeAssets);contentFiles @@ -252,6 +253,10 @@ true + + + %(PaketReferencesFileLinesInfo.PackageVersion) + diff --git a/Expecto.Tests/Tests.fs b/Expecto.Tests/Tests.fs index 48ea2336..2e85884b 100644 --- a/Expecto.Tests/Tests.fs +++ b/Expecto.Tests/Tests.fs @@ -6,6 +6,8 @@ open System.Text.RegularExpressions open System.Threading open System.IO open System.Reflection +open FSharp.Core.LanguagePrimitives +open FSharp.Data.UnitSystems.SI.UnitSymbols open Expecto open Expecto.Impl open Expecto.Logging @@ -921,42 +923,207 @@ let expecto = ] - testList "double" [ - testList "nan testing" [ - testCase "is not 'NaN'" <| fun _ -> - Expect.isNotNaN 4.0 "should pass because it's not 'Nan'" - testCase "is 'NaN'" (fun _ -> - Expect.isNotNaN Double.NaN "should fail because it's 'NaN'" - ) |> assertTestFails + testList "float" [ + testList "float" [ + testList "nan testing" [ + testList "is 'NaN'" [ + testCase "pass" <| fun _ -> + Expect.isNaN nan "should pass because it's 'NaN'" + testCase "fail" (fun _ -> + Expect.isNaN 4.0 "should fail because it's not 'NaN'" + ) |> assertTestFails + ] + testList "is not 'NaN'" [ + testCase "pass" <| fun _ -> + Expect.isNotNaN 4.0 "should pass because it's not 'NaN'" + testCase "fail" (fun _ -> + Expect.isNotNaN nan "should fail because it's 'NaN'" + ) |> assertTestFails + ] + ] + + testList "negative infinity testing" [ + testList "is a negative infinity" [ + testCase "pass" <| fun _ -> + Expect.isNegativeInfinity -infinity "should pass because it's a negative infinity" + testCase "fail infinity" (fun _ -> + Expect.isNegativeInfinity infinity "should fail because it's a not a negative infinity" + ) |> assertTestFails + testCase "fail 4.0" (fun _ -> + Expect.isNegativeInfinity 4.0 "should fail because it's not a negative infinity" + ) |> assertTestFails + ] + testList "is not a negative infinity" [ + testCase "pass infinity" <| fun _ -> + Expect.isNotNegativeInfinity infinity "should pass because it's not a negative infinity" + testCase "pass 4.0" <| fun _ -> + Expect.isNotNegativeInfinity 4.0 "should pass because it's not a negative infinity" + testCase "fail" (fun _ -> + Expect.isNotNegativeInfinity -infinity "should fail because it's negative infinity" + ) |> assertTestFails + ] + ] + + testList "infinity testing" [ + testList "is an infinity" [ + testCase "pass - negative infinity" <| fun _ -> + Expect.isInfinity infinity "should fail because it's negative infinity" + testCase "pass - positive infinity" <| fun _ -> + Expect.isInfinity infinity "should fail because it's positive infinity" + testCase "fail - 4.0" (fun _ -> + Expect.isInfinity 4.0 "should pass because it's not an negative infinity nor positive" + ) |> assertTestFails + ] + testList "is not an infinity" [ + testCase "pass - 4.0" <| fun _ -> + Expect.isNotInfinity 4.0 "should pass because it's not an negative infinity nor positive" + testCase "fail - negative infinity" (fun _ -> + Expect.isNotInfinity -infinity "should fail because it's negative infinity" + ) |> assertTestFails + testCase "fail - positive infinity" (fun _ -> + Expect.isNotInfinity infinity "should fail because it's positive infinity" + ) |> assertTestFails + ] + ] ] + testList "float" [ + testList "nan testing" [ + testList "is 'NaN'" [ + testCase "pass" <| fun _ -> + Expect.isNaN (FloatWithMeasure nan) "should pass because it's 'NaN'" + testCase "fail" (fun _ -> + Expect.isNaN 4.0 "should fail because it's not 'NaN'" + ) |> assertTestFails + ] + testList "is not 'NaN'" [ + testCase "pass" <| fun _ -> + Expect.isNotNaN 4.0 "should pass because it's not 'NaN'" + testCase "fail" (fun _ -> + Expect.isNotNaN nan "should fail because it's 'NaN'" + ) |> assertTestFails + ] + ] + + testList "negative infinity testing" [ + testList "is a negative infinity" [ + testCase "pass" <| fun _ -> + Expect.isNegativeInfinity (FloatWithMeasure -infinity) "should pass because it's a negative infinity" + testCase "fail infinity" (fun _ -> + Expect.isNegativeInfinity (FloatWithMeasure infinity) "should fail because it's a not a negative infinity" + ) |> assertTestFails + testCase "fail 4.0" (fun _ -> + Expect.isNegativeInfinity 4.0 "should fail because it's not a negative infinity" + ) |> assertTestFails + ] + testList "is not a negative infinity" [ + testCase "pass infinity" <| fun _ -> + Expect.isNotNegativeInfinity (FloatWithMeasure infinity) "should pass because it's not a negative infinity" + testCase "pass 4.0" <| fun _ -> + Expect.isNotNegativeInfinity 4.0 "should pass because it's not a negative infinity" + testCase "fail" (fun _ -> + Expect.isNotNegativeInfinity (FloatWithMeasure -infinity) "should fail because it's negative infinity" + ) |> assertTestFails + ] + ] - testList "positive infinity testing" [ - testCase "is not a positive infinity" <| fun _ -> - Expect.isNotPositiveInfinity 4.0 "should pass because it's not positive infinity" - testCase "is a positive infinity" (fun _ -> - Expect.isNotPositiveInfinity Double.PositiveInfinity "should fail because it's a positive infinity" - ) |> assertTestFails + testList "infinity testing" [ + testList "is an infinity" [ + testCase "pass - negative infinity" <| fun _ -> + Expect.isInfinity infinity "should fail because it's negative infinity" + testCase "pass - positive infinity" <| fun _ -> + Expect.isInfinity infinity "should fail because it's positive infinity" + testCase "fail - 4.0" (fun _ -> + Expect.isInfinity 4.0 "should pass because it's not an negative infinity nor positive" + ) |> assertTestFails + ] + testList "is not an infinity" [ + testCase "pass - 4.0" <| fun _ -> + Expect.isNotInfinity 4.0 "should pass because it's not an negative infinity nor positive" + testCase "fail - negative infinity" (fun _ -> + Expect.isNotInfinity -infinity "should fail because it's negative infinity" + ) |> assertTestFails + testCase "fail - positive infinity" (fun _ -> + Expect.isNotInfinity infinity "should fail because it's positive infinity" + ) |> assertTestFails + ] + ] ] - testList "negative infinity testing" [ - testCase "is not a negative infinity" <| fun _ -> - Expect.isNotNegativeInfinity 4.0 "should pass because it's not a negative infinity" - testCase "is a negative infinity" (fun _ -> - Expect.isNotNegativeInfinity Double.NegativeInfinity "should fail because it's negative infinity" - ) |> assertTestFails + testList "float32" [ + testList "nan testing" [ + testCase "is not 'NaN'" <| fun _ -> + Expect.isNotNaNf 4.0f "should pass because it's not 'NaNf'" + testCase "is 'NaN'" (fun _ -> + Expect.isNotNaNf Single.NaN "should fail because it's 'NaNf'" + ) |> assertTestFails + ] + + testList "positive infinity testing" [ + testCase "is not a positive infinity" <| fun _ -> + Expect.isNotPositiveInfinityf 4.0f "should pass because it's not positive infinity" + testCase "is a positive infinity" (fun _ -> + Expect.isNotPositiveInfinityf Single.PositiveInfinity "should fail because it's a positive infinityf" + ) |> assertTestFails + ] + + testList "negative infinity testing" [ + testCase "is not a negative infinity" <| fun _ -> + Expect.isNotNegativeInfinityf 4.0f "should pass because it's not a negative infinity" + testCase "is a negative infinity" (fun _ -> + Expect.isNotNegativeInfinityf Single.NegativeInfinity "should fail because it's negative infinityf" + ) |> assertTestFails + ] + + testList "infinity testing" [ + testCase "is not an infinity" <| fun _ -> + Expect.isNotInfinityf 4.0f "should pass because it's not an negative infinityf nor positive" + + testCase "is a negative infinity" (fun _ -> + Expect.isNotInfinityf Single.NegativeInfinity "should fail because it's negative infinityf" + ) |> assertTestFails + + testCase "is a positive infinity" (fun _ -> + Expect.isNotInfinityf Single.PositiveInfinity "should fail because it's positive infinityf" + ) |> assertTestFails + ] ] + testList "float32" [ + testList "nan testing" [ + testCase "is not 'NaN'" <| fun _ -> + Expect.isNotNaNf 4.0f "should pass because it's not 'NaNf'" + testCase "is 'NaN'" (fun _ -> + Expect.isNotNaNf (Float32WithMeasure Single.NaN) "should fail because it's 'NaNf'" + ) |> assertTestFails + ] + + testList "positive infinity testing" [ + testCase "is not a positive infinity" <| fun _ -> + Expect.isNotPositiveInfinityf 4.0f "should pass because it's not positive infinity" + testCase "is a positive infinity" (fun _ -> + Expect.isNotPositiveInfinityf (Float32WithMeasure Single.PositiveInfinity) "should fail because it's a positive infinityf" + ) |> assertTestFails + ] - testList "infinity testing" [ - testCase "is not an infinity" <| fun _ -> - Expect.isNotInfinity 4.0 "should pass because it's not an negative infinity nor positive" + testList "negative infinity testing" [ + testCase "is not a negative infinity" <| fun _ -> + Expect.isNotNegativeInfinityf 4.0f "should pass because it's not a negative infinity" + testCase "is a negative infinity" (fun _ -> + Expect.isNotNegativeInfinityf (Float32WithMeasure Single.NegativeInfinity) "should fail because it's negative infinityf" + ) |> assertTestFails + ] + + testList "infinity testing" [ + testCase "is not an infinity" <| fun _ -> + Expect.isNotInfinityf 4.0f "should pass because it's not an negative infinityf nor positive" - testCase "is a negative infinity" (fun _ -> - Expect.isNotInfinity Double.NegativeInfinity "should fail because it's negative infinity" - ) |> assertTestFails + testCase "is a negative infinity" (fun _ -> + Expect.isNotInfinityf (Float32WithMeasure Single.NegativeInfinity) "should fail because it's negative infinityf" + ) |> assertTestFails - testCase "is a positive infinity" (fun _ -> - Expect.isNotInfinity Double.PositiveInfinity "should fail because it's positive infinity" - ) |> assertTestFails + testCase "is a positive infinity" (fun _ -> + Expect.isNotInfinityf (Float32WithMeasure Single.PositiveInfinity) "should fail because it's positive infinityf" + ) |> assertTestFails + ] ] ] @@ -1560,52 +1727,190 @@ let performance = [] let close = testList "close" [ + testList "float" [ + testCase "zero" <| fun _ -> + Expect.floatClose Accuracy.veryHigh 0.0 0.0 "zero" + + testCase "small" <| fun _ -> + Expect.floatClose Accuracy.low 0.000001 0.0 "small" + + testCase "large" <| fun _ -> + Expect.floatClose Accuracy.low 10004.0 10000.0 "large" + + testCase "user" <| fun _ -> + Expect.floatClose {absolute=0.0; relative=1e-3} + 10004.0 10000.0 "user" + + testCase "can fail" (fun _ -> + Expect.floatClose Accuracy.low 1004.0 1000.0 "can fail" + ) |> assertTestFails + + testCase "nan fails" (fun _ -> + Expect.floatClose Accuracy.low nan 1.0 "nan fails" + ) |> assertTestFails + + testCase "inf fails" (fun _ -> + Expect.floatClose Accuracy.low infinity 1.0 "inf fails" + ) |> assertTestFails + + testCase "less than easy" <| fun _ -> + Expect.floatLessThanOrClose Accuracy.low -1.0 0.0 "less" + + testCase "not less than but close" <| fun _ -> + Expect.floatLessThanOrClose Accuracy.low 0.000001 0.0 "close" - testCase "zero" <| fun _ -> - Expect.floatClose Accuracy.veryHigh 0.0 0.0 "zero" + testCase "not less than fails" (fun _ -> + Expect.floatLessThanOrClose Accuracy.low 1.0 0.0 "fail" + ) |> assertTestFails + + testCase "greater than easy" <| fun _ -> + Expect.floatGreaterThanOrClose Accuracy.low 1.0 0.0 "greater" + + testCase "not greater than but close" <| fun _ -> + Expect.floatGreaterThanOrClose Accuracy.low -0.000001 0.0 "close" + + testCase "not greater than fails" (fun _ -> + Expect.floatGreaterThanOrClose Accuracy.low -1.0 0.0 "fail" + ) |> assertTestFails + ] + testList "float" [ + testCase "zero" <| fun _ -> + Expect.floatClose Accuracy.veryHigh 0.0 0.0 "zero" + + testCase "small" <| fun _ -> + Expect.floatClose Accuracy.low 0.000001 0.0 "small" + + testCase "large" <| fun _ -> + Expect.floatClose Accuracy.low 10004.0 10000.0 "large" + + testCase "user" <| fun _ -> + Expect.floatClose {absolute=0.0; relative=1e-3} + 10004.0 10000.0 "user" + + testCase "can fail" (fun _ -> + Expect.floatClose Accuracy.low 1004.0 1000.0 "can fail" + ) |> assertTestFails + + testCase "nan fails" (fun _ -> + Expect.floatClose Accuracy.low (FloatWithMeasure nan) 1.0 "nan fails" + ) |> assertTestFails + + testCase "inf fails" (fun _ -> + Expect.floatClose Accuracy.low (FloatWithMeasure infinity) 1.0 "inf fails" + ) |> assertTestFails - testCase "small" <| fun _ -> - Expect.floatClose Accuracy.low 0.000001 0.0 "small" + testCase "less than easy" <| fun _ -> + Expect.floatLessThanOrClose Accuracy.low -1.0 0.0 "less" + + testCase "not less than but close" <| fun _ -> + Expect.floatLessThanOrClose Accuracy.low 0.000001 0.0 "close" + + testCase "not less than fails" (fun _ -> + Expect.floatLessThanOrClose Accuracy.low 1.0 0.0 "fail" + ) |> assertTestFails - testCase "large" <| fun _ -> - Expect.floatClose Accuracy.low 10004.0 10000.0 "large" + testCase "greater than easy" <| fun _ -> + Expect.floatGreaterThanOrClose Accuracy.low 1.0 0.0 "greater" - testCase "user" <| fun _ -> - Expect.floatClose {absolute=0.0; relative=1e-3} - 10004.0 10000.0 "user" + testCase "not greater than but close" <| fun _ -> + Expect.floatGreaterThanOrClose Accuracy.low -0.000001 0.0 "close" - testCase "can fail" (fun _ -> - Expect.floatClose Accuracy.low 1004.0 1000.0 "can fail" - ) |> assertTestFails + testCase "not greater than fails" (fun _ -> + Expect.floatGreaterThanOrClose Accuracy.low -1.0 0.0 "fail" + ) |> assertTestFails + ] + testList "float32" [ + testCase "zero" <| fun _ -> + Expect.float32Close Accuracy32.veryHigh 0.0f 0.0f "zero" - testCase "nan fails" (fun _ -> - Expect.floatClose Accuracy.low nan 1.0 "nan fails" - ) |> assertTestFails + testCase "small" <| fun _ -> + Expect.float32Close Accuracy32.low 0.000001f 0.0f "small" - testCase "inf fails" (fun _ -> - Expect.floatClose Accuracy.low infinity 1.0 "inf fails" - ) |> assertTestFails + testCase "large" <| fun _ -> + Expect.float32Close Accuracy32.low 10004.0f 10000.0f "large" - testCase "less than easy" <| fun _ -> - Expect.floatLessThanOrClose Accuracy.low -1.0 0.0 "less" + testCase "user" <| fun _ -> + Expect.float32Close {absolute=0.0f; relative=1e-3f} + 10004.0f 10000.0f "user" - testCase "not less than but close" <| fun _ -> - Expect.floatLessThanOrClose Accuracy.low 0.000001 0.0 "close" + testCase "can fail" (fun _ -> + Expect.float32Close Accuracy32.low 1004.0f 1000.0f "can fail" + ) |> assertTestFails - testCase "not less than fails" (fun _ -> - Expect.floatLessThanOrClose Accuracy.low 1.0 0.0 "fail" - ) |> assertTestFails + testCase "nan fails" (fun _ -> + Expect.float32Close Accuracy32.low nanf 1.0f "nanf fails" + ) |> assertTestFails - testCase "greater than easy" <| fun _ -> - Expect.floatGreaterThanOrClose Accuracy.low 1.0 0.0 "greater" + testCase "inf fails" (fun _ -> + Expect.float32Close Accuracy32.low infinityf 1.0f "infinityf fails" + ) |> assertTestFails - testCase "not greater than but close" <| fun _ -> - Expect.floatGreaterThanOrClose Accuracy.low -0.000001 0.0 "close" + testCase "less than easy" <| fun _ -> + Expect.float32LessThanOrClose Accuracy32.low -1.0f 0.0f "less" - testCase "not greater than fails" (fun _ -> - Expect.floatGreaterThanOrClose Accuracy.low -1.0 0.0 "fail" - ) |> assertTestFails + testCase "not less than but close" <| fun _ -> + Expect.float32LessThanOrClose Accuracy32.low 0.000001f 0.0f "close" + testCase "not less than fails" (fun _ -> + Expect.float32LessThanOrClose Accuracy32.low 1.0f 0.0f "fail" + ) |> assertTestFails + + testCase "greater than easy" <| fun _ -> + Expect.float32GreaterThanOrClose Accuracy32.low 1.0f 0.0f "greater" + + testCase "not greater than but close" <| fun _ -> + Expect.float32GreaterThanOrClose Accuracy32.low -0.000001f 0.0f "close" + + testCase "not greater than fails" (fun _ -> + Expect.float32GreaterThanOrClose Accuracy32.low -1.0f 0.0f "fail" + ) |> assertTestFails + ] + testList "float32" [ + testCase "zero" <| fun _ -> + Expect.float32Close Accuracy32.veryHigh 0.0f 0.0f "zero" + + testCase "small" <| fun _ -> + Expect.float32Close Accuracy32.low 0.000001f 0.0f "small" + + testCase "large" <| fun _ -> + Expect.float32Close Accuracy32.low 10004.0f 10000.0f "large" + + testCase "user" <| fun _ -> + Expect.float32Close {absolute=0.0f; relative=1e-3f} + 10004.0f 10000.0f "user" + + testCase "can fail" (fun _ -> + Expect.float32Close Accuracy32.low 1004.0f 1000.0f "can fail" + ) |> assertTestFails + + testCase "nan fails" (fun _ -> + Expect.float32Close Accuracy32.low (Float32WithMeasure nanf) 1.0f "nanf fails" + ) |> assertTestFails + + testCase "inf fails" (fun _ -> + Expect.float32Close Accuracy32.low (Float32WithMeasure infinityf) 1.0f "infinityf fails" + ) |> assertTestFails + + testCase "less than easy" <| fun _ -> + Expect.float32LessThanOrClose Accuracy32.low -1.0f 0.0f "less" + + testCase "not less than but close" <| fun _ -> + Expect.float32LessThanOrClose Accuracy32.low 0.000001f 0.0f "close" + + testCase "not less than fails" (fun _ -> + Expect.float32LessThanOrClose Accuracy32.low 1.0f 0.0f "fail" + ) |> assertTestFails + + testCase "greater than easy" <| fun _ -> + Expect.float32GreaterThanOrClose Accuracy32.low 1.0f 0.0f "greater" + + testCase "not greater than but close" <| fun _ -> + Expect.float32GreaterThanOrClose Accuracy32.low -0.000001f 0.0f "close" + + testCase "not greater than fails" (fun _ -> + Expect.float32GreaterThanOrClose Accuracy32.low -1.0f 0.0f "fail" + ) |> assertTestFails + ] ] [] diff --git a/Expecto/Expect.fs b/Expecto/Expect.fs index 2177ddc3..6abc092a 100644 --- a/Expecto/Expect.fs +++ b/Expecto/Expect.fs @@ -276,10 +276,24 @@ let floatEqual actual expected epsilon message = /// Expects `actual` and `expected` (that are both floats) to be within a /// given `accuracy`. -let floatClose accuracy actual expected message = - if Double.IsInfinity actual then +let floatClose (accuracy: Accuracy<'u>) (actual: float<'u>) (expected: float<'u>) (message: string) : unit = + if Double.IsInfinity (float actual) then failtestf "%s. Expected actual to not be infinity, but it was." message - elif Double.IsInfinity expected then + elif Double.IsInfinity (float expected) then + failtestf "%s. Expected expected to not be infinity, but it was." message + elif Accuracy.areClose accuracy actual expected |> not then + failtestf + "%s. Expected difference to be less than %.20g for accuracy {absolute=%.20g; relative=%.20g}, but was %.20g. actual=%.20g expected=%.20g" + message (Accuracy.areCloseRhs accuracy actual expected) + accuracy.absolute accuracy.relative + (Accuracy.areCloseLhs actual expected) + actual expected +/// Expects `actual` and `expected` (that are both float32s) to be within a +/// given `accuracy`. +let float32Close (accuracy: Accuracy32<'u>) (actual: float32<'u>) (expected: float32<'u>) message = + if Single.IsInfinity (float32 actual) then + failtestf "%s. Expected actual to not be infinity, but it was." message + elif Single.IsInfinity (float32 expected) then failtestf "%s. Expected expected to not be infinity, but it was." message elif Accuracy.areClose accuracy actual expected |> not then failtestf @@ -292,22 +306,75 @@ let floatClose accuracy actual expected message = /// given `accuracy`. let floatLessThanOrClose accuracy actual expected message = if actual>expected then floatClose accuracy actual expected message +/// Expects `actual` to be less than `expected` or to be within a +/// given `accuracy`. +let float32LessThanOrClose accuracy actual expected message = + if actual>expected then float32Close accuracy actual expected message /// Expects `actual` to be greater than `expected` or to be within a /// given `accuracy`. let floatGreaterThanOrClose accuracy actual expected message = if actual) message = + if not (Double.IsNaN (float f)) then failtestf "%s. Float should be a NaN (not a number) value." message + +/// Expect the passed float to not be a number. +let isNaNf (f: float32<'u>) message = + if not (Single.IsNaN (float32 f)) then failtestf "%s. Float should be a NaN (not a number) value." message + +/// Expect the passed float to be a number. +let isNotNaN (f: float<'u>) message = + if Double.IsNaN (float f) then failtestf "%s. Float was the NaN (not a number) value." message /// Expect the passed float to be a number. -let isNotNaN f message = - if Double.IsNaN f then failtestf "%s. Float was the NaN (not a number) value." message +let isNotNaNf (f: float32<'u>) message = + if Single.IsNaN (float32 f) then failtestf "%s. Float was the NaN (not a number) value." message + /// Expect the passed float not to be positive infinity. -let isNotPositiveInfinity actual message = - if Double.IsPositiveInfinity actual then failtestf "%s. Float was positive infinity." message +let isPositiveInfinity (actual: float<'u>) message = + if not (Double.IsPositiveInfinity (float actual)) then failtestf "%s. Float should be positive infinity." message + +/// Expect the passed float not to be positive infinity. +let isPositiveInfinityf (actual: float32<'u>) message = + if not (Single.IsPositiveInfinity (float32 actual)) then failtestf "%s. Float should be positive infinity." message /// Expect the passed float not to be negative infinity. -let isNotNegativeInfinity actual message = - if Double.IsNegativeInfinity actual then failtestf "%s. Float was negative infinity." message +let isNegativeInfinity (actual: float<'u>) message = + if not (Double.IsNegativeInfinity (float actual)) then failtestf "%s. Float should be negative infinity." message + +/// Expect the passed float not to be negative infinity. +let isNegativeInfinityf (actual: float32<'u>) message = + if not (Single.IsNegativeInfinity (float32 actual)) then failtestf "%s. Float should be negative infinity." message + +/// Expect the passed float not to be infinity. +let isInfinity (actual: float<'u>) message = + if not (Double.IsInfinity (float actual)) then failtestf "%s. Float should be infinity." message + +/// Expect the passed float not to be infinity. +let isInfinityf (actual: float32<'u>) message = + if not (Single.IsInfinity (float32 actual)) then failtestf "%s. Float should be infinity." message + +/// Expect the passed float not to be positive infinity. +let isNotPositiveInfinity (actual: float<'u>) message = + if Double.IsPositiveInfinity (float actual) then failtestf "%s. Float was positive infinity." message + +/// Expect the passed float not to be positive infinity. +let isNotPositiveInfinityf (actual: float32<'u>) message = + if Single.IsPositiveInfinity (float32 actual) then failtestf "%s. Float was positive infinity." message + +/// Expect the passed float not to be negative infinity. +let isNotNegativeInfinity (actual: float<'u>) message = + if Double.IsNegativeInfinity (float actual) then failtestf "%s. Float was negative infinity." message + +/// Expect the passed float not to be negative infinity. +let isNotNegativeInfinityf (actual: float32<'u>) message = + if Single.IsNegativeInfinity (float32 actual) then failtestf "%s. Float was negative infinity." message /// Expect the passed float not to be infinity. let isNotInfinity actual message = @@ -315,6 +382,12 @@ let isNotInfinity actual message = isNotPositiveInfinity actual message // passed via excluded middle +/// Expect the passed float not to be infinity. +let isNotInfinityf actual message = + isNotNegativeInfinityf actual message + isNotPositiveInfinityf actual message + // passed via excluded middle + /// Expect the passed string not to be empty. let isNotEmpty (actual : string) message = isNotNull actual message diff --git a/Expecto/Flip.Expect.fs b/Expecto/Flip.Expect.fs index 1e33a17f..019d61cf 100644 --- a/Expecto/Flip.Expect.fs +++ b/Expecto/Flip.Expect.fs @@ -72,26 +72,50 @@ let inline isGreaterThanOrEqual message (a, b) = Expecto.Expect.isGreaterThanOrE /// given `accuracy`. let inline floatClose message accuracy expected actual = Expecto.Expect.floatClose accuracy actual expected message +/// Expects `actual` and `expected` (that are both floats) to be within a +/// given `accuracy`. +let inline float32Close message accuracy expected actual = Expecto.Expect.float32Close accuracy actual expected message + /// Expects `actual` to be less than `expected` or to be within a /// given `accuracy`. let inline floatLessThanOrClose message accuracy expected actual = Expecto.Expect.floatLessThanOrClose accuracy actual expected message +/// Expects `actual` to be less than `expected` or to be within a +/// given `accuracy`. +let inline float32LessThanOrClose message accuracy expected actual = Expecto.Expect.float32LessThanOrClose accuracy actual expected message + /// Expects `actual` to be greater than `expected` or to be within a /// given `accuracy`. let inline floatGreaterThanOrClose message accuracy expected actual = Expecto.Expect.floatGreaterThanOrClose accuracy actual expected message +/// Expects `actual` to be greater than `expected` or to be within a +/// given `accuracy`. +let inline float32GreaterThanOrClose message accuracy expected actual = Expecto.Expect.float32GreaterThanOrClose accuracy actual expected message + /// Expect the passed float to be a number. let inline isNotNaN message f = Expecto.Expect.isNotNaN f message +/// Expect the passed float to be a number. +let inline isNotNaNf message f = Expecto.Expect.isNotNaNf f message + /// Expect the passed float not to be positive infinity. let inline isNotPositiveInfinity message f = Expecto.Expect.isNotPositiveInfinity f message +/// Expect the passed float not to be positive infinity. +let inline isNotPositiveInfinityf message f = Expecto.Expect.isNotPositiveInfinityf f message + /// Expect the passed float not to be negative infinity. let inline isNotNegativeInfinity message f = Expecto.Expect.isNotNegativeInfinity f message +/// Expect the passed float not to be negative infinity. +let inline isNotNegativeInfinityf message f = Expecto.Expect.isNotNegativeInfinityf f message + /// Expect the passed float not to be infinity. let inline isNotInfinity message f = Expecto.Expect.isNotInfinity f message +/// Expect the passed float not to be infinity. +let inline isNotInfinityf message f = Expecto.Expect.isNotInfinityf f message + /// Expect the passed string not to be empty. let inline isNotEmpty message actual = Expecto.Expect.isNotEmpty actual message diff --git a/Expecto/Performance.fs b/Expecto/Performance.fs index f640c351..db1f6803 100644 --- a/Expecto/Performance.fs +++ b/Expecto/Performance.fs @@ -3,17 +3,35 @@ namespace Expecto open System open Expecto.Logging open Expecto.Logging.Message +open FSharp.Core.LanguagePrimitives -type Accuracy = { absolute: float; relative: float } +type Accuracy<'abs, 'rel> = { absolute: 'abs; relative: 'rel } + +type Accuracy<[] 'u> = Accuracy, float> +type Accuracy = Accuracy<1> +type Accuracy32<[] 'u> = Accuracy, float32> +type Accuracy32 = Accuracy32<1> module Accuracy = - let inline areCloseLhs a b = abs(a-b) - let inline areCloseRhs m a b = m.absolute + m.relative * max (abs a) (abs b) - let inline areClose m a b = areCloseLhs a b <= areCloseRhs m a b - let low = {absolute=1e-6; relative=1e-3} - let medium = {absolute=1e-8; relative=1e-5} - let high = {absolute=1e-10; relative=1e-7} - let veryHigh = {absolute=1e-12; relative=1e-9} + let inline areCloseLhs a b : ^a = abs(a-b) + let inline areCloseRhs (m: Accuracy<'a,_>) (a: 'a) (b: 'a) : 'a = m.absolute + m.relative * max (abs a) (abs b) + let inline areClose (m: Accuracy<'a,_>) (a: 'a) (b: 'a) : bool = areCloseLhs a b <= areCloseRhs m a b + + let low<[] 'u> : Accuracy<'u> = { absolute = FloatWithMeasure 1e-6; relative = 1e-3 } + let medium<[] 'u> : Accuracy<'u> = { absolute = FloatWithMeasure 1e-8; relative = 1e-5 } + let mediumf<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-8f; relative = 1e-5f } + let high<[] 'u> : Accuracy<'u> = { absolute = FloatWithMeasure 1e-10; relative = 1e-7 } + let highf<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-10f; relative = 1e-7f } + let veryHigh<[] 'u> : Accuracy<'u> = { absolute = FloatWithMeasure 1e-12; relative = 1e-9 } + let veryHighf<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-12f; relative = 1e-9f } + +module Accuracy32 = + open Accuracy + + let low<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-6f; relative = 1e-3f } + let medium<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-8f; relative = 1e-5f } + let high<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-10f; relative = 1e-7f } + let veryHigh<[] 'u> : Accuracy32<'u> = { absolute = Float32WithMeasure 1e-12f; relative = 1e-9f } module Performance = open Statistics