diff --git a/src/FSharpx.Collections/NonEmptyList.fs b/src/FSharpx.Collections/NonEmptyList.fs index 4540ee7c..0815de18 100644 --- a/src/FSharpx.Collections/NonEmptyList.fs +++ b/src/FSharpx.Collections/NonEmptyList.fs @@ -121,3 +121,47 @@ module NonEmptyList = [] let zip list1 list2 = { List = List.zip list1.List list2.List } + + [] + let iter action list = + List.iter action list.List + + [] + let iteri action list = + List.iteri action list.List + + [] + let mapi mapping list = + { List = List.mapi mapping list.List } + + [] + let exists predicate list = + List.exists predicate list.List + + [] + let forall predicate list = + List.forall predicate list.List + + [] + let contains value list = + List.contains value list.List + + [] + let sortWith comparer list = + { List = List.sortWith comparer list.List } + + [] + let sortBy projection list = + { List = List.sortBy projection list.List } + + [] + let sort list = + { List = List.sort list.List } + + [] + let maxBy projection list = + List.maxBy projection list.List + + [] + let minBy projection list = + List.minBy projection list.List diff --git a/tests/FSharpx.Collections.Tests/NonEmptyListTests.fs b/tests/FSharpx.Collections.Tests/NonEmptyListTests.fs index acb77c36..0e8435a3 100644 --- a/tests/FSharpx.Collections.Tests/NonEmptyListTests.fs +++ b/tests/FSharpx.Collections.Tests/NonEmptyListTests.fs @@ -257,4 +257,113 @@ module NonEmptyListTests = (Prop.forAll(twoDifferentLengths()) <| fun (nel1, nel2) -> Expect.throwsT (sprintf "length %i; length %i" nel1.Length nel2.Length) (fun () -> - NonEmptyList.zip nel1 nel2 |> ignore)) ] + NonEmptyList.zip nel1 nel2 |> ignore)) + + testPropertyWithConfig + config10k + "cons prepends an element" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> + let consed = NonEmptyList.cons 42 nel + consed.Head = 42 && consed.Length = nel.Length + 1) + + testPropertyWithConfig config10k "appendList combines NonEmptyList with plain list" + <| fun (a: _ list) (b: _ list) -> + if a.IsEmpty then + true + else + let neA = NonEmptyList.create a.Head a.Tail + (NonEmptyList.appendList neA b).Length = neA.Length + b.Length + + testPropertyWithConfig + config10k + "toSeq yields all elements" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> Seq.forall2 (=) (NonEmptyList.toSeq nel) (NonEmptyList.toList nel)) + + testPropertyWithConfig + config10k + "iter visits all elements" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> + let visited = System.Collections.Generic.List() + NonEmptyList.iter visited.Add nel + Seq.toList visited = NonEmptyList.toList nel) + + testPropertyWithConfig + config10k + "iteri visits all elements with correct indices" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> + let pairs = System.Collections.Generic.List() + NonEmptyList.iteri (fun i x -> pairs.Add(i, x)) nel + let expected = nel |> NonEmptyList.toList |> List.mapi(fun i x -> i, x) + Seq.toList pairs = expected) + + testPropertyWithConfig + config10k + "mapi produces indexed values" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> + let actual = NonEmptyList.mapi (fun i x -> i, x) nel |> NonEmptyList.toList + let expected = nel |> NonEmptyList.toList |> List.mapi(fun i x -> i, x) + actual = expected) + + testPropertyWithConfig + config10k + "exists returns true when element satisfies predicate" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> NonEmptyList.exists (fun _ -> true) nel = true) + + testPropertyWithConfig + config10k + "forall returns true when all elements satisfy predicate" + (Prop.forAll(NonEmptyListGen.NonEmptyList()) + <| fun nel -> NonEmptyList.forall (fun _ -> true) nel = true) + + testPropertyWithConfig config10k "contains finds a present element" + <| fun (xs: int list) -> + if xs.IsEmpty then + true + else + let nel = NonEmptyList.create xs.Head xs.Tail + NonEmptyList.contains xs.Head nel + + testPropertyWithConfig + config10k + "sort produces sorted output" + (Prop.forAll(neListOfInt()) + <| fun nel -> + let sorted = NonEmptyList.sort nel |> NonEmptyList.toList + let expected = nel |> NonEmptyList.toList |> List.sort + sorted = expected) + + testPropertyWithConfig + config10k + "sortBy produces sorted output" + (Prop.forAll(neListOfInt()) + <| fun nel -> + let sorted = NonEmptyList.sortBy id nel |> NonEmptyList.toList + let expected = nel |> NonEmptyList.toList |> List.sort + sorted = expected) + + testPropertyWithConfig + config10k + "sortWith produces sorted output" + (Prop.forAll(neListOfInt()) + <| fun nel -> + let sorted = NonEmptyList.sortWith compare nel |> NonEmptyList.toList + let expected = nel |> NonEmptyList.toList |> List.sort + sorted = expected) + + testPropertyWithConfig + config10k + "maxBy returns maximum element" + (Prop.forAll(neListOfInt()) + <| fun nel -> NonEmptyList.maxBy id nel = (nel |> NonEmptyList.toList |> List.max)) + + testPropertyWithConfig + config10k + "minBy returns minimum element" + (Prop.forAll(neListOfInt()) + <| fun nel -> NonEmptyList.minBy id nel = (nel |> NonEmptyList.toList |> List.min)) ] diff --git a/tests/FSharpx.Collections.Tests/SeqTests.fs b/tests/FSharpx.Collections.Tests/SeqTests.fs index 1d334e63..8ee01a10 100644 --- a/tests/FSharpx.Collections.Tests/SeqTests.fs +++ b/tests/FSharpx.Collections.Tests/SeqTests.fs @@ -244,4 +244,87 @@ module SeqTests = Expect.sequenceEqual "" a (a |> Seq.intersperse ',') } - testPropertyWithConfig config10k "I should interperse always 2n-1 elements" intersperse ] + testPropertyWithConfig config10k "I should interperse always 2n-1 elements" intersperse + + test "cons prepends an element to a seq" { + Seq.cons 0 [ 1; 2; 3 ] + |> Expect.sequenceEqual "cons" (List.toSeq [ 0; 1; 2; 3 ]) + } + + test "unCons returns None for empty seq" { Seq.unCons Seq.empty |> Expect.isNone "unCons" } + + test "unCons returns Some (head, tail) for non-empty seq" { + match Seq.unCons [ 1; 2; 3 ] with + | Some(head, tail) -> + Expect.equal "unCons head" 1 head + Expect.sequenceEqual "unCons tail" [ 2; 3 ] tail + | None -> failwith "Expected Some" + } + + test "findExactlyOne returns the single matching element" { + Expect.equal "findExactlyOne" 3 (Seq.findExactlyOne ((=) 3) [ 1; 2; 3; 4; 5 ]) + } + + test "catOptions extracts Some values from a seq of options" { + let opts = [ Some 1; None; Some 2; None; Some 3 ] + + Seq.catOptions opts + |> Expect.sequenceEqual "catOptions" (List.toSeq [ 1; 2; 3 ]) + } + + test "catOptions returns empty seq when all are None" { + let opts: int option list = [ None; None ] + + Seq.catOptions opts + |> Expect.sequenceEqual "catOptions empty" Seq.empty + } + + test "choice1s extracts Choice1Of2 values" { + let choices = [ Choice1Of2 1; Choice2Of2 "a"; Choice1Of2 2; Choice2Of2 "b" ] + + Seq.choice1s choices + |> Expect.sequenceEqual "choice1s" (List.toSeq [ 1; 2 ]) + } + + test "choice2s extracts Choice2Of2 values" { + let choices = [ Choice1Of2 1; Choice2Of2 "a"; Choice1Of2 2; Choice2Of2 "b" ] + + Seq.choice2s choices + |> Expect.sequenceEqual "choice2s" (List.toSeq [ "a"; "b" ]) + } + + test "partitionChoices splits into two seqs" { + let choices = [ Choice1Of2 1; Choice2Of2 "a"; Choice1Of2 2; Choice2Of2 "b" ] + let c1s, c2s = Seq.partitionChoices choices + Expect.sequenceEqual "partitionChoices c1s" (List.toSeq [ 1; 2 ]) c1s + Expect.sequenceEqual "partitionChoices c2s" (List.toSeq [ "a"; "b" ]) c2s + } + + test "equalsWith returns true for equal seqs" { Expect.isTrue "equalsWith" (Seq.equalsWith (=) [ 1; 2; 3 ] [ 1; 2; 3 ]) } + + test "equalsWith returns false for unequal seqs" { Expect.isFalse "equalsWith" (Seq.equalsWith (=) [ 1; 2; 3 ] [ 1; 2; 4 ]) } + + test "equalsWith returns true for empty seqs" { + Expect.isTrue "equalsWith empty" (Seq.equalsWith (=) (Seq.empty) (Seq.empty)) + } + + test "groupNeighboursBy groups consecutive equal-key elements" { + let input = [ 1; 1; 2; 2; 1; 1 ] + let result = Seq.groupNeighboursBy id input |> Seq.toList + let keys = result |> List.map fst + Expect.equal "groupNeighboursBy keys" [ 1; 2; 1 ] keys + let groups = result |> List.map(snd >> Seq.toList) + Expect.equal "groupNeighboursBy groups" [ [ 1; 1 ]; [ 2; 2 ]; [ 1; 1 ] ] groups + } + + test "groupNeighboursBy on empty seq gives empty result" { + Seq.groupNeighboursBy id (Seq.empty) + |> Seq.isEmpty + |> Expect.isTrue "groupNeighboursBy empty" + } + + test "groupNeighboursBy on singleton gives one group" { + let result = Seq.groupNeighboursBy id [ 42 ] |> Seq.toList + Expect.equal "groupNeighboursBy singleton" 1 result.Length + Expect.equal "groupNeighboursBy singleton key" 42 (fst result.[0]) + } ]