diff --git a/tests/FSharp.Collections.Immutable.Tests/Assert.fs b/tests/FSharp.Collections.Immutable.Tests/Assert.fs
new file mode 100644
index 00000000..17b27fe3
--- /dev/null
+++ b/tests/FSharp.Collections.Immutable.Tests/Assert.fs
@@ -0,0 +1,134 @@
+///
+/// Assertions on F# ,
+/// and
+/// values as extensions of .
+///
+/// The assertions whose names start with Want return the unwrapped value or fail the test; their Is
+/// twins discard the value.
+///
+///
+[]
+module FSharp.Collections.Immutable.Tests.AssertExtensions
+
+open System.Runtime.InteropServices
+open Microsoft.VisualStudio.TestTools.UnitTesting
+
+// MSTest takes the message of a failure as a text that is not null, while an optional message that the caller
+// omitted is null.
+let private messageOrEmpty (message : string | null) : string =
+ match message with
+ | null -> ""
+ | message -> message
+
+type Assert with
+
+ ///
+ /// Returns the value of ; fails the test with
+ /// on .
+ ///
+ static member WantSome (value, [] message : string | null) =
+ match value with
+ | Some some -> some
+ | None ->
+ Assert.Fail (messageOrEmpty message)
+ Unchecked.defaultof<_>
+
+ ///
+ /// Fails the test with unless is
+ /// .
+ ///
+ static member IsSome (value, [] message : string | null) = Assert.WantSome (value, message) |> ignore
+
+ ///
+ /// Fails the test with unless is
+ /// .
+ ///
+ static member IsNone (value, [] message : string | null) =
+ match value with
+ | Some _ -> Assert.Fail (messageOrEmpty message)
+ | None -> ()
+
+ ///
+ /// Returns the value of ; fails the test
+ /// with on .
+ ///
+ static member WantValueSome (value, [] message : string | null) =
+ match value with
+ | ValueSome some -> some
+ | ValueNone ->
+ Assert.Fail (messageOrEmpty message)
+ Unchecked.defaultof<_>
+
+ ///
+ /// Fails the test with unless is
+ /// .
+ ///
+ static member IsValueSome (value, [] message : string | null) = Assert.WantValueSome (value, message) |> ignore
+
+ ///
+ /// Fails the test with unless is
+ /// .
+ ///
+ static member IsValueNone (value, [] message : string | null) =
+ match value with
+ | ValueSome _ -> Assert.Fail (messageOrEmpty message)
+ | ValueNone -> ()
+
+ ///
+ /// Returns the value of ; fails the test with
+ /// and the error on .
+ ///
+ static member WantOk (value, [] message : string | null) =
+ match value with
+ | Ok ok -> ok
+ | Error error ->
+ match message with
+ | null -> Assert.Fail (string error)
+ | message -> Assert.Fail ($"'{message}': {error}")
+ Unchecked.defaultof<_>
+
+ ///
+ /// Fails the test with unless is
+ /// .
+ ///
+ static member IsOk (value, [] message : string | null) = Assert.WantOk (value, message) |> ignore
+
+ ///
+ /// Returns the error of ; fails the test with
+ /// and the value on .
+ ///
+ static member WantError (value, [] message : string | null) =
+ match value with
+ | Error error -> error
+ | Ok value ->
+ match message with
+ | null -> Assert.Fail (string value)
+ | message -> Assert.Fail ($"'{message}': {value}")
+ Unchecked.defaultof<_>
+
+ ///
+ /// Fails the test with unless is
+ /// .
+ ///
+ static member IsError (value, [] message : string | null) = Assert.WantError (value, message) |> ignore
+
+ ///
+ /// Fails the test with unless is the default value of its
+ /// type.
+ ///
+ static member inline IsDefaultOf< ^T> (value : ^T, [] message : string) =
+ Assert.AreEqual (box value, box Unchecked.defaultof< ^T>, message)
+
+ ///
+ /// Fails the test with unless is
+ /// holding .
+ ///
+ static member inline OkEquals< ^R, 'E> (expected : ^R, actual : Result< ^R, 'E >, [] message : string | null) =
+ Assert.AreEqual (box expected, box (Assert.WantOk (actual, message)), message)
+
+ ///
+ /// Fails the test with unless is
+ /// holding .
+ ///
+ static member inline ErrorEquals<'R, ^E> (expected : ^E, actual : Result<'R, ^E>, [] message : string | null) =
+ Assert.AreEqual (box expected, box (Assert.WantError (actual, message)), message)
diff --git a/tests/FSharp.Collections.Immutable.Tests/AssertExtensionsTests.fs b/tests/FSharp.Collections.Immutable.Tests/AssertExtensionsTests.fs
new file mode 100644
index 00000000..571324da
--- /dev/null
+++ b/tests/FSharp.Collections.Immutable.Tests/AssertExtensionsTests.fs
@@ -0,0 +1,156 @@
+namespace FSharp.Collections.Immutable.Tests
+
+open System
+open Microsoft.VisualStudio.TestTools.UnitTesting
+
+///
+/// The assertions on options, value options and results that extend
+/// .
+///
+[]
+type AssertExtensionsTests () =
+
+ /// The message of the failure that an assertion raises.
+ static let failureOf (assertion : unit -> unit) : string =
+ let failure = Assert.Throws(Action assertion, "The assertion must fail")
+ failure.Message
+
+ static let assertContains (failure : string) (expected : string) (message : string) =
+ StringAssert.Contains (failure, expected, StringComparison.Ordinal, message)
+
+ // The empty option and value option of a known type: a type annotation cannot sit in a tuple of arguments.
+ static let noInt : int option = None
+ static let noValueInt : int voption = ValueNone
+
+ // ---- option ----
+
+ []
+ member _.``WantSome returns the value of Some`` () =
+ Assert.AreEqual(5, Assert.WantSome (Some 5, "message"), "WantSome must return the value of Some")
+
+ []
+ member _.``WantSome fails with the message on None`` () =
+ let failure = failureOf (fun () -> Assert.WantSome (noInt, "the value is missing") |> ignore)
+ assertContains failure "the value is missing" "WantSome must fail with its message on None"
+
+ []
+ member _.``IsSome passes for Some and fails for None`` () =
+ Assert.IsSome (Some 5, "message")
+ let failure = failureOf (fun () -> Assert.IsSome (noInt, "the value is missing"))
+ assertContains failure "the value is missing" "IsSome must fail with its message on None"
+
+ []
+ member _.``IsNone passes for None and fails for Some`` () =
+ Assert.IsNone (noInt, "message")
+ let failure = failureOf (fun () -> Assert.IsNone (Some 5, "the value is there"))
+ assertContains failure "the value is there" "IsNone must fail with its message on Some"
+
+ // ---- voption ----
+
+ []
+ member _.``WantValueSome returns the value of ValueSome`` () =
+ Assert.AreEqual(5, Assert.WantValueSome (ValueSome 5, "message"), "WantValueSome must return the value")
+
+ []
+ member _.``WantValueSome fails with the message on ValueNone`` () =
+ let failure =
+ failureOf (fun () ->
+ Assert.WantValueSome (noValueInt, "the value is missing")
+ |> ignore
+ )
+
+ assertContains failure "the value is missing" "WantValueSome must fail with its message on ValueNone"
+
+ []
+ member _.``IsValueSome passes for ValueSome and fails for ValueNone`` () =
+ Assert.IsValueSome (ValueSome 5, "message")
+ let failure = failureOf (fun () -> Assert.IsValueSome (noValueInt, "the value is missing"))
+ assertContains failure "the value is missing" "IsValueSome must fail with its message on ValueNone"
+
+ []
+ member _.``IsValueNone passes for ValueNone and fails for ValueSome`` () =
+ Assert.IsValueNone (noValueInt, "message")
+ let failure = failureOf (fun () -> Assert.IsValueNone (ValueSome 5, "the value is there"))
+ assertContains failure "the value is there" "IsValueNone must fail with its message on ValueSome"
+
+ // ---- result ----
+
+ []
+ member _.``WantOk returns the value of Ok`` () =
+ let result : Result = Ok 5
+ Assert.AreEqual(5, Assert.WantOk (result, "message"), "WantOk must return the value of Ok")
+
+ []
+ member _.``WantOk fails with the message and the error on Error`` () =
+ let result : Result = Error "the error"
+ let failure = failureOf (fun () -> Assert.WantOk (result, "the call must succeed") |> ignore)
+ assertContains failure "the call must succeed" "WantOk must fail with its message on Error"
+ assertContains failure "the error" "WantOk must fail with the error"
+
+ []
+ member _.``WantOk fails with the error alone when it has no message`` () =
+ let result : Result = Error "the error"
+ let failure = failureOf (fun () -> Assert.WantOk (result) |> ignore)
+ assertContains failure "the error" "WantOk must fail with the error"
+
+ []
+ member _.``IsOk passes for Ok and fails for Error`` () =
+ let ok : Result = Ok 5
+ let error : Result = Error "the error"
+ Assert.IsOk (ok, "message")
+ let failure = failureOf (fun () -> Assert.IsOk (error, "the call must succeed"))
+ assertContains failure "the call must succeed" "IsOk must fail with its message on Error"
+
+ []
+ member _.``WantError returns the error of Error`` () =
+ let result : Result = Error "the error"
+ Assert.AreEqual("the error", Assert.WantError (result, "message"), "WantError must return the error")
+
+ []
+ member _.``WantError fails with the message and the value on Ok`` () =
+ let result : Result = Ok 5
+ let failure = failureOf (fun () -> Assert.WantError (result, "the call must fail") |> ignore)
+ assertContains failure "the call must fail" "WantError must fail with its message on Ok"
+ assertContains failure "5" "WantError must fail with the value"
+
+ []
+ member _.``IsError passes for Error and fails for Ok`` () =
+ let ok : Result = Ok 5
+ let error : Result = Error "the error"
+ Assert.IsError (error, "message")
+ let failure = failureOf (fun () -> Assert.IsError (ok, "the call must fail"))
+ assertContains failure "the call must fail" "IsError must fail with its message on Ok"
+
+ []
+ member _.``OkEquals passes for the expected value and fails for another value or for Error`` () =
+ let ok : Result = Ok 5
+ Assert.OkEquals (5, ok, "message")
+ let other : Result = Ok 6
+ let different = failureOf (fun () -> Assert.OkEquals (5, other, "the value must be 5"))
+ assertContains different "the value must be 5" "OkEquals must fail with its message on another value"
+ let error : Result = Error "the error"
+ let failure = failureOf (fun () -> Assert.OkEquals (5, error, "the call must succeed"))
+ assertContains failure "the error" "OkEquals must fail with the error on Error"
+
+ []
+ member _.``ErrorEquals passes for the expected error and fails for another error or for Ok`` () =
+ let error : Result = Error "the error"
+ Assert.ErrorEquals ("the error", error, "message")
+ let other : Result = Error "another error"
+ let different =
+ failureOf (fun () -> Assert.ErrorEquals ("the error", other, "the error must be the expected one"))
+ assertContains different "the error must be the expected one" "ErrorEquals must fail with its message on another error"
+ let ok : Result = Ok 5
+ let failure = failureOf (fun () -> Assert.ErrorEquals ("the error", ok, "the call must fail"))
+ assertContains failure "5" "ErrorEquals must fail with the value on Ok"
+
+ // ---- default values ----
+
+ []
+ member _.``IsDefaultOf passes for the default value and fails for another one`` () =
+ Assert.IsDefaultOf (0, "message")
+ Assert.IsDefaultOf (noInt, "message")
+ let number = failureOf (fun () -> Assert.IsDefaultOf (1, "the number must be the default"))
+ assertContains number "the number must be the default" "IsDefaultOf must fail with its message on a non-default number"
+ let option = failureOf (fun () -> Assert.IsDefaultOf (Some 1, "the option must be the default"))
+ assertContains option "the option must be the default" "IsDefaultOf must fail with its message on a non-default option"
diff --git a/tests/FSharp.Collections.Immutable.Tests/Contracts/SeqLike/SeqLikeAccessContract.fs b/tests/FSharp.Collections.Immutable.Tests/Contracts/SeqLike/SeqLikeAccessContract.fs
index 8fee7d72..8f9ba026 100644
--- a/tests/FSharp.Collections.Immutable.Tests/Contracts/SeqLike/SeqLikeAccessContract.fs
+++ b/tests/FSharp.Collections.Immutable.Tests/Contracts/SeqLike/SeqLikeAccessContract.fs
@@ -150,18 +150,15 @@ type SeqLikeAccessContract<'C> (ops : SeqOps<'C>) =
let struct (collection, elements) = this.Create input
let tail = ops.TryTail collection
- Assert.AreEqual(
- not (Array.isEmpty elements),
- tail.IsSome,
- $"%s{ops.Name}.tryTail must return a tail exactly for a collection with elements"
- )
+ if Array.isEmpty elements then
+ Assert.IsValueNone (tail, $"%s{ops.Name}.tryTail must return nothing for an empty collection")
+ else
+ let tail =
+ Assert.WantValueSome (tail, $"%s{ops.Name}.tryTail must return a tail for a collection with elements")
- // CollectionAssert compares the elements of the tail and reports the first one that differs
- match tail with
- | ValueSome tail ->
+ // CollectionAssert compares the elements of the tail and reports the first one that differs
CollectionAssert.AreEqual (
Array.tail elements,
ops.ToArray tail,
$"%s{ops.Name}.tryTail must return the elements after the first one"
)
- | ValueNone -> ()
diff --git a/tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj b/tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj
index 42889292..ff9cc792 100644
--- a/tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj
+++ b/tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj
@@ -30,7 +30,9 @@
+
+
diff --git a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListBuilderTests.fs b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListBuilderTests.fs
index b9e80eb2..5a1f07bc 100644
--- a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListBuilderTests.fs
+++ b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListBuilderTests.fs
@@ -599,8 +599,10 @@ type FlatListBuilderTests () =
FlatListBuilder.replace oldValue 99 builder |> ignore
CollectionAssert.AreEqual (
- FlatListOracles.replaceFirst (=) oldValue 99 input
- |> ValueOption.get,
+ Assert.WantValueSome (
+ FlatListOracles.replaceFirst (=) oldValue 99 input,
+ "The test data must contain the value to replace"
+ ),
Seq.toArray builder,
"FlatListBuilder.replace must replace the first equal element"
)
@@ -626,8 +628,10 @@ type FlatListBuilderTests () =
|> ignore
CollectionAssert.AreEqual (
- FlatListOracles.replaceFirst FlatListOracles.equalIgnoringCase oldValue "X" names
- |> ValueOption.get,
+ Assert.WantValueSome (
+ FlatListOracles.replaceFirst FlatListOracles.equalIgnoringCase oldValue "X" names,
+ "The test data must contain a string that the comparer finds equal"
+ ),
Seq.toArray builder,
"FlatListBuilder.replaceWith must replace the first element that the comparer finds equal"
)
diff --git a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOptionTests.fs b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOptionTests.fs
index 17554b8b..dfb1a9cb 100644
--- a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOptionTests.fs
+++ b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOptionTests.fs
@@ -67,19 +67,21 @@ type FlatListOptionTests () =
[]
member _.``tryTail returns an option`` () =
- let expected = FSharp.Collections.Immutable.FlatList.tryTail array
+ let expected =
+ Assert.WantValueSome (
+ FSharp.Collections.Immutable.FlatList.tryTail array,
+ "FlatList.tryTail must return a tail for a flat list with elements"
+ )
+
let found : FlatList option = FlatList.tryTail array
- Assert.AreEqual(expected.IsSome, found.IsSome, "Option.FlatList.tryTail must return a tail when the default does")
+ let tail = Assert.WantSome (found, "Option.FlatList.tryTail must return a tail when the default does")
// The tails are compared element by element
- match expected, found with
- | ValueSome expected, Some found ->
- CollectionAssert.AreEqual (
- FlatList.toArray expected,
- FlatList.toArray found,
- "Option.FlatList.tryTail must return the tail of the default"
- )
- | _ -> ()
+ CollectionAssert.AreEqual (
+ FlatList.toArray expected,
+ FlatList.toArray tail,
+ "Option.FlatList.tryTail must return the tail of the default"
+ )
[]
member _.``pick takes a chooser of options`` () =
diff --git a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOracles.fs b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOracles.fs
index e55f5207..4ce92320 100644
--- a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOracles.fs
+++ b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListOracles.fs
@@ -4,6 +4,7 @@ module FSharp.Collections.Immutable.Tests.FlatListOracles
open System
open System.Collections.Generic
+open Microsoft.VisualStudio.TestTools.UnitTesting
open FSharp.Collections.Immutable
/// A flat list of strings as a sequence of objects. A flat list of strings is also a sequence of objects, by the
@@ -59,12 +60,11 @@ let messageDetail (action : unit -> unit) =
with ex ->
ValueSome ex
- match thrown with
- | ValueSome ex ->
- match ex.Message.IndexOf '\n' with
- | -1 -> ""
- | newline -> ex.Message.Substring (newline + 1)
- | ValueNone -> invalidOp "The action must throw"
+ let ex = Assert.WantValueSome (thrown, "The action must throw")
+
+ match ex.Message.IndexOf '\n' with
+ | -1 -> ""
+ | newline -> ex.Message.Substring (newline + 1)
/// The index of the first element of a range that the equality finds equal to the value, or -1: the oracle of the
/// searches from the front.
diff --git a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListTests.fs b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListTests.fs
index 560bd80e..1114f0dc 100644
--- a/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListTests.fs
+++ b/tests/FSharp.Collections.Immutable.Tests/FlatList/FlatListTests.fs
@@ -1089,8 +1089,10 @@ type FlatListTests () =
[]
member _.``replace replaces the first equal element`` (input : int array, oldValue : int) =
CollectionAssert.AreEqual (
- FlatListOracles.replaceFirst (=) oldValue 99 input
- |> ValueOption.get,
+ Assert.WantValueSome (
+ FlatListOracles.replaceFirst (=) oldValue 99 input,
+ "The test data must contain the value to replace"
+ ),
FlatList.toArray (FlatList.replace oldValue 99 (FlatList.ofArray input)),
"FlatList.replace must replace the first equal element"
)
@@ -1130,8 +1132,10 @@ type FlatListTests () =
let names = FlatListOracles.names ()
CollectionAssert.AreEqual (
- FlatListOracles.replaceFirst FlatListOracles.equalIgnoringCase oldValue "X" names
- |> ValueOption.get,
+ Assert.WantValueSome (
+ FlatListOracles.replaceFirst FlatListOracles.equalIgnoringCase oldValue "X" names,
+ "The test data must contain a string that the comparer finds equal"
+ ),
FlatList.toArray (FlatList.replaceWith StringComparer.OrdinalIgnoreCase oldValue "X" (FlatList.ofArray names)),
"FlatList.replaceWith must replace the first element that the comparer finds equal"
)
diff --git a/tests/FSharp.Collections.Immutable.Tests/TestCategories.fs b/tests/FSharp.Collections.Immutable.Tests/TestCategories.fs
index 49796ff7..6c2bbb9e 100644
--- a/tests/FSharp.Collections.Immutable.Tests/TestCategories.fs
+++ b/tests/FSharp.Collections.Immutable.Tests/TestCategories.fs
@@ -8,6 +8,12 @@ open Microsoft.VisualStudio.TestTools.UnitTesting
// The attributes go on the test classes only, for the collection and the area that a class examines; test methods carry
// no categories (ADR 0001, D8).
+/// The assertions that extend `Assert` (`Assert.fs`).
+type AssertExtensionsTestCategoryAttribute () =
+ inherit TestCategoryBaseAttribute ()
+ static let categories : IList = [| "Assert extensions" |]
+ override _.TestCategories = categories
+
/// The builder functions and the builder modules.
type BuilderTestCategoryAttribute () =
inherit TestCategoryBaseAttribute ()