From 1180b13cf8a29a3b2afab12b5bf62bd40c3f8d23 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 01:28:01 +0000 Subject: [PATCH 1/2] test: add SideEffects and ImmTaskSeq variant tests for chunkBy/chunkByAsync - Add SideEffects module to TaskSeq.ChunkBy.Tests.fs with TestSideEffectTaskSeq variant tests for chunkBy and chunkByAsync - Test that each projection is called exactly once per element - Test that the source is not evaluated before enumeration - Add two more TestImmTaskSeq variant tests (each-element-own-chunk, chunkByAsync) Total new test cases: +121 (grows from ~11 scenarios to 170 xUnit test runs with variants) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes.txt | 1 + .../TaskSeq.ChunkBy.Tests.fs | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/release-notes.txt b/release-notes.txt index 20ee8fb0..71b5deab 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -2,6 +2,7 @@ Release notes: Unreleased + - test: add SideEffects module and ImmTaskSeq variant tests to TaskSeq.ChunkBy.Tests.fs, improving coverage for chunkBy and chunkByAsync 1.1.1 - perf: use while! in groupBy, countBy, partition, except, exceptOfSeq to eliminate redundant mutable 'go' variables and initial MoveNextAsync calls diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.ChunkBy.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.ChunkBy.Tests.fs index 76f684c1..a8014efe 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.ChunkBy.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.ChunkBy.Tests.fs @@ -133,3 +133,124 @@ module Functionality = let _, arr = result[0] arr |> should haveLength 10 } + + [)>] + let ``TaskSeq-chunkBy each element its own chunk as variants`` variant = task { + let ts = Gen.getSeqImmutable variant + let! result = TaskSeq.chunkBy id ts |> TaskSeq.toArrayAsync + result |> should haveLength 10 + + result + |> Array.iteri (fun i (k, arr) -> + k |> should equal (i + 1) + arr |> should haveLength 1 + arr[0] |> should equal (i + 1)) + } + + [)>] + let ``TaskSeq-chunkByAsync all elements same key as variants`` variant = task { + let ts = Gen.getSeqImmutable variant + + let! result = + TaskSeq.chunkByAsync (fun _ -> Task.fromResult 0) ts + |> TaskSeq.toArrayAsync + + result |> should haveLength 1 + let _, arr = result[0] + arr |> should haveLength 10 + } + + +module SideEffects = + [)>] + let ``TaskSeq-chunkBy on side-effect seq groups all elements under one key`` variant = task { + let! result = + Gen.getSeqWithSideEffect variant + |> TaskSeq.chunkBy (fun _ -> 0) + |> TaskSeq.toArrayAsync + + result |> should haveLength 1 + let _, arr = result[0] + arr |> should equal [| 1..10 |] + } + + [)>] + let ``TaskSeq-chunkByAsync on side-effect seq groups all elements under one key`` variant = task { + let! result = + Gen.getSeqWithSideEffect variant + |> TaskSeq.chunkByAsync (fun _ -> Task.fromResult 0) + |> TaskSeq.toArrayAsync + + result |> should haveLength 1 + let _, arr = result[0] + arr |> should equal [| 1..10 |] + } + + [)>] + let ``TaskSeq-chunkBy on side-effect seq produces correct singleton chunks`` variant = task { + let! result = + Gen.getSeqWithSideEffect variant + |> TaskSeq.chunkBy id + |> TaskSeq.toArrayAsync + + result |> should haveLength 10 + + result + |> Array.iteri (fun i (k, arr) -> + k |> should equal (i + 1) + arr |> should equal [| i + 1 |]) + } + + [] + let ``TaskSeq-chunkBy projection is called exactly once per element`` () = task { + let mutable callCount = 0 + + let ts = taskSeq { yield! [ 1; 1; 2; 3; 3 ] } + + let! result = + ts + |> TaskSeq.chunkBy (fun x -> + callCount <- callCount + 1 + x % 2) + |> TaskSeq.toArrayAsync + + callCount |> should equal 5 + result |> should haveLength 3 + } + + [] + let ``TaskSeq-chunkByAsync projection is called exactly once per element`` () = task { + let mutable callCount = 0 + + let ts = taskSeq { yield! [ 1; 1; 2; 3; 3 ] } + + let! result = + ts + |> TaskSeq.chunkByAsync (fun x -> task { + callCount <- callCount + 1 + return x % 2 + }) + |> TaskSeq.toArrayAsync + + callCount |> should equal 5 + result |> should haveLength 3 + } + + [] + let ``TaskSeq-chunkBy does not evaluate elements before enumeration`` () = task { + let mutable sourceCount = 0 + + let ts = taskSeq { + for i in 1..5 do + sourceCount <- sourceCount + 1 + yield i + } + + let chunked = TaskSeq.chunkBy (fun x -> x % 2 = 0) ts + // Building the pipeline does not consume the source + sourceCount |> should equal 0 + + let! _ = TaskSeq.toArrayAsync chunked + // Only after consuming does the source get evaluated + sourceCount |> should equal 5 + } From 49863ec9ca2b1b28ea1c30be267777bcf635671d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Apr 2026 01:28:03 +0000 Subject: [PATCH 2/2] ci: trigger checks