Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 4.18.0

* Tests: Added comprehensive tests for `AsyncSeq.zapp`, `AsyncSeq.zappAsync`, and `AsyncSeq.compareWithAsync`, which previously had no dedicated test coverage.

### 4.17.0

* Added `AsyncSeq.exists2` β€” asynchronously tests whether any corresponding pair of elements in two async sequences satisfies the predicate. Evaluates pairwise up to the shorter sequence; short-circuits on first match. Mirrors `Seq.exists2`.
Expand Down
99 changes: 99 additions & 0 deletions tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2094,7 +2094,7 @@
let actual =
ls
|> AsyncSeq.ofSeq
|> AsyncSeq.groupBy p

Check warning on line 2097 in tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

View workflow job for this annotation

GitHub Actions / build

The result of groupBy must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.
|> AsyncSeq.mapAsyncParallel (snd >> AsyncSeq.toListAsync)
Assert.AreEqual(expected, actual)

Expand All @@ -2103,7 +2103,7 @@
let expected = asyncSeq { raise (exn("test")) }
let actual =
asyncSeq { raise (exn("test")) }
|> AsyncSeq.groupBy (fun i -> i % 3)

Check warning on line 2106 in tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

View workflow job for this annotation

GitHub Actions / build

The result of groupBy must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.
|> AsyncSeq.mapAsyncParallel (snd >> AsyncSeq.toListAsync)
Assert.AreEqual(expected, actual)

Expand Down Expand Up @@ -4851,7 +4851,7 @@
let ``AsyncSeq.groupByAsync groups elements by async projection`` () =
let result =
AsyncSeq.ofSeq [1..6]
|> AsyncSeq.groupByAsync (fun x -> async { return x % 2 })

Check warning on line 4854 in tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

View workflow job for this annotation

GitHub Actions / build

The result of groupByAsync must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.
|> AsyncSeq.mapAsyncParallel (fun (key, grp) -> async {
let! items = AsyncSeq.toArrayAsync grp
return key, Array.sort items })
Expand All @@ -4864,7 +4864,7 @@
let ``AsyncSeq.groupByAsync on empty sequence returns empty`` () =
let result =
AsyncSeq.empty<int>
|> AsyncSeq.groupByAsync (fun x -> async { return x % 2 })

Check warning on line 4867 in tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

View workflow job for this annotation

GitHub Actions / build

The result of groupByAsync must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.
|> AsyncSeq.toArrayAsync
|> Async.RunSynchronously
Assert.AreEqual([||], result)
Expand All @@ -4873,7 +4873,7 @@
let ``AsyncSeq.groupByAsync with all-same key produces single group`` () =
let result =
AsyncSeq.ofSeq [1; 2; 3]
|> AsyncSeq.groupByAsync (fun _ -> async { return "same" })

Check warning on line 4876 in tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

View workflow job for this annotation

GitHub Actions / build

The result of groupByAsync must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.
|> AsyncSeq.mapAsyncParallel (fun (key, grp) -> async {
let! items = AsyncSeq.toArrayAsync grp
return key, Array.sort items })
Expand Down Expand Up @@ -5087,3 +5087,102 @@
(AsyncSeq.ofSeq [1;2;3])
|> Async.RunSynchronously
Assert.IsTrue(result)

// ===== zapp / zappAsync =====

[<Test>]
let ``AsyncSeq.zapp applies functions to corresponding elements`` () =
let fs = asyncSeq { yield (fun x -> x + 10); yield (fun x -> x * 2); yield (fun x -> x - 1) }
let vs = asyncSeq { yield 1; yield 2; yield 3 }
let result = AsyncSeq.zapp fs vs |> AsyncSeq.toArrayAsync |> Async.RunSynchronously
Assert.AreEqual([| 11; 4; 2 |], result)

[<Test>]
let ``AsyncSeq.zapp stops when functions run out`` () =
let fs = asyncSeq { yield (fun x -> x + 1); yield (fun x -> x + 2) }
let vs = asyncSeq { yield 10; yield 20; yield 30 }
let result = AsyncSeq.zapp fs vs |> AsyncSeq.toArrayAsync |> Async.RunSynchronously
Assert.AreEqual([| 11; 22 |], result)

[<Test>]
let ``AsyncSeq.zapp stops when values run out`` () =
let fs = asyncSeq { yield (fun x -> x + 1); yield (fun x -> x + 2); yield (fun x -> x + 3) }
let vs = asyncSeq { yield 5 }
let result = AsyncSeq.zapp fs vs |> AsyncSeq.toArrayAsync |> Async.RunSynchronously
Assert.AreEqual([| 6 |], result)

[<Test>]
let ``AsyncSeq.zapp on empty functions returns empty`` () =
let fs = AsyncSeq.empty<int -> int>
let vs = asyncSeq { yield 1; yield 2; yield 3 }
let result = AsyncSeq.zapp fs vs |> AsyncSeq.toArrayAsync |> Async.RunSynchronously
Assert.AreEqual([||], result)

[<Test>]
let ``AsyncSeq.zappAsync applies async functions to corresponding elements`` () =
let fs = asyncSeq {
yield (fun x -> async { return x + 10 })
yield (fun x -> async { return x * 3 })
}
let vs = asyncSeq { yield 5; yield 4 }
let result = AsyncSeq.zappAsync fs vs |> AsyncSeq.toArrayAsync |> Async.RunSynchronously
Assert.AreEqual([| 15; 12 |], result)

[<Test>]
let ``AsyncSeq.zappAsync on empty source returns empty`` () =
let fs = asyncSeq { yield (fun x -> async { return x + 1 }) }
let vs = AsyncSeq.empty<int>
let result = AsyncSeq.zappAsync fs vs |> AsyncSeq.toArrayAsync |> Async.RunSynchronously
Assert.AreEqual([||], result)

// ===== compareWithAsync =====

[<Test>]
let ``AsyncSeq.compareWithAsync equal sequences returns 0`` () =
let result =
AsyncSeq.compareWithAsync
(fun a b -> async { return compare a b })
(AsyncSeq.ofSeq [1;2;3])
(AsyncSeq.ofSeq [1;2;3])
|> Async.RunSynchronously
Assert.AreEqual(0, result)

[<Test>]
let ``AsyncSeq.compareWithAsync shorter is less than longer`` () =
let result =
AsyncSeq.compareWithAsync
(fun a b -> async { return compare a b })
(AsyncSeq.ofSeq [1;2])
(AsyncSeq.ofSeq [1;2;3])
|> Async.RunSynchronously
Assert.IsTrue(result < 0)

[<Test>]
let ``AsyncSeq.compareWithAsync longer is greater than shorter`` () =
let result =
AsyncSeq.compareWithAsync
(fun a b -> async { return compare a b })
(AsyncSeq.ofSeq [1;2;3])
(AsyncSeq.ofSeq [1;2])
|> Async.RunSynchronously
Assert.IsTrue(result > 0)

[<Test>]
let ``AsyncSeq.compareWithAsync lexicographic difference`` () =
let result =
AsyncSeq.compareWithAsync
(fun a b -> async { return compare a b })
(AsyncSeq.ofSeq [1;3])
(AsyncSeq.ofSeq [1;2])
|> Async.RunSynchronously
Assert.IsTrue(result > 0)

[<Test>]
let ``AsyncSeq.compareWithAsync empty sequences returns 0`` () =
let result =
AsyncSeq.compareWithAsync
(fun a b -> async { return compare a b })
AsyncSeq.empty<int>
AsyncSeq.empty<int>
|> Async.RunSynchronously
Assert.AreEqual(0, result)
Loading