1+ #!/ usr/ bin/ env dotnet fsi
2+
3+ // Test script for the new mapAsyncUnorderedParallel function
4+ // Run with: dotnet fsi mapAsyncUnorderedParallel_test.fsx
5+
6+ #r " ./src/FSharp.Control.AsyncSeq/bin/Release/netstandard2.1/FSharp.Control.AsyncSeq.dll"
7+
8+ open System
9+ open System.Threading
10+ open FSharp.Control
11+ open System.Diagnostics
12+ open System.Collections .Generic
13+
14+ // Test 1: Basic functionality - ensure results are all present
15+ let testBasicFunctionality () =
16+ printfn " === Test 1: Basic Functionality ==="
17+
18+ let input = [ 1 ; 2 ; 3 ; 4 ; 5 ] |> AsyncSeq.ofSeq
19+ let expected = [ 2 ; 4 ; 6 ; 8 ; 10 ] |> Set.ofList
20+
21+ let actual =
22+ input
23+ |> AsyncSeq.mapAsyncUnorderedParallel ( fun x -> async {
24+ do ! Async.Sleep( 100 ) // Simulate work
25+ return x * 2
26+ })
27+ |> AsyncSeq.toListAsync
28+ |> Async.RunSynchronously
29+ |> Set.ofList
30+
31+ if actual = expected then
32+ printfn " ✅ All expected results present: %A " ( Set.toList actual)
33+ else
34+ printfn " ❌ Results mismatch. Expected: %A , Got: %A " ( Set.toList expected) ( Set.toList actual)
35+
36+ // Test 2: Exception handling
37+ let testExceptionHandling () =
38+ printfn " \n === Test 2: Exception Handling ==="
39+
40+ let input = [ 1 ; 2 ; 3 ; 4 ; 5 ] |> AsyncSeq.ofSeq
41+
42+ try
43+ input
44+ |> AsyncSeq.mapAsyncUnorderedParallel ( fun x -> async {
45+ if x = 3 then failwith " Test exception"
46+ return x * 2
47+ })
48+ |> AsyncSeq.toListAsync
49+ |> Async.RunSynchronously
50+ |> ignore
51+ printfn " ❌ Expected exception but none was thrown"
52+ with
53+ | ex -> printfn " ✅ Exception correctly propagated: %s " ex.Message
54+
55+ // Test 3: Order independence - results should come in any order
56+ let testOrderIndependence () =
57+ printfn " \n === Test 3: Order Independence ==="
58+
59+ let input = [ 1 ; 2 ; 3 ; 4 ; 5 ] |> AsyncSeq.ofSeq
60+ let results = List< int>()
61+
62+ input
63+ |> AsyncSeq.mapAsyncUnorderedParallel ( fun x -> async {
64+ // Longer sleep for smaller numbers to test unordered behavior
65+ do ! Async.Sleep( 600 - x * 100 )
66+ results.Add( x)
67+ return x
68+ })
69+ |> AsyncSeq.iter ignore
70+ |> Async.RunSynchronously
71+
72+ let resultOrder = results |> List.ofSeq
73+ printfn " Processing order: %A " resultOrder
74+
75+ // In unordered parallel, we expect larger numbers (shorter delays) to complete first
76+ if resultOrder <> [ 1 ; 2 ; 3 ; 4 ; 5 ] then
77+ printfn " ✅ Results processed in non-sequential order (expected for unordered)"
78+ else
79+ printfn " ⚠️ Results processed in sequential order (might be coincidental)"
80+
81+ // Test 4: Performance comparison
82+ let performanceComparison () =
83+ printfn " \n === Test 4: Performance Comparison ==="
84+
85+ let input = [ 1 .. 20 ] |> AsyncSeq.ofSeq
86+ let workload x = async {
87+ do ! Async.Sleep( 50 ) // Simulate I/O work
88+ return x * 2
89+ }
90+
91+ // Test ordered parallel
92+ let sw1 = Stopwatch.StartNew()
93+ let orderedResults =
94+ input
95+ |> AsyncSeq.mapAsyncParallel workload
96+ |> AsyncSeq.toListAsync
97+ |> Async.RunSynchronously
98+ sw1.Stop()
99+
100+ // Test unordered parallel
101+ let sw2 = Stopwatch.StartNew()
102+ let unorderedResults =
103+ input
104+ |> AsyncSeq.mapAsyncUnorderedParallel workload
105+ |> AsyncSeq.toListAsync
106+ |> Async.RunSynchronously
107+ |> List.sort // Sort for comparison
108+ sw2.Stop()
109+
110+ printfn " Ordered parallel: %d ms, results: %A " sw1.ElapsedMilliseconds orderedResults
111+ printfn " Unordered parallel: %d ms, results: %A " sw2.ElapsedMilliseconds unorderedResults
112+
113+ if List.sort orderedResults = unorderedResults then
114+ printfn " ✅ Both methods produce same results when sorted"
115+ else
116+ printfn " ❌ Results differ between methods"
117+
118+ let improvement = ( float sw1.ElapsedMilliseconds - float sw2.ElapsedMilliseconds) / float sw1.ElapsedMilliseconds * 100.0
119+ if improvement > 5.0 then
120+ printfn " ✅ Unordered is %.1f %% faster" improvement
121+ elif improvement < - 5.0 then
122+ printfn " ❌ Unordered is %.1f %% slower" (- improvement)
123+ else
124+ printfn " ➡️ Performance similar (%.1f %% difference)" improvement
125+
126+ // Test 5: Cancellation behavior
127+ let testCancellation () =
128+ printfn " \n === Test 5: Cancellation Behavior ==="
129+
130+ let input = [ 1 .. 20 ] |> AsyncSeq.ofSeq
131+ let cts = new CancellationTokenSource()
132+
133+ // Cancel after 500ms
134+ Async.Start( async {
135+ do ! Async.Sleep( 500 )
136+ cts.Cancel()
137+ })
138+
139+ let sw = Stopwatch.StartNew()
140+ try
141+ let work = input
142+ |> AsyncSeq.mapAsyncUnorderedParallel ( fun x -> async {
143+ do ! Async.Sleep( 200 ) // Each item takes 200ms
144+ return x
145+ })
146+ |> AsyncSeq.iter ( fun x -> printfn " Processed: %d " x)
147+
148+ Async.RunSynchronously( work, cancellationToken = cts.Token)
149+ printfn " ❌ Expected cancellation but completed normally in %d ms" sw.ElapsedMilliseconds
150+ with
151+ | :? OperationCanceledException ->
152+ sw.Stop()
153+ printfn " ✅ Cancellation handled correctly after %d ms" sw.ElapsedMilliseconds
154+ | ex -> printfn " ❌ Unexpected exception: %s " ex.Message
155+
156+ // Run all tests
157+ printfn " Testing mapAsyncUnorderedParallel Function"
158+ printfn " =========================================="
159+
160+ testBasicFunctionality()
161+ testExceptionHandling()
162+ testOrderIndependence()
163+ performanceComparison()
164+ testCancellation()
165+
166+ printfn " \n === All Tests Complete ==="
0 commit comments