From 56f226e8cfcccc61f5f401865d16239e2e700856 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Fri, 28 Oct 2022 23:53:40 +0200 Subject: [PATCH] Re-enable previously hanging and crashing tests, add extra, more complex, mutable state scenario --- src/FSharpy.TaskSeq.Test/TaskSeq.Realworld.fs | 6 +- ...Seq.StateTransitionBug-delayed.Tests.CE.fs | 75 +++++++++++++------ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/FSharpy.TaskSeq.Test/TaskSeq.Realworld.fs b/src/FSharpy.TaskSeq.Test/TaskSeq.Realworld.fs index 4805811a..47ce835d 100644 --- a/src/FSharpy.TaskSeq.Test/TaskSeq.Realworld.fs +++ b/src/FSharpy.TaskSeq.Test/TaskSeq.Realworld.fs @@ -158,13 +158,17 @@ type ``Real world tests``(output: ITestOutputHelper) = } + // This test used to have the following, which has since been solved through #42 + // please leave this test in, as it tests a case that's quite easily reached if we + // introduce mistakes in the resumable code. + // //System.InvalidOperationException: An attempt was made to transition a task to a final state when it had already completed. // at .$TaskSeq.Realworld.clo@58-4.MoveNext() in D:\Projects\OpenSource\Abel\TaskSeq\src\FSharpy.TaskSeq.Test\TaskSeq.Realworld.fs:line 77 // at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_0.<b__1>d.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 264 //--- End of stack trace from previous location --- // at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in /_/src/xunit.execution/Sdk/Frameworks/ExecutionTimer.cs:line 48 // at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in /_/src/xunit.core/Sdk/ExceptionAggregator.cs:line 90\ - [] + [] let ``Reading a 1MB buffered IAsync stream from start to finish InvalidOperationException`` () = task { let mutable count = 0 use reader = AsyncBufferedReader(output, Array.init 1_048_576 byte, 256) diff --git a/src/FSharpy.TaskSeq.Test/TaskSeq.StateTransitionBug-delayed.Tests.CE.fs b/src/FSharpy.TaskSeq.Test/TaskSeq.StateTransitionBug-delayed.Tests.CE.fs index d411f086..053de511 100644 --- a/src/FSharpy.TaskSeq.Test/TaskSeq.StateTransitionBug-delayed.Tests.CE.fs +++ b/src/FSharpy.TaskSeq.Test/TaskSeq.StateTransitionBug-delayed.Tests.CE.fs @@ -56,11 +56,8 @@ let ``CE empty taskSeq, GetAsyncEnumerator multiple times`` variant = task { () } -[] +// Note: this test used to hang (#42), please leave it in, no matter how silly it looks +[] let ``CE empty taskSeq, GetAsyncEnumerator multiple times and then MoveNextAsync`` variant = task { let tskSeq = getEmptyVariant variant use enumerator = tskSeq.GetAsyncEnumerator() @@ -68,11 +65,8 @@ let ``CE empty taskSeq, GetAsyncEnumerator multiple times and then MoveNextAsyn do! moveNextAndCheck false enumerator } -[] +// Note: this test used to cause xUnit to crash (#42), please leave it in, no matter how silly it looks +[] let ``CE empty taskSeq, GetAsyncEnumerator + MoveNextAsync multiple times`` variant = task { let tskSeq = getEmptyVariant variant use enumerator1 = tskSeq.GetAsyncEnumerator() @@ -84,11 +78,8 @@ let ``CE empty taskSeq, GetAsyncEnumerator + MoveNextAsync multiple times`` vari do! moveNextAndCheck false enumerator2 // new hone should also work without raising } -[] +// Note: this test used to cause xUnit to crash (#42), please leave it in, no matter how silly it looks +[] let ``CE empty taskSeq, GetAsyncEnumerator + MoveNextAsync in a loop`` variant = task { let tskSeq = getEmptyVariant variant @@ -193,7 +184,8 @@ let ``CE taskSeq, MoveNext too far`` () = task { enum.Current |> should equal Guid.Empty // we return Unchecked.defaultof, which is Guid.Empty for guids } -[] +// Note: this test used to cause xUnit to crash (#42), please leave it in, no matter how silly it looks +[] let ``CE taskSeq, call GetAsyncEnumerator twice, both should have equal behavior`` () = task { let tskSeq = taskSeq { do! delayRandom () @@ -218,7 +210,8 @@ let ``CE taskSeq, call GetAsyncEnumerator twice, both should have equal behavior do! moveNextAndCheckCurrent false 0 enum2 // this used to be an error, see issue #39 and PR #42 } -[] +// Note: this test used to cause xUnit to crash (#42), please leave it in, no matter how silly it looks +[] let ``CE taskSeq, cal GetAsyncEnumerator twice -- in lockstep`` () = task { let tskSeq = taskSeq { do! delayRandom () @@ -244,7 +237,8 @@ let ``CE taskSeq, cal GetAsyncEnumerator twice -- in lockstep`` () = task { do! moveNextAndCheckCurrent false 0 enum2 // this used to be an error, see issue #39 and PR #42 } -[] +// Note: this test used to cause xUnit to crash (#42), please leave it in, no matter how silly it looks +[] let ``CE taskSeq, call GetAsyncEnumerator twice -- after full iteration`` () = task { let tskSeq = taskSeq { yield 1 @@ -267,7 +261,8 @@ let ``CE taskSeq, call GetAsyncEnumerator twice -- after full iteration`` () = t do! moveNextAndCheckCurrent false 0 enum2 // this used to be an error, see issue #39 and PR #42 } -[] +// Note: this test used to hang (#42), please leave it in, no matter how silly it looks +[] let ``CE taskSeq, call GetAsyncEnumerator twice -- random mixed iteration`` () = task { let tskSeq = taskSeq { yield 1 @@ -323,7 +318,8 @@ let ``CE taskSeq, call GetAsyncEnumerator twice -- random mixed iteration`` () = enum1.Current |> should equal 0 } -[] +// Note: this test used to hang (#42), please leave it in, no matter how silly it looks +[] let ``TaskSeq-toArray can be applied multiple times to the same sequence`` () = let tq = taskSeq { yield! [ 1..3 ] @@ -336,7 +332,38 @@ let ``TaskSeq-toArray can be applied multiple times to the same sequence`` () = let (results2: _[]) = tq |> TaskSeq.toArray let (results3: _[]) = tq |> TaskSeq.toArray let (results4: _[]) = tq |> TaskSeq.toArray - results1 |> should equal [| 1..10 |] - results2 |> should equal [| 1..10 |] - results3 |> should equal [| 1..10 |] - results4 |> should equal [| 1..10 |] + results1 |> should equal [| 1..7 |] + results2 |> should equal [| 1..7 |] // no mutable state in taskSeq, multi iter remains stable + results3 |> should equal [| 1..7 |] // id + results4 |> should equal [| 1..7 |] // id + +// Note: this test used to hang (#42), please leave it in, no matter how silly it looks +[] +let ``TaskSeq-toArray can be applied multiple times to the same sequence -- mutable state`` () = + let mutable before, middle, after = (0, 0, 0) + + let tq = taskSeq { + before <- before + 1 + yield before + yield! [ 100..120 ] + do! delayRandom () + middle <- middle + 1 + yield middle + yield! [ 100..120 ] + do! delayRandom () + after <- after + 1 + yield after + } + + let (results1: _ list) = tq |> TaskSeq.toList + let (results2: _ list) = tq |> TaskSeq.toList + let (results3: _ list) = tq |> TaskSeq.toList + let (results4: _ list) = tq |> TaskSeq.toList + + let expectMutatedTo a = (a :: [ 100..120 ] @ [ a ] @ [ 100..120 ] @ [ a ]) + results1 + |> should equal (expectMutatedTo 1) + + results2 |> should equal [| 1..7 |] // no mutable state in taskSeq, multi iter remains stable + results3 |> should equal [| 1..7 |] // id + results4 |> should equal [| 1..7 |] // id