diff --git a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
index dbf1cfc..10ef602 100644
--- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
+++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
@@ -57,6 +57,8 @@
+
+
diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Builder.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Builder.Tests.fs
new file mode 100644
index 0000000..86c1bc5
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Builder.Tests.fs
@@ -0,0 +1,351 @@
+module TaskSeq.Tests.Builder
+
+open System
+open System.Reflection
+open System.Threading.Tasks
+open System.Collections.Generic
+open System.Threading
+
+open Xunit
+open FsUnit.Xunit
+
+open FSharp.Control
+
+//
+// Tests for TaskSeq computation expression builder edge cases
+// These test specific edge cases and internal builder functionality
+//
+
+[]
+let ``taskSeq builder should handle empty computation expression`` () = task {
+ let emptySeq = taskSeq { () }
+
+ let! items = TaskSeq.toListAsync emptySeq
+ items |> should be Empty
+}
+
+[]
+let ``taskSeq builder should handle single yield`` () = task {
+ let seq = taskSeq {
+ yield 42
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [42]
+}
+
+[]
+let ``taskSeq builder should handle multiple yields`` () = task {
+ let seq = taskSeq {
+ yield 1
+ yield 2
+ yield 3
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [1; 2; 3]
+}
+
+[]
+let ``taskSeq builder should handle yield! with another TaskSeq`` () = task {
+ let innerSeq = taskSeq {
+ yield 1
+ yield 2
+ }
+
+ let outerSeq = taskSeq {
+ yield 0
+ yield! innerSeq
+ yield 3
+ }
+
+ let! items = TaskSeq.toListAsync outerSeq
+ items |> should equal [0; 1; 2; 3]
+}
+
+[]
+let ``taskSeq builder should handle yield! with regular sequence`` () = task {
+ let seq = taskSeq {
+ yield 0
+ yield! [1; 2; 3]
+ yield 4
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [0; 1; 2; 3; 4]
+}
+
+[]
+let ``taskSeq builder should handle async operations in do!`` () = task {
+ let mutable sideEffect = 0
+
+ let seq = taskSeq {
+ do! Task.Delay(1)
+ sideEffect <- 42
+ yield sideEffect
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [42]
+ sideEffect |> should equal 42
+}
+
+[]
+let ``taskSeq builder should handle let! with async values`` () = task {
+ let seq = taskSeq {
+ let! value = Task.FromResult(42)
+ yield value * 2
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [84]
+}
+
+[]
+let ``taskSeq builder should handle conditional yields`` () = task {
+ let seq = taskSeq {
+ for i in 1..5 do
+ if i % 2 = 0 then
+ yield i
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [2; 4]
+}
+
+[]
+let ``taskSeq builder should handle nested for loops`` () = task {
+ let seq = taskSeq {
+ for i in 1..2 do
+ for j in 1..2 do
+ yield (i, j)
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [(1, 1); (1, 2); (2, 1); (2, 2)]
+}
+
+[]
+let ``taskSeq builder should handle try-with exception handling`` () = task {
+ let seq = taskSeq {
+ try
+ yield 1
+ failwith "test error"
+ yield 2
+ with
+ | _ -> yield 999
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [1; 999]
+}
+
+[]
+let ``taskSeq builder should handle try-finally`` () = task {
+ let mutable finalized = false
+
+ let seq = taskSeq {
+ try
+ yield 1
+ yield 2
+ finally
+ finalized <- true
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [1; 2]
+ finalized |> should equal true
+}
+
+[]
+let ``taskSeq builder should handle use for disposable resources`` () = task {
+ let mutable disposed = false
+
+ let disposable = { new IDisposable with
+ member _.Dispose() = disposed <- true }
+
+ let seq = taskSeq {
+ use d = disposable
+ yield 42
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [42]
+ disposed |> should equal true
+}
+
+[]
+let ``taskSeq builder should handle use! for async disposable resources`` () = task {
+ let mutable disposed = false
+
+ let asyncDisposable = { new IAsyncDisposable with
+ member _.DisposeAsync() =
+ disposed <- true
+ ValueTask.CompletedTask }
+
+ let seq = taskSeq {
+ use! d = Task.FromResult(asyncDisposable)
+ yield 42
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [42]
+ disposed |> should equal true
+}
+
+[]
+let ``taskSeq builder should handle while loops`` () = task {
+ let seq = taskSeq {
+ let mutable i = 0
+ while i < 3 do
+ yield i
+ i <- i + 1
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [0; 1; 2]
+}
+
+[]
+let ``taskSeq builder should handle match expressions`` () = task {
+ let getValue x =
+ match x with
+ | 1 -> "one"
+ | 2 -> "two"
+ | _ -> "other"
+
+ let seq = taskSeq {
+ for i in 1..3 do
+ yield getValue i
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal ["one"; "two"; "other"]
+}
+
+[]
+let ``taskSeq builder should handle complex async computations`` () = task {
+ let asyncComputation x = task {
+ do! Task.Delay(1)
+ return x * x
+ }
+
+ let seq = taskSeq {
+ for i in 1..3 do
+ let! squared = asyncComputation i
+ yield squared
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [1; 4; 9]
+}
+
+[]
+let ``taskSeq builder should handle cancellation token propagation`` () = task {
+ use cts = new CancellationTokenSource()
+ cts.CancelAfter(100)
+
+ let seq = taskSeq {
+ for i in 1..1000 do
+ do! Task.Delay(10, cts.Token)
+ yield i
+ }
+
+ let asyncEnumerator = seq.GetAsyncEnumerator(cts.Token)
+
+ let testAction() = task {
+ let mutable count = 0
+ let mutable hasMore = true
+
+ while hasMore do
+ let! moveNext = asyncEnumerator.MoveNextAsync()
+ hasMore <- moveNext
+ if hasMore then count <- count + 1
+
+ return count
+ }
+
+ // Should be cancelled before reaching 1000 items
+ let ex = Assert.ThrowsAsync(fun () -> testAction())
+ let! _ = ex
+ ()
+}
+
+[]
+let ``taskSeq builder should handle empty yield! with empty sequence`` () = task {
+ let seq = taskSeq {
+ yield 1
+ yield! TaskSeq.empty
+ yield 2
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [1; 2]
+}
+
+[]
+let ``taskSeq builder should handle multiple yield! operations`` () = task {
+ let seq1 = taskSeq { yield 1; yield 2 }
+ let seq2 = taskSeq { yield 3; yield 4 }
+
+ let combined = taskSeq {
+ yield 0
+ yield! seq1
+ yield! seq2
+ yield 5
+ }
+
+ let! items = TaskSeq.toListAsync combined
+ items |> should equal [0; 1; 2; 3; 4; 5]
+}
+
+[]
+let ``taskSeq builder should handle async functions with side effects`` () = task {
+ let mutable callCount = 0
+
+ let asyncSideEffect () = task {
+ callCount <- callCount + 1
+ return callCount
+ }
+
+ let seq = taskSeq {
+ let! first = asyncSideEffect()
+ yield first
+ let! second = asyncSideEffect()
+ yield second
+ }
+
+ let! items = TaskSeq.toListAsync seq
+ items |> should equal [1; 2]
+ callCount |> should equal 2
+}
+
+[]
+let ``taskSeq builder should handle nested taskSeq expressions`` () = task {
+ let innerSeq x = taskSeq {
+ for i in 1..x do
+ yield i * 10
+ }
+
+ let outerSeq = taskSeq {
+ for x in 1..2 do
+ yield! innerSeq x
+ }
+
+ let! items = TaskSeq.toListAsync outerSeq
+ items |> should equal [10; 10; 20]
+}
+
+[]
+let ``taskSeq builder should handle computation expression return`` () = task {
+ let seq = taskSeq {
+ if true then
+ return 42
+ else
+ yield 0
+ }
+
+ // Should complete immediately with no items when using return
+ let! items = TaskSeq.toListAsync seq
+ items |> should be Empty
+}
\ No newline at end of file
diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Internal.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Internal.Tests.fs
new file mode 100644
index 0000000..a9b17ef
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Internal.Tests.fs
@@ -0,0 +1,261 @@
+module TaskSeq.Tests.Internal
+
+open System
+open System.Reflection
+open System.Threading.Tasks
+open System.Collections.Generic
+open System.Threading
+
+open Xunit
+open FsUnit.Xunit
+
+open FSharp.Control
+
+//
+// NOTE: This test module tests internal functions in TaskSeqInternal module
+// using reflection where necessary, as these functions are not exposed publicly
+//
+
+[]
+let ``checkNonNull should throw ArgumentNullException for null argument`` () =
+ // Get internal module type and method
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let checkNonNullMethod = internalType.GetMethod("checkNonNull", BindingFlags.Static ||| BindingFlags.Public)
+
+ // Test with null argument
+ let testAction() =
+ checkNonNullMethod.Invoke(null, [| "testArg"; null |]) |> ignore
+
+ testAction |> should throwWithMessage "testArg"
+
+[]
+let ``checkNonNull should not throw for non-null argument`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let checkNonNullMethod = internalType.GetMethod("checkNonNull", BindingFlags.Static ||| BindingFlags.Public)
+
+ // Test with non-null argument
+ let result = checkNonNullMethod.Invoke(null, [| "testArg"; "validValue" |])
+ result |> should equal null // void return
+
+[]
+let ``raiseEmptySeq should throw correct ArgumentException`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let raiseEmptySeqMethod = internalType.GetMethod("raiseEmptySeq", BindingFlags.Static ||| BindingFlags.Public)
+
+ let testAction() = raiseEmptySeqMethod.Invoke(null, [||]) |> ignore
+
+ let ex = Assert.Throws(testAction)
+ let innerEx = ex.InnerException :?> ArgumentException
+ innerEx.ParamName |> should equal "source"
+ innerEx.Message |> should contain "empty"
+
+[]
+let ``raiseCannotBeNegative should throw for negative values`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("raiseCannotBeNegative", BindingFlags.Static ||| BindingFlags.Public)
+
+ let testAction() = method.Invoke(null, [| "count"; -1 |]) |> ignore
+
+ let ex = Assert.Throws(testAction)
+ let innerEx = ex.InnerException :?> ArgumentException
+ innerEx.ParamName |> should equal "count"
+ innerEx.Message |> should contain "non-negative"
+
+[]
+let ``raiseCannotBeNegative should not throw for zero`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("raiseCannotBeNegative", BindingFlags.Static ||| BindingFlags.Public)
+
+ let result = method.Invoke(null, [| "count"; 0 |])
+ result |> should equal null
+
+[]
+let ``raiseCannotBeNegative should not throw for positive values`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("raiseCannotBeNegative", BindingFlags.Static ||| BindingFlags.Public)
+
+ let result = method.Invoke(null, [| "count"; 42 |])
+ result |> should equal null
+
+[]
+let ``raiseOutOfBounds should throw correct ArgumentException`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("raiseOutOfBounds", BindingFlags.Static ||| BindingFlags.Public)
+
+ let testAction() = method.Invoke(null, [| "index" |]) |> ignore
+
+ let ex = Assert.Throws(testAction)
+ let innerEx = ex.InnerException :?> ArgumentException
+ innerEx.ParamName |> should equal "index"
+ innerEx.Message |> should contain "bounds"
+
+[]
+let ``raiseInsufficient should throw correct ArgumentException`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("raiseInsufficient", BindingFlags.Static ||| BindingFlags.Public)
+
+ let testAction() = method.Invoke(null, [||]) |> ignore
+
+ let ex = Assert.Throws(testAction)
+ let innerEx = ex.InnerException :?> ArgumentException
+ innerEx.ParamName |> should equal "source"
+ innerEx.Message |> should contain "insufficient"
+
+[]
+let ``raiseNotFound should throw KeyNotFoundException`` () =
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("raiseNotFound", BindingFlags.Static ||| BindingFlags.Public)
+
+ let testAction() = method.Invoke(null, [||]) |> ignore
+
+ let ex = Assert.Throws(testAction)
+ let innerEx = ex.InnerException :?> KeyNotFoundException
+ innerEx.Message |> should contain "predicate"
+
+[]
+let ``isEmpty should return true for empty sequence`` () = task {
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("isEmpty", BindingFlags.Static ||| BindingFlags.Public)
+
+ let emptySeq = TaskSeq.empty
+ let result = method.Invoke(null, [| emptySeq |]) :?> Task
+ let! isEmpty = result
+ isEmpty |> should equal true
+}
+
+[]
+let ``isEmpty should return false for non-empty sequence`` () = task {
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("isEmpty", BindingFlags.Static ||| BindingFlags.Public)
+
+ let nonEmptySeq = TaskSeq.singleton 42
+ let result = method.Invoke(null, [| nonEmptySeq |]) :?> Task
+ let! isEmpty = result
+ isEmpty |> should equal false
+}
+
+[]
+let ``empty should create proper empty sequence`` () = task {
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let property = internalType.GetProperty("empty", BindingFlags.Static ||| BindingFlags.Public)
+
+ let emptySeq = property.GetValue(null) :?> TaskSeq
+
+ use enumerator = emptySeq.GetAsyncEnumerator(CancellationToken.None)
+ let! hasItems = enumerator.MoveNextAsync()
+ hasItems |> should equal false
+}
+
+[]
+let ``singleton should create sequence with single item`` () = task {
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("singleton", BindingFlags.Static ||| BindingFlags.Public)
+
+ let singletonSeq = method.Invoke(null, [| 42 |]) :?> TaskSeq
+
+ use enumerator = singletonSeq.GetAsyncEnumerator(CancellationToken.None)
+
+ // Should have first item
+ let! hasFirst = enumerator.MoveNextAsync()
+ hasFirst |> should equal true
+ enumerator.Current |> should equal 42
+
+ // Should not have second item
+ let! hasSecond = enumerator.MoveNextAsync()
+ hasSecond |> should equal false
+}
+
+[]
+let ``moveFirstOrRaiseUnsafe should not throw for non-empty sequence`` () = task {
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("moveFirstOrRaiseUnsafe", BindingFlags.Static ||| BindingFlags.Public)
+
+ let seq = TaskSeq.singleton 42
+ use enumerator = seq.GetAsyncEnumerator(CancellationToken.None)
+
+ let result = method.Invoke(null, [| enumerator |]) :?> Task
+ do! result
+
+ enumerator.Current |> should equal 42
+}
+
+[]
+let ``moveFirstOrRaiseUnsafe should throw for empty sequence`` () = task {
+ let assembly = typeof>.Assembly
+ let internalType = assembly.GetType("FSharp.Control.TaskSeqInternal")
+ let method = internalType.GetMethod("moveFirstOrRaiseUnsafe", BindingFlags.Static ||| BindingFlags.Public)
+
+ let emptySeq = TaskSeq.empty
+ use enumerator = emptySeq.GetAsyncEnumerator(CancellationToken.None)
+
+ let result = method.Invoke(null, [| enumerator |]) :?> Task
+
+ let ex = Assert.ThrowsAsync(fun () -> result)
+ let! exception = ex
+ exception.ParamName |> should equal "source"
+ exception.Message |> should contain "empty"
+}
+
+// Test internal discriminated union types
+[]
+let ``AsyncEnumStatus enum values should exist`` () =
+ let assembly = typeof>.Assembly
+ let statusType = assembly.GetType("FSharp.Control.AsyncEnumStatus")
+
+ statusType |> should not' (equal null)
+ statusType.IsEnum |> should equal false // It's a DU, not an enum
+ statusType.IsValueType |> should equal true // Struct DU
+
+[]
+let ``TakeOrSkipKind enum values should exist`` () =
+ let assembly = typeof>.Assembly
+ let kindType = assembly.GetType("FSharp.Control.TakeOrSkipKind")
+
+ kindType |> should not' (equal null)
+ kindType.IsValueType |> should equal true
+
+[]
+let ``Action union type should exist`` () =
+ let assembly = typeof>.Assembly
+ let actionType = assembly.GetTypes() |> Array.find (fun t -> t.Name.StartsWith("Action") && t.IsValueType)
+
+ actionType |> should not' (equal null)
+ actionType.IsValueType |> should equal true
+
+[]
+let ``FolderAction union type should exist`` () =
+ let assembly = typeof>.Assembly
+ let folderType = assembly.GetTypes() |> Array.find (fun t -> t.Name.StartsWith("FolderAction") && t.IsValueType)
+
+ folderType |> should not' (equal null)
+ folderType.IsValueType |> should equal true
+
+[]
+let ``ChooserAction union type should exist`` () =
+ let assembly = typeof>.Assembly
+ let chooserType = assembly.GetTypes() |> Array.find (fun t -> t.Name.StartsWith("ChooserAction") && t.IsValueType)
+
+ chooserType |> should not' (equal null)
+ chooserType.IsValueType |> should equal true
+
+[]
+let ``PredicateAction union type should exist`` () =
+ let assembly = typeof>.Assembly
+ let predType = assembly.GetTypes() |> Array.find (fun t -> t.Name.StartsWith("PredicateAction") && t.IsValueType)
+
+ predType |> should not' (equal null)
+ predType.IsValueType |> should equal true
\ No newline at end of file