From dce8280d461ed53e5caa1a450176bf03a5c45080 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:33:54 +0000 Subject: [PATCH 01/18] Initial plan From 41cad23783fd65e4f4b5e91f6563dc3485b8ee34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:37:26 +0000 Subject: [PATCH 02/18] test: add integration parity tests against direct R3 behavior Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/b095f03a-938a-4a06-95c5-e5801a65c7cd Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../FSharp.Control.R3.Tests.fsproj | 1 + .../IntegrationParityTests.fs | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs diff --git a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj index 9c07d08..add3fe7 100644 --- a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj +++ b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj @@ -7,6 +7,7 @@ + diff --git a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs new file mode 100644 index 0000000..03e30c0 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs @@ -0,0 +1,76 @@ +namespace FSharp.Control.R3.Tests + +open System.Threading +open System.Threading.Tasks +open FSharp.Control.R3 +open Microsoft.VisualStudio.TestTools.UnitTesting + +[] +type IntegrationParityTests () = + + static member private SourceValues = [ 1; 2; 3; 4; 5; 6 ] + + static member private CreateSource () = R3.Observable.ToObservable IntegrationParityTests.SourceValues + + [] + member _.``Observable wrappers should match direct R3 pipeline results`` () : Task = task { + let expectedPipeline = + IntegrationParityTests.CreateSource () + |> fun source -> R3.ObservableExtensions.Where (source, fun x -> x % 2 = 0) + |> fun source -> R3.ObservableExtensions.Select (source, fun x -> x * 10) + |> fun source -> R3.ObservableExtensions.Skip (source, 1) + |> fun source -> R3.ObservableExtensions.Take (source, 1) + + let actualPipeline = + IntegrationParityTests.CreateSource () + |> FSharp.Control.R3.Observable.filter (fun x -> x % 2 = 0) + |> FSharp.Control.R3.Observable.map (fun x -> x * 10) + |> FSharp.Control.R3.Observable.skip 1 + |> FSharp.Control.R3.Observable.take 1 + + let! expected = R3.ObservableExtensions.ToArrayAsync expectedPipeline + let! actual = R3.ObservableExtensions.ToArrayAsync actualPipeline + + CollectionAssert.AreEqual (expected, actual, "Wrapper pipeline result must match direct R3 pipeline result.") + } + + [] + member _.``Async Observable toArray should match direct R3 ToArrayAsync`` () : Task = task { + let! expected = R3.ObservableExtensions.ToArrayAsync (IntegrationParityTests.CreateSource ()) + + let! actual = + IntegrationParityTests.CreateSource () + |> FSharp.Control.R3.Async.Observable.toArray + |> Async.StartImmediateAsTask + + CollectionAssert.AreEqual (expected, actual, "Async wrapper toArray must match direct R3 ToArrayAsync.") + } + + [] + member _.``Task Observable mapAsync should match direct R3 SelectAwait`` () : Task = task { + let options = ProcessingOptions.Default + + let selector x (ct : CancellationToken) = + ValueTask (Task.FromResult (x + (if ct.IsCancellationRequested then 0 else 1))) + + let expectedPipeline = + R3.ObservableExtensions.SelectAwait ( + IntegrationParityTests.CreateSource (), + selector, + options.AwaitOperation, + options.ConfigureAwait, + options.CancelOnCompleted, + options.MaxConcurrent + ) + + let actualPipeline = + IntegrationParityTests.CreateSource () + |> FSharp.Control.R3.Task.Observable.mapAsync + options + (fun (ct : CancellationToken) x -> Task.FromResult (x + (if ct.IsCancellationRequested then 0 else 1))) + + let! expected = R3.ObservableExtensions.ToArrayAsync expectedPipeline + let! actual = R3.ObservableExtensions.ToArrayAsync actualPipeline + + CollectionAssert.AreEqual (expected, actual, "Task wrapper mapAsync must match direct R3 SelectAwait pipeline.") + } From 59797de5cecb290ce8f427f921e19c0839024dcb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:41:41 +0000 Subject: [PATCH 03/18] test: refine integration parity assertions and readability Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/b095f03a-938a-4a06-95c5-e5801a65c7cd Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../IntegrationParityTests.fs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs index 03e30c0..c1849d2 100644 --- a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs +++ b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs @@ -5,28 +5,31 @@ open System.Threading.Tasks open FSharp.Control.R3 open Microsoft.VisualStudio.TestTools.UnitTesting +module ObservableModule = FSharp.Control.R3.Observable +module AsyncObservable = FSharp.Control.R3.Async.Observable +module TaskObservable = FSharp.Control.R3.Task.Observable + [] type IntegrationParityTests () = - static member private SourceValues = [ 1; 2; 3; 4; 5; 6 ] + static member private SourceValues = [| 1; 2; 3; 4; 5; 6 |] - static member private CreateSource () = R3.Observable.ToObservable IntegrationParityTests.SourceValues + static member private CreateSourceObservable () = R3.Observable.ToObservable IntegrationParityTests.SourceValues [] member _.``Observable wrappers should match direct R3 pipeline results`` () : Task = task { - let expectedPipeline = - IntegrationParityTests.CreateSource () - |> fun source -> R3.ObservableExtensions.Where (source, fun x -> x % 2 = 0) - |> fun source -> R3.ObservableExtensions.Select (source, fun x -> x * 10) - |> fun source -> R3.ObservableExtensions.Skip (source, 1) - |> fun source -> R3.ObservableExtensions.Take (source, 1) + let expectedSource = IntegrationParityTests.CreateSourceObservable () + let expectedFiltered = R3.ObservableExtensions.Where (expectedSource, fun x -> x % 2 = 0) + let expectedSelected = R3.ObservableExtensions.Select (expectedFiltered, fun x -> x * 10) + let expectedSkipped = R3.ObservableExtensions.Skip (expectedSelected, 1) + let expectedPipeline = R3.ObservableExtensions.Take (expectedSkipped, 1) let actualPipeline = - IntegrationParityTests.CreateSource () - |> FSharp.Control.R3.Observable.filter (fun x -> x % 2 = 0) - |> FSharp.Control.R3.Observable.map (fun x -> x * 10) - |> FSharp.Control.R3.Observable.skip 1 - |> FSharp.Control.R3.Observable.take 1 + IntegrationParityTests.CreateSourceObservable () + |> ObservableModule.filter (fun x -> x % 2 = 0) + |> ObservableModule.map (fun x -> x * 10) + |> ObservableModule.skip 1 + |> ObservableModule.take 1 let! expected = R3.ObservableExtensions.ToArrayAsync expectedPipeline let! actual = R3.ObservableExtensions.ToArrayAsync actualPipeline @@ -36,12 +39,12 @@ type IntegrationParityTests () = [] member _.``Async Observable toArray should match direct R3 ToArrayAsync`` () : Task = task { - let! expected = R3.ObservableExtensions.ToArrayAsync (IntegrationParityTests.CreateSource ()) + let! expected = R3.ObservableExtensions.ToArrayAsync (IntegrationParityTests.CreateSourceObservable ()) let! actual = - IntegrationParityTests.CreateSource () - |> FSharp.Control.R3.Async.Observable.toArray - |> Async.StartImmediateAsTask + IntegrationParityTests.CreateSourceObservable () + |> AsyncObservable.toArray + |> Async.StartAsTask CollectionAssert.AreEqual (expected, actual, "Async wrapper toArray must match direct R3 ToArrayAsync.") } @@ -50,13 +53,12 @@ type IntegrationParityTests () = member _.``Task Observable mapAsync should match direct R3 SelectAwait`` () : Task = task { let options = ProcessingOptions.Default - let selector x (ct : CancellationToken) = - ValueTask (Task.FromResult (x + (if ct.IsCancellationRequested then 0 else 1))) + let addOne x = x + 1 let expectedPipeline = R3.ObservableExtensions.SelectAwait ( - IntegrationParityTests.CreateSource (), - selector, + IntegrationParityTests.CreateSourceObservable (), + (fun x (_ : CancellationToken) -> ValueTask.FromResult (addOne x)), options.AwaitOperation, options.ConfigureAwait, options.CancelOnCompleted, @@ -64,10 +66,8 @@ type IntegrationParityTests () = ) let actualPipeline = - IntegrationParityTests.CreateSource () - |> FSharp.Control.R3.Task.Observable.mapAsync - options - (fun (ct : CancellationToken) x -> Task.FromResult (x + (if ct.IsCancellationRequested then 0 else 1))) + IntegrationParityTests.CreateSourceObservable () + |> TaskObservable.mapAsync options (fun (_ : CancellationToken) x -> Task.FromResult (addOne x)) let! expected = R3.ObservableExtensions.ToArrayAsync expectedPipeline let! actual = R3.ObservableExtensions.ToArrayAsync actualPipeline From 49b597746d64ad280aa190d80acb8a5a1011e1ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 17:29:44 +0000 Subject: [PATCH 04/18] test: use fluent direct R3 style in expected parity pipeline Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/78d0c1b6-ce13-4e66-afab-ff7ae29025dc Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../IntegrationParityTests.fs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs index c1849d2..36aeb82 100644 --- a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs +++ b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs @@ -4,6 +4,7 @@ open System.Threading open System.Threading.Tasks open FSharp.Control.R3 open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 module ObservableModule = FSharp.Control.R3.Observable module AsyncObservable = FSharp.Control.R3.Async.Observable @@ -18,11 +19,12 @@ type IntegrationParityTests () = [] member _.``Observable wrappers should match direct R3 pipeline results`` () : Task = task { - let expectedSource = IntegrationParityTests.CreateSourceObservable () - let expectedFiltered = R3.ObservableExtensions.Where (expectedSource, fun x -> x % 2 = 0) - let expectedSelected = R3.ObservableExtensions.Select (expectedFiltered, fun x -> x * 10) - let expectedSkipped = R3.ObservableExtensions.Skip (expectedSelected, 1) - let expectedPipeline = R3.ObservableExtensions.Take (expectedSkipped, 1) + let expectedPipeline = + IntegrationParityTests.CreateSourceObservable () + |> _.Where(fun x -> x % 2 = 0) + |> _.Select(fun x -> x * 10) + |> _.Skip(1) + |> _.Take(1) let actualPipeline = IntegrationParityTests.CreateSourceObservable () From f5732efadfdc9f9ee4f1517272c3610352b8a13c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:33:54 +0000 Subject: [PATCH 05/18] Initial plan Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> From e8fe3db77dc69db855bcd0fa2463a934cfb84091 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:33:54 +0000 Subject: [PATCH 06/18] Initial plan Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> From 8367eecbbcd39a68cb189313fc4dcf4e765f130a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:33:54 +0000 Subject: [PATCH 07/18] Initial plan Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> From 1cc17e714946dd3187e2684eeb8f0371f5babf02 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 23:27:57 +0000 Subject: [PATCH 08/18] test: organize coverage by module and function Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/1a9a39d4-61e8-489f-bff8-c1cf1814e0d4 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../AsyncObservable/AggregateTests.fs | 23 ++++++ .../AsyncObservable/AllTests.fs | 23 ++++++ .../AsyncObservable/ExistsAsyncTests.fs | 18 +++++ .../AsyncObservable/FirstAsyncTests.fs | 18 +++++ .../AsyncObservable/IterAsyncTests.fs | 24 ++++++ .../AsyncObservable/IterTests.fs | 23 ++++++ .../AsyncObservable/LengthTests.fs | 18 +++++ .../AsyncObservable/MapAsyncTests.fs | 36 +++++++++ .../AsyncObservable/OfAsyncTests.fs | 19 +++++ .../AsyncObservable/ToArrayTests.fs | 18 +++++ .../AsyncObservable/ToListTests.fs | 18 +++++ tests/FSharp.Control.R3.Tests/BuilderTests.fs | 53 ------------- .../FSharp.Control.R3.Tests.fsproj | 8 +- .../IntegrationParityTests.fs | 78 ------------------- .../Observable/AsObservableTests.fs | 20 +++++ .../Observable/BindTests.fs | 23 ++++++ .../Observable/CastTests.fs | 19 +++++ .../Observable/CatchTests.fs | 21 +++++ .../Observable/ChunkBySizeTests.fs | 23 ++++++ .../Observable/ChunkByTests.fs | 28 +++++++ .../Observable/ConcatTests.fs | 19 +++++ .../Observable/DistinctTests.fs | 19 +++++ .../Observable/EmptyTests.fs | 17 ++++ .../Observable/FilterTests.fs | 19 +++++ .../Observable/MapTests.fs | 19 +++++ .../Observable/MapiTests.fs | 23 ++++++ .../Observable/MergeTests.fs | 23 ++++++ .../Observable/OfTypeTests.fs | 19 +++++ .../Observable/SingletonTests.fs | 17 ++++ .../Observable/SkipTests.fs | 19 +++++ .../Observable/TakeTests.fs | 19 +++++ .../Observable/WhereTests.fs | 19 +++++ .../ObservableTests.fs | 65 ---------------- .../ProcessingOptions/AsyncWindowTests.fs | 21 +++++ .../MillisecondsCountTests.fs | 19 +++++ .../ProcessingOptions/MillisecondsTests.fs | 18 +++++ .../ProcessingOptions/TimeSpanCountTests.fs | 19 +++++ .../ProcessingOptions/TimeSpanTests.fs | 18 +++++ .../TaskObservable/AggregateTests.fs | 21 +++++ .../TaskObservable/AllTests.fs | 21 +++++ .../TaskObservable/ExistsAsyncTests.fs | 21 +++++ .../TaskObservable/FirstAsyncTests.fs | 21 +++++ .../TaskObservable/IterAsyncTests.fs | 23 ++++++ .../TaskObservable/IterTests.fs | 22 ++++++ .../TaskObservable/LengthTests.fs | 19 +++++ .../TaskObservable/MapAsyncTests.fs | 23 ++++++ .../TestHelpers/TestHelpers.fs | 19 +++++ 47 files changed, 894 insertions(+), 199 deletions(-) create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/BuilderTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/BindTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/CastTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/MapTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/ObservableTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs new file mode 100644 index 0000000..648f76f --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type AggregateTests () = + [] + member _.``aggregate should match direct AggregateAsync`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let expected = + ObservableExtensions.AggregateAsync (source, 0, (fun acc x -> acc + x), TestHelpers.cancellationToken) + let! actual = + source + |> Observable.aggregate 0 (fun acc x -> acc + x) + |> Async.StartAsTask + Assert.AreEqual (expected.Result, actual, "Async aggregate must match direct AggregateAsync result.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs new file mode 100644 index 0000000..0486b3d --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type AllTests () = + [] + member _.``all should match direct AllAsync`` () : Task = task { + let source = TestHelpers.createObservable [| 2; 4; 6 |] + let expected = + ObservableExtensions.AllAsync (source, (fun x -> x % 2 = 0), TestHelpers.cancellationToken) + let! actual = + source + |> Observable.all (fun x -> x % 2 = 0) + |> Async.StartAsTask + Assert.AreEqual (expected.Result, actual, "Async all must match direct AllAsync result.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs new file mode 100644 index 0000000..2302854 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type ExistsAsyncTests () = + [] + member _.``existsAsync should detect available values`` () : Task = task { + let source = TestHelpers.createObservable [| 1 |] + let! actual = source |> Observable.existsAsync |> Async.StartAsTask + Assert.IsTrue (actual, "existsAsync must return true for non-empty sequence.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs new file mode 100644 index 0000000..d7766e4 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type FirstAsyncTests () = + [] + member _.``firstAsync should return first value`` () : Task = task { + let source = TestHelpers.createObservable [| 9; 8 |] + let! actual = source |> Observable.firstAsync |> Async.StartAsTask + Assert.AreEqual (9, actual, "firstAsync must return the first emitted value.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs new file mode 100644 index 0000000..d936e87 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs @@ -0,0 +1,24 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type IterAsyncTests () = + [] + member _.``iterAsync should await async action for each value`` () : Task = task { + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iterAsync options (fun x -> async { sum <- sum + x }) + |> Async.StartAsTask + :> Task + Assert.AreEqual (6, sum, "iterAsync must apply asynchronous action to every value.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs new file mode 100644 index 0000000..bca27fd --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type IterTests () = + [] + member _.``iter should invoke action for each value`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iter (fun x -> sum <- sum + x) + |> Async.StartAsTask + :> Task + Assert.AreEqual (6, sum, "iter must invoke action for each emitted value.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs new file mode 100644 index 0000000..218d979 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type LengthTests () = + [] + member _.``length should count values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = source |> Observable.length |> Async.StartAsTask + Assert.AreEqual (4, actual, "length must return emitted value count.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs new file mode 100644 index 0000000..392386a --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs @@ -0,0 +1,36 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type MapAsyncTests () = + [] + member _.``mapAsync should match direct SelectAwait`` () : Task = task { + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let expected = + ObservableExtensions.SelectAwait ( + source, + (fun x (_ : Threading.CancellationToken) -> ValueTask.FromResult (x + 1)), + options.AwaitOperation, + options.ConfigureAwait, + options.CancelOnCompleted, + options.MaxConcurrent + ) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + + let actual = + source + |> Observable.mapAsync options (fun x -> async { return x + 1 }) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + + CollectionAssert.AreEqual (expected, actual, "Async mapAsync must match direct SelectAwait behavior.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs new file mode 100644 index 0000000..35e2271 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type OfAsyncTests () = + [] + member _.``ofAsync should emit computation result`` () = + let actual = + Observable.ofAsync (async { return 7 }) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 7 |], actual, "ofAsync must emit the async computation result.") diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs new file mode 100644 index 0000000..f2f49aa --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type ToArrayTests () = + [] + member _.``toArray should return all values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let! actual = source |> Observable.toArray |> Async.StartAsTask + CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "toArray must return all source values.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs new file mode 100644 index 0000000..d8dd37c --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.AsyncObservable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Async +open FSharp.Control.R3.Tests + +[] +type ToListTests () = + [] + member _.``toList should return all values as list`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let! actual = source |> Observable.toList |> Async.StartAsTask + CollectionAssert.AreEqual ([| 1; 2; 3 |], actual |> List.toArray, "toList must return all source values in order.") + } diff --git a/tests/FSharp.Control.R3.Tests/BuilderTests.fs b/tests/FSharp.Control.R3.Tests/BuilderTests.fs deleted file mode 100644 index 68f41c0..0000000 --- a/tests/FSharp.Control.R3.Tests/BuilderTests.fs +++ /dev/null @@ -1,53 +0,0 @@ -namespace FSharp.Control.R3.Tests - -open System -open System.Threading.Tasks -open FSharp.Control.R3.Async -open FSharp.Control.R3.Observable.Builders -open Microsoft.VisualStudio.TestTools.UnitTesting -open Swensen.Unquote - -[] -type BuilderTests () = - - [] - member _.``Test builder rxquery`` () = - - let mutable hasvisited = false - use r3Bus = new R3.Subject () - - let interesting = rxquery { - for i in r3Bus do - where (i % 2 = 0) - select i - - // Same as: - // if i % 2 = 0 then - // yield i - - } - - // No-one listens yet (vs R3.ReplaySubject - r3Bus.OnNext 2 - - use subscription = - R3.ObservableExtensions.SubscribeAwait ( - interesting, - fun i cancellationToken -> - task { - // Listen events - - hasvisited <- true - - Assert.AreEqual (4, i) - - return () - } - |> System.Threading.Tasks.ValueTask - ) - - // Publish some events, "4" should be heard - [ 3..5 ] |> List.iter r3Bus.OnNext - // Note: Query will not be awaited, that's why delay. - System.Threading.Thread.Sleep 300 - Assert.AreEqual (true, hasvisited) diff --git a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj index add3fe7..ff7535f 100644 --- a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj +++ b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj @@ -5,9 +5,11 @@ - - - + + + + + diff --git a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs b/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs deleted file mode 100644 index 36aeb82..0000000 --- a/tests/FSharp.Control.R3.Tests/IntegrationParityTests.fs +++ /dev/null @@ -1,78 +0,0 @@ -namespace FSharp.Control.R3.Tests - -open System.Threading -open System.Threading.Tasks -open FSharp.Control.R3 -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 - -module ObservableModule = FSharp.Control.R3.Observable -module AsyncObservable = FSharp.Control.R3.Async.Observable -module TaskObservable = FSharp.Control.R3.Task.Observable - -[] -type IntegrationParityTests () = - - static member private SourceValues = [| 1; 2; 3; 4; 5; 6 |] - - static member private CreateSourceObservable () = R3.Observable.ToObservable IntegrationParityTests.SourceValues - - [] - member _.``Observable wrappers should match direct R3 pipeline results`` () : Task = task { - let expectedPipeline = - IntegrationParityTests.CreateSourceObservable () - |> _.Where(fun x -> x % 2 = 0) - |> _.Select(fun x -> x * 10) - |> _.Skip(1) - |> _.Take(1) - - let actualPipeline = - IntegrationParityTests.CreateSourceObservable () - |> ObservableModule.filter (fun x -> x % 2 = 0) - |> ObservableModule.map (fun x -> x * 10) - |> ObservableModule.skip 1 - |> ObservableModule.take 1 - - let! expected = R3.ObservableExtensions.ToArrayAsync expectedPipeline - let! actual = R3.ObservableExtensions.ToArrayAsync actualPipeline - - CollectionAssert.AreEqual (expected, actual, "Wrapper pipeline result must match direct R3 pipeline result.") - } - - [] - member _.``Async Observable toArray should match direct R3 ToArrayAsync`` () : Task = task { - let! expected = R3.ObservableExtensions.ToArrayAsync (IntegrationParityTests.CreateSourceObservable ()) - - let! actual = - IntegrationParityTests.CreateSourceObservable () - |> AsyncObservable.toArray - |> Async.StartAsTask - - CollectionAssert.AreEqual (expected, actual, "Async wrapper toArray must match direct R3 ToArrayAsync.") - } - - [] - member _.``Task Observable mapAsync should match direct R3 SelectAwait`` () : Task = task { - let options = ProcessingOptions.Default - - let addOne x = x + 1 - - let expectedPipeline = - R3.ObservableExtensions.SelectAwait ( - IntegrationParityTests.CreateSourceObservable (), - (fun x (_ : CancellationToken) -> ValueTask.FromResult (addOne x)), - options.AwaitOperation, - options.ConfigureAwait, - options.CancelOnCompleted, - options.MaxConcurrent - ) - - let actualPipeline = - IntegrationParityTests.CreateSourceObservable () - |> TaskObservable.mapAsync options (fun (_ : CancellationToken) x -> Task.FromResult (addOne x)) - - let! expected = R3.ObservableExtensions.ToArrayAsync expectedPipeline - let! actual = R3.ObservableExtensions.ToArrayAsync actualPipeline - - CollectionAssert.AreEqual (expected, actual, "Task wrapper mapAsync must match direct R3 SelectAwait pipeline.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs b/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs new file mode 100644 index 0000000..884792b --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs @@ -0,0 +1,20 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type AsObservableTests () = + [] + member _.``asObservable should keep source values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let expected = source |> TestHelpers.toArrayTask |> TestHelpers.waitTask + let actual = + source + |> Observable.asObservable + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual (expected, actual, "asObservable must preserve source values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/BindTests.fs b/tests/FSharp.Control.R3.Tests/Observable/BindTests.fs new file mode 100644 index 0000000..0ea3c32 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/BindTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type BindTests () = + [] + member _.``bind should match SelectMany behavior`` () = + let source = TestHelpers.createObservable [| 1; 2 |] + let expected = + ObservableExtensions.SelectMany (source, fun x -> Observable.Return (x * 10)) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + let actual = + source + |> Observable.bind (fun x -> Observable.singleton (x * 10)) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual (expected, actual, "bind must match SelectMany behavior.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs b/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs new file mode 100644 index 0000000..d0f14df --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type CastTests () = + [] + member _.``cast should convert values to target type`` () = + let source = TestHelpers.createObservable [| box 1; box 2 |] + let actual = + source + |> Observable.cast + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 1; 2 |], actual, "cast must convert boxed integers.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs b/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs new file mode 100644 index 0000000..bc583a1 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs @@ -0,0 +1,21 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type CatchTests () = + [] + member _.``catch should continue with fallback observable`` () = + use subject = new Subject () + let recovered = + subject + |> Observable.catch (fun (_ : exn) -> Observable.singleton 99) + let pending = TestHelpers.toArrayTask recovered + subject.OnNext 1 + subject.OnCompleted (Result.Failure (Exception ("boom"))) + let actual = TestHelpers.waitTask pending + CollectionAssert.AreEqual ([| 1; 99 |], actual, "catch must append fallback values after source error.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs new file mode 100644 index 0000000..73d419a --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type ChunkBySizeTests () = + [] + member _.``chunkBySize should split by fixed size`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4; 5 |] + let actual = + source + |> Observable.chunkBySize 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + |> Array.map Seq.toArray + Assert.AreEqual (3, actual.Length, "chunkBySize should produce expected number of chunks.") + CollectionAssert.AreEqual ([| 1; 2 |], actual[0], "First chunk should contain first two items.") + CollectionAssert.AreEqual ([| 3; 4 |], actual[1], "Second chunk should contain next two items.") + CollectionAssert.AreEqual ([| 5 |], actual[2], "Third chunk should contain the remaining item.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs new file mode 100644 index 0000000..b9d50d8 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs @@ -0,0 +1,28 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type ChunkByTests () = + [] + member _.``chunkBy ChunkCount should match chunkBySize`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let expected = + source + |> Observable.chunkBySize 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + |> Array.map Seq.toArray + let actual = + source + |> Observable.chunkBy (ChunkCount 2) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + |> Array.map Seq.toArray + Assert.AreEqual (expected.Length, actual.Length, "chunkBy ChunkCount must produce same chunk count as chunkBySize.") + CollectionAssert.AreEqual (expected[0], actual[0], "First chunk must match chunkBySize output.") + CollectionAssert.AreEqual (expected[1], actual[1], "Second chunk must match chunkBySize output.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs new file mode 100644 index 0000000..91ff5b0 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type ConcatTests () = + [] + member _.``concat should append second sequence`` () = + let first = TestHelpers.createObservable [| 1; 2 |] + let second = TestHelpers.createObservable [| 3; 4 |] + let actual = + Observable.concat first second + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 1; 2; 3; 4 |], actual, "concat must append second observable after first completes.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs b/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs new file mode 100644 index 0000000..042c7df --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type DistinctTests () = + [] + member _.``distinct should remove duplicate values`` () = + let source = TestHelpers.createObservable [| 1; 1; 2; 2; 3 |] + let actual = + source + |> Observable.distinct + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "distinct must emit unique values in encounter order.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs b/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs new file mode 100644 index 0000000..060102c --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs @@ -0,0 +1,17 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type EmptyTests () = + [] + member _.``empty should emit no values`` () = + let actual = + Observable.empty () + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + Assert.AreEqual (0, actual.Length, "empty must complete without values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs b/tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs new file mode 100644 index 0000000..c4d6170 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type FilterTests () = + [] + member _.``filter should keep matching values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.filter (fun x -> x % 2 = 0) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 2; 4 |], actual, "filter must keep only matching elements.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapTests.fs new file mode 100644 index 0000000..e163ed2 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/MapTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type MapTests () = + [] + member _.``map should transform each value`` () = + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let actual = + source + |> Observable.map (fun x -> x * 10) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 10; 20; 30 |], actual, "map must transform each emitted value.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs new file mode 100644 index 0000000..e52ab8c --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type MapiTests () = + [] + member _.``mapi should pass index as first argument`` () = + let source = TestHelpers.createObservable [| 4; 5; 6 |] + let expected = + ObservableExtensions.Select (source, fun value index -> (index * 10) + value) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + let actual = + source + |> Observable.mapi (fun index value -> (index * 10) + value) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual (expected, actual, "mapi must pass index first and value second.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs new file mode 100644 index 0000000..8066eb3 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type MergeTests () = + [] + member _.``merge should match direct R3 merge`` () = + let source1 = TestHelpers.createObservable [| 1; 2 |] + let source2 = TestHelpers.createObservable [| 10; 20 |] + let expected = + ObservableExtensions.Merge (source1, source2) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + let actual = + Observable.merge (source1, source2) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual (expected, actual, "merge wrapper must match direct R3 merge output.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs new file mode 100644 index 0000000..1f1e69d --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type OfTypeTests () = + [] + member _.``ofType should keep only requested runtime type`` () = + let source = TestHelpers.createObservable [| box 1; box "x"; box 2 |] + let actual = + source + |> Observable.ofType + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 1; 2 |], actual, "ofType must emit only values of requested type.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs b/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs new file mode 100644 index 0000000..9431432 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs @@ -0,0 +1,17 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type SingletonTests () = + [] + member _.``singleton should emit one value`` () = + let actual = + Observable.singleton 42 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 42 |], actual, "singleton must emit exactly one value.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs b/tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs new file mode 100644 index 0000000..6429b3e --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type SkipTests () = + [] + member _.``skip should skip leading values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.skip 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 3; 4 |], actual, "skip must ignore the configured number of leading values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs new file mode 100644 index 0000000..cb997b4 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type TakeTests () = + [] + member _.``take should keep leading values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.take 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 1; 2 |], actual, "take must emit only the configured number of leading values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs b/tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs new file mode 100644 index 0000000..24e168c --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type WhereTests () = + [] + member _.``where should keep matching values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.where (fun x -> x > 2) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 3; 4 |], actual, "where must keep values matching the predicate.") diff --git a/tests/FSharp.Control.R3.Tests/ObservableTests.fs b/tests/FSharp.Control.R3.Tests/ObservableTests.fs deleted file mode 100644 index e45d2ce..0000000 --- a/tests/FSharp.Control.R3.Tests/ObservableTests.fs +++ /dev/null @@ -1,65 +0,0 @@ -namespace FSharp.Control.R3.Tests - -open System -open System.Threading.Tasks -open FSharp.Control.R3.Async -open Microsoft.VisualStudio.TestTools.UnitTesting -open Swensen.Unquote - -[] -type ObservableTests () = - - [] - member _.``Test length`` () : Task = - - async { - use r3Bus = new R3.Subject () - - r3Bus.OnNext 1 - - let lengthObs = Observable.length r3Bus - - r3Bus.OnNext 2 - r3Bus.OnNext 3 - r3Bus.OnCompleted (R3.Result.Success) - - let! res = lengthObs - - Assert.AreEqual (0, res) - - } - |> Async.StartImmediateAsTask - :> Task - - - [] - member _.``Test filter`` () = - - let mutable hasvisited = false - use r3Bus = new R3.Subject () - let interesting = - r3Bus - |> FSharp.Control.R3.Observable.filter (fun x -> x % 2 = 0) - - // No-one listens yet (vs R3.ReplaySubject - r3Bus.OnNext 2 - - use subscription = - R3.ObservableExtensions.SubscribeAwait ( - interesting, - fun i cancellationToken -> - task { - // Listen events - - hasvisited <- true - - Assert.AreEqual (4, i) - - return () - } - |> System.Threading.Tasks.ValueTask - ) - - // Publish some events, "4" should be heard - [ 3..5 ] |> List.iter r3Bus.OnNext - Assert.AreEqual (true, hasvisited) diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs new file mode 100644 index 0000000..df8169d --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs @@ -0,0 +1,21 @@ +namespace FSharp.Control.R3.Tests.ProcessingOptions + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open FSharp.Control.R3 + +[] +type AsyncWindowTests () = + [] + member _.``AsyncWindow should wrap async callback`` () : Task = task { + let mutable observed = 0 + let configuration = ChunkConfiguration.AsyncWindow (fun value -> async { observed <- value }) + match configuration with + | ChunkAsyncWindow (callback, configureAwait) -> + do! callback.Invoke (11, CancellationToken.None) + Assert.AreEqual (11, observed, "AsyncWindow helper must invoke wrapped callback with value.") + Assert.IsTrue (configureAwait, "AsyncWindow helper must set configureAwait to true.") + | _ -> Assert.Fail ("AsyncWindow helper must create ChunkAsyncWindow configuration.") + } diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs new file mode 100644 index 0000000..f784008 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.ProcessingOptions + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open FSharp.Control.R3 +open R3 + +[] +type MillisecondsCountTests () = + [] + member _.``MillisecondsCount should use default time provider`` () = + match ChunkConfiguration.MillisecondsCount 20 4 with + | ChunkMillisecondsCount (windowTime, windowLength, provider) -> + Assert.AreEqual (20, windowTime, "MillisecondsCount helper must keep provided window time.") + Assert.AreEqual (4, windowLength, "MillisecondsCount helper must keep provided window length.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "MillisecondsCount helper must use default time provider.") + | _ -> Assert.Fail ("MillisecondsCount helper must create ChunkMillisecondsCount configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs new file mode 100644 index 0000000..318c1ec --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.ProcessingOptions + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open FSharp.Control.R3 +open R3 + +[] +type MillisecondsTests () = + [] + member _.``Milliseconds should use default time provider`` () = + match ChunkConfiguration.Milliseconds 15 with + | ChunkMilliseconds (windowTime, provider) -> + Assert.AreEqual (15, windowTime, "Milliseconds helper must keep provided value.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "Milliseconds helper must use default time provider.") + | _ -> Assert.Fail ("Milliseconds helper must create ChunkMilliseconds configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs new file mode 100644 index 0000000..0e97ba6 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.ProcessingOptions + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open FSharp.Control.R3 +open R3 + +[] +type TimeSpanCountTests () = + [] + member _.``TimeSpanCount should use default time provider`` () = + match ChunkConfiguration.TimeSpanCount (TimeSpan.FromMilliseconds 10.) 3 with + | ChunkTimeSpanCount (windowTime, windowLength, provider) -> + Assert.AreEqual (TimeSpan.FromMilliseconds 10., windowTime, "TimeSpanCount helper must keep provided window time.") + Assert.AreEqual (3, windowLength, "TimeSpanCount helper must keep provided window length.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "TimeSpanCount helper must use default time provider.") + | _ -> Assert.Fail ("TimeSpanCount helper must create ChunkTimeSpanCount configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs new file mode 100644 index 0000000..fc8312d --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs @@ -0,0 +1,18 @@ +namespace FSharp.Control.R3.Tests.ProcessingOptions + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open FSharp.Control.R3 +open R3 + +[] +type TimeSpanTests () = + [] + member _.``TimeSpan should use default time provider`` () = + match ChunkConfiguration.TimeSpan (TimeSpan.FromMilliseconds 10.) with + | ChunkTimeSpan (windowTime, provider) -> + Assert.AreEqual (TimeSpan.FromMilliseconds 10., windowTime, "TimeSpan helper must keep provided window time.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "TimeSpan helper must use default time provider.") + | _ -> Assert.Fail ("TimeSpan helper must create ChunkTimeSpan configuration.") diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs new file mode 100644 index 0000000..6513940 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs @@ -0,0 +1,21 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type AggregateTests () = + [] + member _.``aggregate should match direct AggregateAsync`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let! actual = + source + |> Observable.aggregate TestHelpers.cancellationToken 0 (fun acc x -> acc + x) + Assert.AreEqual (6, actual, "Task aggregate must return aggregated sum.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs new file mode 100644 index 0000000..28731bb --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs @@ -0,0 +1,21 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type AllTests () = + [] + member _.``all should return true when all values match`` () : Task = task { + let source = TestHelpers.createObservable [| 2; 4; 6 |] + let! actual = + source + |> Observable.all TestHelpers.cancellationToken (fun x -> x % 2 = 0) + Assert.IsTrue (actual, "Task all must return true when all elements satisfy predicate.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs new file mode 100644 index 0000000..ec67fa4 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs @@ -0,0 +1,21 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type ExistsAsyncTests () = + [] + member _.``existsAsync should detect available values`` () : Task = task { + let source = TestHelpers.createObservable [| 1 |] + let! actual = + source + |> Observable.existsAsync TestHelpers.cancellationToken + Assert.IsTrue (actual, "Task existsAsync must return true for non-empty source.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs new file mode 100644 index 0000000..a459310 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs @@ -0,0 +1,21 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type FirstAsyncTests () = + [] + member _.``firstAsync should return first value`` () : Task = task { + let source = TestHelpers.createObservable [| 5; 6 |] + let! actual = + source + |> Observable.firstAsync TestHelpers.cancellationToken + Assert.AreEqual (5, actual, "Task firstAsync must return first emitted value.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs new file mode 100644 index 0000000..bb59f56 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type IterAsyncTests () = + [] + member _.``iterAsync should await action for each value`` () : Task = task { + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iterAsync TestHelpers.cancellationToken options (fun (_ : CancellationToken) x -> task { sum <- sum + x }) + Assert.AreEqual (6, sum, "Task iterAsync must run action for each emitted value.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs new file mode 100644 index 0000000..f9f4439 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs @@ -0,0 +1,22 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type IterTests () = + [] + member _.``iter should invoke action for each value`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iter TestHelpers.cancellationToken (fun x -> sum <- sum + x) + Assert.AreEqual (6, sum, "Task iter must invoke action for each value.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs new file mode 100644 index 0000000..09eb112 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type LengthTests () = + [] + member _.``length should count values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = source |> Observable.length TestHelpers.cancellationToken + Assert.AreEqual (4, actual, "Task length must return source value count.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs new file mode 100644 index 0000000..21ab70b --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs @@ -0,0 +1,23 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type MapAsyncTests () = + [] + member _.``mapAsync should transform each value`` () = + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let actual = + source + |> Observable.mapAsync options (fun (_ : CancellationToken) x -> Task.FromResult (x + 1)) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 2; 3; 4 |], actual, "Task mapAsync must transform each source value.") diff --git a/tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs b/tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs new file mode 100644 index 0000000..e0ec627 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs @@ -0,0 +1,19 @@ +namespace FSharp.Control.R3.Tests + +module TestHelpers = + open System + open System.Threading + open System.Threading.Tasks + open R3 + + let createObservable (values : 'T array) = Observable.ToObservable values + + let toArrayTask (source : Observable<'T>) = ObservableExtensions.ToArrayAsync source + + let waitTask (task : Task<'T>) = task.Result + + let waitAsync (work : Async<'T>) = Async.RunSynchronously work + + let cancellationToken = CancellationToken.None + + let stringComparer = StringComparer.OrdinalIgnoreCase From 578e83c9d2abec458545d69c6eaad11ffd11390b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 23:29:41 +0000 Subject: [PATCH 09/18] test: address validation feedback in async map test Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/1a9a39d4-61e8-489f-bff8-c1cf1814e0d4 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs | 3 ++- tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs | 3 ++- tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs index 648f76f..1fa1d11 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs @@ -15,9 +15,10 @@ type AggregateTests () = let source = TestHelpers.createObservable [| 1; 2; 3 |] let expected = ObservableExtensions.AggregateAsync (source, 0, (fun acc x -> acc + x), TestHelpers.cancellationToken) + let! expectedValue = expected let! actual = source |> Observable.aggregate 0 (fun acc x -> acc + x) |> Async.StartAsTask - Assert.AreEqual (expected.Result, actual, "Async aggregate must match direct AggregateAsync result.") + Assert.AreEqual (expectedValue, actual, "Async aggregate must match direct AggregateAsync result.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs index 0486b3d..783889c 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs @@ -15,9 +15,10 @@ type AllTests () = let source = TestHelpers.createObservable [| 2; 4; 6 |] let expected = ObservableExtensions.AllAsync (source, (fun x -> x % 2 = 0), TestHelpers.cancellationToken) + let! expectedValue = expected let! actual = source |> Observable.all (fun x -> x % 2 = 0) |> Async.StartAsTask - Assert.AreEqual (expected.Result, actual, "Async all must match direct AllAsync result.") + Assert.AreEqual (expectedValue, actual, "Async all must match direct AllAsync result.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs index 392386a..0c130d9 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs @@ -17,7 +17,7 @@ type MapAsyncTests () = let expected = ObservableExtensions.SelectAwait ( source, - (fun x (_ : Threading.CancellationToken) -> ValueTask.FromResult (x + 1)), + (fun x (_ : System.Threading.CancellationToken) -> ValueTask.FromResult (x + 1)), options.AwaitOperation, options.ConfigureAwait, options.CancelOnCompleted, From d5b2e5fa067d2a7f1b0177b59fd4766534f83165 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 01:02:07 +0000 Subject: [PATCH 10/18] test: merge ProcessingOptions tests into root file Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/d5e79ca1-4a19-4233-ae7a-390b0f5416b2 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../FSharp.Control.R3.Tests.fsproj | 2 +- .../ProcessingOptions/AsyncWindowTests.fs | 21 ------- .../MillisecondsCountTests.fs | 19 ------- .../ProcessingOptions/MillisecondsTests.fs | 18 ------ .../ProcessingOptions/TimeSpanCountTests.fs | 19 ------- .../ProcessingOptions/TimeSpanTests.fs | 18 ------ .../ProcessingOptionsTests.fs | 57 +++++++++++++++++++ 7 files changed, 58 insertions(+), 96 deletions(-) delete mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/ProcessingOptionsTests.fs diff --git a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj index ff7535f..77d8571 100644 --- a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj +++ b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj @@ -9,7 +9,7 @@ - + diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs deleted file mode 100644 index df8169d..0000000 --- a/tests/FSharp.Control.R3.Tests/ProcessingOptions/AsyncWindowTests.fs +++ /dev/null @@ -1,21 +0,0 @@ -namespace FSharp.Control.R3.Tests.ProcessingOptions - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open FSharp.Control.R3 - -[] -type AsyncWindowTests () = - [] - member _.``AsyncWindow should wrap async callback`` () : Task = task { - let mutable observed = 0 - let configuration = ChunkConfiguration.AsyncWindow (fun value -> async { observed <- value }) - match configuration with - | ChunkAsyncWindow (callback, configureAwait) -> - do! callback.Invoke (11, CancellationToken.None) - Assert.AreEqual (11, observed, "AsyncWindow helper must invoke wrapped callback with value.") - Assert.IsTrue (configureAwait, "AsyncWindow helper must set configureAwait to true.") - | _ -> Assert.Fail ("AsyncWindow helper must create ChunkAsyncWindow configuration.") - } diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs deleted file mode 100644 index f784008..0000000 --- a/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsCountTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.ProcessingOptions - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open FSharp.Control.R3 -open R3 - -[] -type MillisecondsCountTests () = - [] - member _.``MillisecondsCount should use default time provider`` () = - match ChunkConfiguration.MillisecondsCount 20 4 with - | ChunkMillisecondsCount (windowTime, windowLength, provider) -> - Assert.AreEqual (20, windowTime, "MillisecondsCount helper must keep provided window time.") - Assert.AreEqual (4, windowLength, "MillisecondsCount helper must keep provided window length.") - Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "MillisecondsCount helper must use default time provider.") - | _ -> Assert.Fail ("MillisecondsCount helper must create ChunkMillisecondsCount configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs deleted file mode 100644 index 318c1ec..0000000 --- a/tests/FSharp.Control.R3.Tests/ProcessingOptions/MillisecondsTests.fs +++ /dev/null @@ -1,18 +0,0 @@ -namespace FSharp.Control.R3.Tests.ProcessingOptions - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open FSharp.Control.R3 -open R3 - -[] -type MillisecondsTests () = - [] - member _.``Milliseconds should use default time provider`` () = - match ChunkConfiguration.Milliseconds 15 with - | ChunkMilliseconds (windowTime, provider) -> - Assert.AreEqual (15, windowTime, "Milliseconds helper must keep provided value.") - Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "Milliseconds helper must use default time provider.") - | _ -> Assert.Fail ("Milliseconds helper must create ChunkMilliseconds configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs deleted file mode 100644 index 0e97ba6..0000000 --- a/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanCountTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.ProcessingOptions - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open FSharp.Control.R3 -open R3 - -[] -type TimeSpanCountTests () = - [] - member _.``TimeSpanCount should use default time provider`` () = - match ChunkConfiguration.TimeSpanCount (TimeSpan.FromMilliseconds 10.) 3 with - | ChunkTimeSpanCount (windowTime, windowLength, provider) -> - Assert.AreEqual (TimeSpan.FromMilliseconds 10., windowTime, "TimeSpanCount helper must keep provided window time.") - Assert.AreEqual (3, windowLength, "TimeSpanCount helper must keep provided window length.") - Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "TimeSpanCount helper must use default time provider.") - | _ -> Assert.Fail ("TimeSpanCount helper must create ChunkTimeSpanCount configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs deleted file mode 100644 index fc8312d..0000000 --- a/tests/FSharp.Control.R3.Tests/ProcessingOptions/TimeSpanTests.fs +++ /dev/null @@ -1,18 +0,0 @@ -namespace FSharp.Control.R3.Tests.ProcessingOptions - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open FSharp.Control.R3 -open R3 - -[] -type TimeSpanTests () = - [] - member _.``TimeSpan should use default time provider`` () = - match ChunkConfiguration.TimeSpan (TimeSpan.FromMilliseconds 10.) with - | ChunkTimeSpan (windowTime, provider) -> - Assert.AreEqual (TimeSpan.FromMilliseconds 10., windowTime, "TimeSpan helper must keep provided window time.") - Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "TimeSpan helper must use default time provider.") - | _ -> Assert.Fail ("TimeSpan helper must create ChunkTimeSpan configuration.") diff --git a/tests/FSharp.Control.R3.Tests/ProcessingOptionsTests.fs b/tests/FSharp.Control.R3.Tests/ProcessingOptionsTests.fs new file mode 100644 index 0000000..ca94fb7 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/ProcessingOptionsTests.fs @@ -0,0 +1,57 @@ +namespace FSharp.Control.R3.Tests + +open System +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open FSharp.Control.R3 +open R3 + +[] +type ProcessingOptionsTests () = + [] + member _.``TimeSpan should use default time provider`` () = + match ChunkConfiguration.TimeSpan (TimeSpan.FromMilliseconds 10.) with + | ChunkTimeSpan (windowTime, provider) -> + Assert.AreEqual (TimeSpan.FromMilliseconds 10., windowTime, "TimeSpan helper must keep provided window time.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "TimeSpan helper must use default time provider.") + | _ -> Assert.Fail ("TimeSpan helper must create ChunkTimeSpan configuration.") + + [] + member _.``TimeSpanCount should use default time provider`` () = + match ChunkConfiguration.TimeSpanCount (TimeSpan.FromMilliseconds 10.) 3 with + | ChunkTimeSpanCount (windowTime, windowLength, provider) -> + Assert.AreEqual (TimeSpan.FromMilliseconds 10., windowTime, "TimeSpanCount helper must keep provided window time.") + Assert.AreEqual (3, windowLength, "TimeSpanCount helper must keep provided window length.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "TimeSpanCount helper must use default time provider.") + | _ -> Assert.Fail ("TimeSpanCount helper must create ChunkTimeSpanCount configuration.") + + [] + member _.``Milliseconds should use default time provider`` () = + match ChunkConfiguration.Milliseconds 15 with + | ChunkMilliseconds (windowTime, provider) -> + Assert.AreEqual (15, windowTime, "Milliseconds helper must keep provided value.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "Milliseconds helper must use default time provider.") + | _ -> Assert.Fail ("Milliseconds helper must create ChunkMilliseconds configuration.") + + [] + member _.``MillisecondsCount should use default time provider`` () = + match ChunkConfiguration.MillisecondsCount 20 4 with + | ChunkMillisecondsCount (windowTime, windowLength, provider) -> + Assert.AreEqual (20, windowTime, "MillisecondsCount helper must keep provided window time.") + Assert.AreEqual (4, windowLength, "MillisecondsCount helper must keep provided window length.") + Assert.AreSame (ObservableSystem.DefaultTimeProvider, provider, "MillisecondsCount helper must use default time provider.") + | _ -> Assert.Fail ("MillisecondsCount helper must create ChunkMillisecondsCount configuration.") + + [] + member _.``AsyncWindow should wrap async callback`` () : Task = task { + let mutable observed = 0 + let configuration = ChunkConfiguration.AsyncWindow (fun value -> async { observed <- value }) + + match configuration with + | ChunkAsyncWindow (callback, configureAwait) -> + do! callback.Invoke (11, CancellationToken.None) + Assert.AreEqual (11, observed, "AsyncWindow helper must invoke wrapped callback with value.") + Assert.IsTrue (configureAwait, "AsyncWindow helper must set configureAwait to true.") + | _ -> Assert.Fail ("AsyncWindow helper must create ChunkAsyncWindow configuration.") + } From df374d5f23c7002395916d8283f8f134820f33ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 01:04:13 +0000 Subject: [PATCH 11/18] test: use task CE shape in Task mapAsync test Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/f5275f04-80ce-4f7f-b590-6d5daa0a054d Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs index 21ab70b..6d98752 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs @@ -12,12 +12,12 @@ open FSharp.Control.R3.Tests [] type MapAsyncTests () = [] - member _.``mapAsync should transform each value`` () = + member _.``mapAsync should transform each value`` () : Task = task { let options = ProcessingOptions.Default let source = TestHelpers.createObservable [| 1; 2; 3 |] - let actual = + let! actual = source |> Observable.mapAsync options (fun (_ : CancellationToken) x -> Task.FromResult (x + 1)) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 2; 3; 4 |], actual, "Task mapAsync must transform each source value.") + } From ce05f4bfa3fe4472dbee7ebcbcc178d27c8fe623 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 01:10:04 +0000 Subject: [PATCH 12/18] test: regroup observable tests by operation category Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/08eea792-5d36-4dec-90f8-e09312be0832 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../Observable/ChunkBySizeTests.fs | 23 ------- .../Observable/ChunkByTests.fs | 28 --------- .../Observable/ChunkSkipTakeTests.fs | 62 +++++++++++++++++++ .../Observable/FilterCategoryTests.fs | 39 ++++++++++++ .../Observable/FilterTests.fs | 19 ------ .../{BindTests.fs => MapCategoryTests.fs} | 12 +++- .../Observable/MapTests.fs | 19 ------ .../Observable/SkipTests.fs | 19 ------ .../Observable/TakeTests.fs | 19 ------ .../Observable/WhereTests.fs | 19 ------ 10 files changed, 112 insertions(+), 147 deletions(-) delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs rename tests/FSharp.Control.R3.Tests/Observable/{BindTests.fs => MapCategoryTests.fs} (64%) delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/MapTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs deleted file mode 100644 index 73d419a..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/ChunkBySizeTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type ChunkBySizeTests () = - [] - member _.``chunkBySize should split by fixed size`` () = - let source = TestHelpers.createObservable [| 1; 2; 3; 4; 5 |] - let actual = - source - |> Observable.chunkBySize 2 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - |> Array.map Seq.toArray - Assert.AreEqual (3, actual.Length, "chunkBySize should produce expected number of chunks.") - CollectionAssert.AreEqual ([| 1; 2 |], actual[0], "First chunk should contain first two items.") - CollectionAssert.AreEqual ([| 3; 4 |], actual[1], "Second chunk should contain next two items.") - CollectionAssert.AreEqual ([| 5 |], actual[2], "Third chunk should contain the remaining item.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs deleted file mode 100644 index b9d50d8..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/ChunkByTests.fs +++ /dev/null @@ -1,28 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type ChunkByTests () = - [] - member _.``chunkBy ChunkCount should match chunkBySize`` () = - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let expected = - source - |> Observable.chunkBySize 2 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - |> Array.map Seq.toArray - let actual = - source - |> Observable.chunkBy (ChunkCount 2) - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - |> Array.map Seq.toArray - Assert.AreEqual (expected.Length, actual.Length, "chunkBy ChunkCount must produce same chunk count as chunkBySize.") - CollectionAssert.AreEqual (expected[0], actual[0], "First chunk must match chunkBySize output.") - CollectionAssert.AreEqual (expected[1], actual[1], "Second chunk must match chunkBySize output.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs new file mode 100644 index 0000000..32de635 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs @@ -0,0 +1,62 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type ChunkSkipTakeTests () = + [] + member _.``chunkBySize should split by fixed size`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4; 5 |] + let actual = + source + |> Observable.chunkBySize 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + |> Array.map Seq.toArray + Assert.AreEqual (3, actual.Length, "chunkBySize should produce expected number of chunks.") + CollectionAssert.AreEqual ([| 1; 2 |], actual[0], "First chunk should contain first two items.") + CollectionAssert.AreEqual ([| 3; 4 |], actual[1], "Second chunk should contain next two items.") + CollectionAssert.AreEqual ([| 5 |], actual[2], "Third chunk should contain the remaining item.") + + [] + member _.``chunkBy ChunkCount should match chunkBySize`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let expected = + source + |> Observable.chunkBySize 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + |> Array.map Seq.toArray + let actual = + source + |> Observable.chunkBy (ChunkCount 2) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + |> Array.map Seq.toArray + Assert.AreEqual (expected.Length, actual.Length, "chunkBy ChunkCount must produce same chunk count as chunkBySize.") + CollectionAssert.AreEqual (expected[0], actual[0], "First chunk must match chunkBySize output.") + CollectionAssert.AreEqual (expected[1], actual[1], "Second chunk must match chunkBySize output.") + + [] + member _.``skip should skip leading values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.skip 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 3; 4 |], actual, "skip must ignore the configured number of leading values.") + + [] + member _.``take should keep leading values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.take 2 + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 1; 2 |], actual, "take must emit only the configured number of leading values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs new file mode 100644 index 0000000..fafcede --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs @@ -0,0 +1,39 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type FilterCategoryTests () = + [] + member _.``where should keep matching values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.where (fun x -> x > 2) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 3; 4 |], actual, "where must keep values matching the predicate.") + + [] + member _.``filter should keep matching values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> Observable.filter (fun x -> x % 2 = 0) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 2; 4 |], actual, "filter must keep only matching elements.") + + [] + member _.``choose should keep only Some values`` () = + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let actual = + source + |> FSharp.Control.R3.Observable.OptionExtensions.Observable.choose (fun x -> if x % 2 = 0 then Some (x * 10) else None) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 20; 40 |], actual, "choose must emit only mapped values from Some results.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs b/tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs deleted file mode 100644 index c4d6170..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/FilterTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type FilterTests () = - [] - member _.``filter should keep matching values`` () = - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = - source - |> Observable.filter (fun x -> x % 2 = 0) - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - CollectionAssert.AreEqual ([| 2; 4 |], actual, "filter must keep only matching elements.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/BindTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs similarity index 64% rename from tests/FSharp.Control.R3.Tests/Observable/BindTests.fs rename to tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs index 0ea3c32..69ebcd0 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/BindTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs @@ -7,7 +7,17 @@ open FSharp.Control.R3 open FSharp.Control.R3.Tests [] -type BindTests () = +type MapCategoryTests () = + [] + member _.``map should transform each value`` () = + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let actual = + source + |> Observable.map (fun x -> x * 10) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 10; 20; 30 |], actual, "map must transform each emitted value.") + [] member _.``bind should match SelectMany behavior`` () = let source = TestHelpers.createObservable [| 1; 2 |] diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapTests.fs deleted file mode 100644 index e163ed2..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/MapTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type MapTests () = - [] - member _.``map should transform each value`` () = - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let actual = - source - |> Observable.map (fun x -> x * 10) - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - CollectionAssert.AreEqual ([| 10; 20; 30 |], actual, "map must transform each emitted value.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs b/tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs deleted file mode 100644 index 6429b3e..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/SkipTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type SkipTests () = - [] - member _.``skip should skip leading values`` () = - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = - source - |> Observable.skip 2 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - CollectionAssert.AreEqual ([| 3; 4 |], actual, "skip must ignore the configured number of leading values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs deleted file mode 100644 index cb997b4..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/TakeTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type TakeTests () = - [] - member _.``take should keep leading values`` () = - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = - source - |> Observable.take 2 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - CollectionAssert.AreEqual ([| 1; 2 |], actual, "take must emit only the configured number of leading values.") diff --git a/tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs b/tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs deleted file mode 100644 index 24e168c..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/WhereTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type WhereTests () = - [] - member _.``where should keep matching values`` () = - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = - source - |> Observable.where (fun x -> x > 2) - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - CollectionAssert.AreEqual ([| 3; 4 |], actual, "where must keep values matching the predicate.") From 7ee21999e38ef1e4eef81faf20a364fb9edb5353 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 14:43:49 +0000 Subject: [PATCH 13/18] test: use FusionTasks binding in AsyncObservable tests Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/b75c1330-ae04-4568-91e8-4f490579252c Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- Directory.Packages.props | 1 + .../AsyncObservable/AggregateTests.fs | 8 +++----- .../FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs | 8 +++----- .../AsyncObservable/ExistsAsyncTests.fs | 5 +++-- .../AsyncObservable/FirstAsyncTests.fs | 5 +++-- .../AsyncObservable/IterAsyncTests.fs | 5 ++--- .../FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs | 9 +++------ .../AsyncObservable/LengthTests.fs | 5 +++-- .../AsyncObservable/ToArrayTests.fs | 5 +++-- .../AsyncObservable/ToListTests.fs | 5 +++-- .../FSharp.Control.R3.Tests.fsproj | 1 + 11 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 05b5418..d28212f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -14,6 +14,7 @@ + diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs index 1fa1d11..ffa4c66 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -16,9 +17,6 @@ type AggregateTests () = let expected = ObservableExtensions.AggregateAsync (source, 0, (fun acc x -> acc + x), TestHelpers.cancellationToken) let! expectedValue = expected - let! actual = - source - |> Observable.aggregate 0 (fun acc x -> acc + x) - |> Async.StartAsTask + let! actual = source |> Observable.aggregate 0 (fun acc x -> acc + x) Assert.AreEqual (expectedValue, actual, "Async aggregate must match direct AggregateAsync result.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs index 783889c..b7275e0 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -16,9 +17,6 @@ type AllTests () = let expected = ObservableExtensions.AllAsync (source, (fun x -> x % 2 = 0), TestHelpers.cancellationToken) let! expectedValue = expected - let! actual = - source - |> Observable.all (fun x -> x % 2 = 0) - |> Async.StartAsTask + let! actual = source |> Observable.all (fun x -> x % 2 = 0) Assert.AreEqual (expectedValue, actual, "Async all must match direct AllAsync result.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs index 2302854..b499453 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -13,6 +14,6 @@ type ExistsAsyncTests () = [] member _.``existsAsync should detect available values`` () : Task = task { let source = TestHelpers.createObservable [| 1 |] - let! actual = source |> Observable.existsAsync |> Async.StartAsTask + let! actual = source |> Observable.existsAsync Assert.IsTrue (actual, "existsAsync must return true for non-empty sequence.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs index d7766e4..88ba54d 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -13,6 +14,6 @@ type FirstAsyncTests () = [] member _.``firstAsync should return first value`` () : Task = task { let source = TestHelpers.createObservable [| 9; 8 |] - let! actual = source |> Observable.firstAsync |> Async.StartAsTask + let! actual = source |> Observable.firstAsync Assert.AreEqual (9, actual, "firstAsync must return the first emitted value.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs index d936e87..ab2699b 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -18,7 +19,5 @@ type IterAsyncTests () = do! source |> Observable.iterAsync options (fun x -> async { sum <- sum + x }) - |> Async.StartAsTask - :> Task Assert.AreEqual (6, sum, "iterAsync must apply asynchronous action to every value.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs index bca27fd..f362fde 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -14,10 +15,6 @@ type IterTests () = member _.``iter should invoke action for each value`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] let mutable sum = 0 - do! - source - |> Observable.iter (fun x -> sum <- sum + x) - |> Async.StartAsTask - :> Task + do! source |> Observable.iter (fun x -> sum <- sum + x) Assert.AreEqual (6, sum, "iter must invoke action for each emitted value.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs index 218d979..4589d8e 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -13,6 +14,6 @@ type LengthTests () = [] member _.``length should count values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let! actual = source |> Observable.length |> Async.StartAsTask + let! actual = source |> Observable.length Assert.AreEqual (4, actual, "length must return emitted value count.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs index f2f49aa..4142661 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -13,6 +14,6 @@ type ToArrayTests () = [] member _.``toArray should return all values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] - let! actual = source |> Observable.toArray |> Async.StartAsTask + let! actual = source |> Observable.toArray CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "toArray must return all source values.") } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs index d8dd37c..10c79da 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs @@ -2,8 +2,9 @@ namespace FSharp.Control.R3.Tests.AsyncObservable open System open System.Threading.Tasks +open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 +open global.R3 open FSharp.Control.R3 open FSharp.Control.R3.Async open FSharp.Control.R3.Tests @@ -13,6 +14,6 @@ type ToListTests () = [] member _.``toList should return all values as list`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] - let! actual = source |> Observable.toList |> Async.StartAsTask + let! actual = source |> Observable.toList CollectionAssert.AreEqual ([| 1; 2; 3 |], actual |> List.toArray, "toList must return all source values in order.") } diff --git a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj index 77d8571..86fd5aa 100644 --- a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj +++ b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj @@ -14,6 +14,7 @@ + From 788aca9cccb4e9285e4f0770e65d89840bf61c5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 15:00:32 +0000 Subject: [PATCH 14/18] Regroup Async and Task observable tests by categories Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/a6751a07-082f-4b6d-a346-d2b4f5d5f694 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- ...teTests.fs => AggregationCategoryTests.fs} | 10 ++++- ...lTests.fs => AllSelectionCategoryTests.fs} | 10 ++++- ...istTests.fs => ConversionCategoryTests.fs} | 10 ++++- .../AsyncObservable/ExistsAsyncTests.fs | 19 -------- .../AsyncObservable/IterAsyncTests.fs | 23 ---------- .../AsyncObservable/IterTests.fs | 20 --------- .../AsyncObservable/LengthTests.fs | 19 -------- .../AsyncObservable/OfAsyncTests.fs | 19 -------- ...Tests.fs => SingleElementCategoryTests.fs} | 3 +- .../AsyncObservable/ToArrayTests.fs | 19 -------- ...ests.fs => TransformationCategoryTests.fs} | 29 ++++++++++++- ...teTests.fs => AggregationCategoryTests.fs} | 10 ++++- ...lTests.fs => AllSelectionCategoryTests.fs} | 12 +++++- .../TaskObservable/ExistsAsyncTests.fs | 21 --------- .../TaskObservable/IterAsyncTests.fs | 23 ---------- .../TaskObservable/IterTests.fs | 22 ---------- .../TaskObservable/LengthTests.fs | 19 -------- .../TaskObservable/MapAsyncTests.fs | 23 ---------- ...Tests.fs => SingleElementCategoryTests.fs} | 3 +- .../TransformationCategoryTests.fs | 43 +++++++++++++++++++ 20 files changed, 115 insertions(+), 242 deletions(-) rename tests/FSharp.Control.R3.Tests/AsyncObservable/{AggregateTests.fs => AggregationCategoryTests.fs} (71%) rename tests/FSharp.Control.R3.Tests/AsyncObservable/{AllTests.fs => AllSelectionCategoryTests.fs} (68%) rename tests/FSharp.Control.R3.Tests/AsyncObservable/{ToListTests.fs => ConversionCategoryTests.fs} (63%) delete mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs rename tests/FSharp.Control.R3.Tests/AsyncObservable/{FirstAsyncTests.fs => SingleElementCategoryTests.fs} (93%) delete mode 100644 tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs rename tests/FSharp.Control.R3.Tests/AsyncObservable/{MapAsyncTests.fs => TransformationCategoryTests.fs} (50%) rename tests/FSharp.Control.R3.Tests/TaskObservable/{AggregateTests.fs => AggregationCategoryTests.fs} (64%) rename tests/FSharp.Control.R3.Tests/TaskObservable/{AllTests.fs => AllSelectionCategoryTests.fs} (61%) delete mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs rename tests/FSharp.Control.R3.Tests/TaskObservable/{FirstAsyncTests.fs => SingleElementCategoryTests.fs} (94%) create mode 100644 tests/FSharp.Control.R3.Tests/TaskObservable/TransformationCategoryTests.fs diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregationCategoryTests.fs similarity index 71% rename from tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs rename to tests/FSharp.Control.R3.Tests/AsyncObservable/AggregationCategoryTests.fs index ffa4c66..6125b69 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregateTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AggregationCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.AsyncObservable -open System open System.Threading.Tasks open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,7 @@ open FSharp.Control.R3.Async open FSharp.Control.R3.Tests [] -type AggregateTests () = +type AggregationCategoryTests () = [] member _.``aggregate should match direct AggregateAsync`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] @@ -20,3 +19,10 @@ type AggregateTests () = let! actual = source |> Observable.aggregate 0 (fun acc x -> acc + x) Assert.AreEqual (expectedValue, actual, "Async aggregate must match direct AggregateAsync result.") } + + [] + member _.``length should count values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = source |> Observable.length + Assert.AreEqual (4, actual, "length must return emitted value count.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllSelectionCategoryTests.fs similarity index 68% rename from tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs rename to tests/FSharp.Control.R3.Tests/AsyncObservable/AllSelectionCategoryTests.fs index b7275e0..6b463b2 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/AllTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/AllSelectionCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.AsyncObservable -open System open System.Threading.Tasks open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,7 @@ open FSharp.Control.R3.Async open FSharp.Control.R3.Tests [] -type AllTests () = +type AllSelectionCategoryTests () = [] member _.``all should match direct AllAsync`` () : Task = task { let source = TestHelpers.createObservable [| 2; 4; 6 |] @@ -20,3 +19,10 @@ type AllTests () = let! actual = source |> Observable.all (fun x -> x % 2 = 0) Assert.AreEqual (expectedValue, actual, "Async all must match direct AllAsync result.") } + + [] + member _.``existsAsync should detect available values`` () : Task = task { + let source = TestHelpers.createObservable [| 1 |] + let! actual = source |> Observable.existsAsync + Assert.IsTrue (actual, "existsAsync must return true for non-empty sequence.") + } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ConversionCategoryTests.fs similarity index 63% rename from tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs rename to tests/FSharp.Control.R3.Tests/AsyncObservable/ConversionCategoryTests.fs index 10c79da..07841b7 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToListTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/ConversionCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.AsyncObservable -open System open System.Threading.Tasks open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,14 @@ open FSharp.Control.R3.Async open FSharp.Control.R3.Tests [] -type ToListTests () = +type ConversionCategoryTests () = + [] + member _.``toArray should return all values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let! actual = source |> Observable.toArray + CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "toArray must return all source values.") + } + [] member _.``toList should return all values as list`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs deleted file mode 100644 index b499453..0000000 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/ExistsAsyncTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.AsyncObservable - -open System -open System.Threading.Tasks -open global.FSharp.Control -open Microsoft.VisualStudio.TestTools.UnitTesting -open global.R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Async -open FSharp.Control.R3.Tests - -[] -type ExistsAsyncTests () = - [] - member _.``existsAsync should detect available values`` () : Task = task { - let source = TestHelpers.createObservable [| 1 |] - let! actual = source |> Observable.existsAsync - Assert.IsTrue (actual, "existsAsync must return true for non-empty sequence.") - } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs deleted file mode 100644 index ab2699b..0000000 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterAsyncTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.AsyncObservable - -open System -open System.Threading.Tasks -open global.FSharp.Control -open Microsoft.VisualStudio.TestTools.UnitTesting -open global.R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Async -open FSharp.Control.R3.Tests - -[] -type IterAsyncTests () = - [] - member _.``iterAsync should await async action for each value`` () : Task = task { - let options = ProcessingOptions.Default - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let mutable sum = 0 - do! - source - |> Observable.iterAsync options (fun x -> async { sum <- sum + x }) - Assert.AreEqual (6, sum, "iterAsync must apply asynchronous action to every value.") - } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs deleted file mode 100644 index f362fde..0000000 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/IterTests.fs +++ /dev/null @@ -1,20 +0,0 @@ -namespace FSharp.Control.R3.Tests.AsyncObservable - -open System -open System.Threading.Tasks -open global.FSharp.Control -open Microsoft.VisualStudio.TestTools.UnitTesting -open global.R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Async -open FSharp.Control.R3.Tests - -[] -type IterTests () = - [] - member _.``iter should invoke action for each value`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let mutable sum = 0 - do! source |> Observable.iter (fun x -> sum <- sum + x) - Assert.AreEqual (6, sum, "iter must invoke action for each emitted value.") - } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs deleted file mode 100644 index 4589d8e..0000000 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/LengthTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.AsyncObservable - -open System -open System.Threading.Tasks -open global.FSharp.Control -open Microsoft.VisualStudio.TestTools.UnitTesting -open global.R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Async -open FSharp.Control.R3.Tests - -[] -type LengthTests () = - [] - member _.``length should count values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let! actual = source |> Observable.length - Assert.AreEqual (4, actual, "length must return emitted value count.") - } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs deleted file mode 100644 index 35e2271..0000000 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/OfAsyncTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.AsyncObservable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Async -open FSharp.Control.R3.Tests - -[] -type OfAsyncTests () = - [] - member _.``ofAsync should emit computation result`` () = - let actual = - Observable.ofAsync (async { return 7 }) - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - CollectionAssert.AreEqual ([| 7 |], actual, "ofAsync must emit the async computation result.") diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/SingleElementCategoryTests.fs similarity index 93% rename from tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs rename to tests/FSharp.Control.R3.Tests/AsyncObservable/SingleElementCategoryTests.fs index 88ba54d..c153d4c 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/FirstAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/SingleElementCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.AsyncObservable -open System open System.Threading.Tasks open global.FSharp.Control open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,7 @@ open FSharp.Control.R3.Async open FSharp.Control.R3.Tests [] -type FirstAsyncTests () = +type SingleElementCategoryTests () = [] member _.``firstAsync should return first value`` () : Task = task { let source = TestHelpers.createObservable [| 9; 8 |] diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs deleted file mode 100644 index 4142661..0000000 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/ToArrayTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.AsyncObservable - -open System -open System.Threading.Tasks -open global.FSharp.Control -open Microsoft.VisualStudio.TestTools.UnitTesting -open global.R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Async -open FSharp.Control.R3.Tests - -[] -type ToArrayTests () = - [] - member _.``toArray should return all values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let! actual = source |> Observable.toArray - CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "toArray must return all source values.") - } diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs similarity index 50% rename from tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs rename to tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs index 0c130d9..c59149d 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/MapAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs @@ -9,7 +9,7 @@ open FSharp.Control.R3.Async open FSharp.Control.R3.Tests [] -type MapAsyncTests () = +type TransformationCategoryTests () = [] member _.``mapAsync should match direct SelectAwait`` () : Task = task { let options = ProcessingOptions.Default @@ -34,3 +34,30 @@ type MapAsyncTests () = CollectionAssert.AreEqual (expected, actual, "Async mapAsync must match direct SelectAwait behavior.") } + + [] + member _.``iter should invoke action for each value`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! source |> Observable.iter (fun x -> sum <- sum + x) + Assert.AreEqual (6, sum, "iter must invoke action for each emitted value.") + } + + [] + member _.``iterAsync should await async action for each value`` () : Task = task { + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iterAsync options (fun x -> async { sum <- sum + x }) + Assert.AreEqual (6, sum, "iterAsync must apply asynchronous action to every value.") + } + + [] + member _.``ofAsync should emit computation result`` () = + let actual = + Observable.ofAsync (async { return 7 }) + |> TestHelpers.toArrayTask + |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 7 |], actual, "ofAsync must emit the async computation result.") diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs similarity index 64% rename from tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs rename to tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs index 6513940..307a081 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/AggregateTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.TaskObservable -open System open System.Threading open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,7 @@ open FSharp.Control.R3.Task open FSharp.Control.R3.Tests [] -type AggregateTests () = +type AggregationCategoryTests () = [] member _.``aggregate should match direct AggregateAsync`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] @@ -19,3 +18,10 @@ type AggregateTests () = |> Observable.aggregate TestHelpers.cancellationToken 0 (fun acc x -> acc + x) Assert.AreEqual (6, actual, "Task aggregate must return aggregated sum.") } + + [] + member _.``length should count values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = source |> Observable.length TestHelpers.cancellationToken + Assert.AreEqual (4, actual, "Task length must return source value count.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs similarity index 61% rename from tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs rename to tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs index 28731bb..38b60c8 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/AllTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.TaskObservable -open System open System.Threading open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,7 @@ open FSharp.Control.R3.Task open FSharp.Control.R3.Tests [] -type AllTests () = +type AllSelectionCategoryTests () = [] member _.``all should return true when all values match`` () : Task = task { let source = TestHelpers.createObservable [| 2; 4; 6 |] @@ -19,3 +18,12 @@ type AllTests () = |> Observable.all TestHelpers.cancellationToken (fun x -> x % 2 = 0) Assert.IsTrue (actual, "Task all must return true when all elements satisfy predicate.") } + + [] + member _.``existsAsync should detect available values`` () : Task = task { + let source = TestHelpers.createObservable [| 1 |] + let! actual = + source + |> Observable.existsAsync TestHelpers.cancellationToken + Assert.IsTrue (actual, "Task existsAsync must return true for non-empty source.") + } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs deleted file mode 100644 index ec67fa4..0000000 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/ExistsAsyncTests.fs +++ /dev/null @@ -1,21 +0,0 @@ -namespace FSharp.Control.R3.Tests.TaskObservable - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Task -open FSharp.Control.R3.Tests - -[] -type ExistsAsyncTests () = - [] - member _.``existsAsync should detect available values`` () : Task = task { - let source = TestHelpers.createObservable [| 1 |] - let! actual = - source - |> Observable.existsAsync TestHelpers.cancellationToken - Assert.IsTrue (actual, "Task existsAsync must return true for non-empty source.") - } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs deleted file mode 100644 index bb59f56..0000000 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/IterAsyncTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.TaskObservable - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Task -open FSharp.Control.R3.Tests - -[] -type IterAsyncTests () = - [] - member _.``iterAsync should await action for each value`` () : Task = task { - let options = ProcessingOptions.Default - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let mutable sum = 0 - do! - source - |> Observable.iterAsync TestHelpers.cancellationToken options (fun (_ : CancellationToken) x -> task { sum <- sum + x }) - Assert.AreEqual (6, sum, "Task iterAsync must run action for each emitted value.") - } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs deleted file mode 100644 index f9f4439..0000000 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/IterTests.fs +++ /dev/null @@ -1,22 +0,0 @@ -namespace FSharp.Control.R3.Tests.TaskObservable - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Task -open FSharp.Control.R3.Tests - -[] -type IterTests () = - [] - member _.``iter should invoke action for each value`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let mutable sum = 0 - do! - source - |> Observable.iter TestHelpers.cancellationToken (fun x -> sum <- sum + x) - Assert.AreEqual (6, sum, "Task iter must invoke action for each value.") - } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs deleted file mode 100644 index 09eb112..0000000 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/LengthTests.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests.TaskObservable - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Task -open FSharp.Control.R3.Tests - -[] -type LengthTests () = - [] - member _.``length should count values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let! actual = source |> Observable.length TestHelpers.cancellationToken - Assert.AreEqual (4, actual, "Task length must return source value count.") - } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs deleted file mode 100644 index 6d98752..0000000 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/MapAsyncTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.TaskObservable - -open System -open System.Threading -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Task -open FSharp.Control.R3.Tests - -[] -type MapAsyncTests () = - [] - member _.``mapAsync should transform each value`` () : Task = task { - let options = ProcessingOptions.Default - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let! actual = - source - |> Observable.mapAsync options (fun (_ : CancellationToken) x -> Task.FromResult (x + 1)) - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 2; 3; 4 |], actual, "Task mapAsync must transform each source value.") - } diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs similarity index 94% rename from tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs rename to tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs index a459310..6a6b125 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/FirstAsyncTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.TaskObservable -open System open System.Threading open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting @@ -10,7 +9,7 @@ open FSharp.Control.R3.Task open FSharp.Control.R3.Tests [] -type FirstAsyncTests () = +type SingleElementCategoryTests () = [] member _.``firstAsync should return first value`` () : Task = task { let source = TestHelpers.createObservable [| 5; 6 |] diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/TransformationCategoryTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/TransformationCategoryTests.fs new file mode 100644 index 0000000..165a20e --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/TransformationCategoryTests.fs @@ -0,0 +1,43 @@ +namespace FSharp.Control.R3.Tests.TaskObservable + +open System.Threading +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Task +open FSharp.Control.R3.Tests + +[] +type TransformationCategoryTests () = + [] + member _.``mapAsync should transform each value`` () : Task = task { + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let! actual = + source + |> Observable.mapAsync options (fun (_ : CancellationToken) x -> Task.FromResult (x + 1)) + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 2; 3; 4 |], actual, "Task mapAsync must transform each source value.") + } + + [] + member _.``iter should invoke action for each value`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iter TestHelpers.cancellationToken (fun x -> sum <- sum + x) + Assert.AreEqual (6, sum, "Task iter must invoke action for each value.") + } + + [] + member _.``iterAsync should await action for each value`` () : Task = task { + let options = ProcessingOptions.Default + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let mutable sum = 0 + do! + source + |> Observable.iterAsync TestHelpers.cancellationToken options (fun (_ : CancellationToken) x -> task { sum <- sum + x }) + Assert.AreEqual (6, sum, "Task iterAsync must run action for each emitted value.") + } From cbaf6ab963a4c125a764dfb23ce240d41e589e84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 15:02:23 +0000 Subject: [PATCH 15/18] Clean up regrouped test imports Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/a6751a07-082f-4b6d-a346-d2b4f5d5f694 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../AsyncObservable/TransformationCategoryTests.fs | 1 - .../TaskObservable/AggregationCategoryTests.fs | 1 - .../TaskObservable/AllSelectionCategoryTests.fs | 1 - .../TaskObservable/SingleElementCategoryTests.fs | 1 - 4 files changed, 4 deletions(-) diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs index c59149d..25b1117 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.AsyncObservable -open System open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs index 307a081..968c44b 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/AggregationCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.TaskObservable -open System.Threading open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs index 38b60c8..4b90160 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/AllSelectionCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.TaskObservable -open System.Threading open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 diff --git a/tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs b/tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs index 6a6b125..afa1bc7 100644 --- a/tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/TaskObservable/SingleElementCategoryTests.fs @@ -1,6 +1,5 @@ namespace FSharp.Control.R3.Tests.TaskObservable -open System.Threading open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 From 33c718b70683108a23a6ca8834de6a7fba415fd1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 15:04:24 +0000 Subject: [PATCH 16/18] Move TestHelpers to root module file Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/244a22d1-52fa-4b08-b7b2-729a28a43b65 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../FSharp.Control.R3.Tests.fsproj | 2 +- tests/FSharp.Control.R3.Tests/TestHelpers.fs | 18 ++++++++++++++++++ .../TestHelpers/TestHelpers.fs | 19 ------------------- 3 files changed, 19 insertions(+), 20 deletions(-) create mode 100644 tests/FSharp.Control.R3.Tests/TestHelpers.fs delete mode 100644 tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs diff --git a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj index 86fd5aa..bc5ecbd 100644 --- a/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj +++ b/tests/FSharp.Control.R3.Tests/FSharp.Control.R3.Tests.fsproj @@ -5,7 +5,7 @@ - + diff --git a/tests/FSharp.Control.R3.Tests/TestHelpers.fs b/tests/FSharp.Control.R3.Tests/TestHelpers.fs new file mode 100644 index 0000000..ec7e818 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/TestHelpers.fs @@ -0,0 +1,18 @@ +module FSharp.Control.R3.Tests.TestHelpers + +open System +open System.Threading +open System.Threading.Tasks +open R3 + +let createObservable (values : 'T array) = Observable.ToObservable values + +let toArrayTask (source : Observable<'T>) = ObservableExtensions.ToArrayAsync source + +let waitTask (task : Task<'T>) = task.Result + +let waitAsync (work : Async<'T>) = Async.RunSynchronously work + +let cancellationToken = CancellationToken.None + +let stringComparer = StringComparer.OrdinalIgnoreCase diff --git a/tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs b/tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs deleted file mode 100644 index e0ec627..0000000 --- a/tests/FSharp.Control.R3.Tests/TestHelpers/TestHelpers.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FSharp.Control.R3.Tests - -module TestHelpers = - open System - open System.Threading - open System.Threading.Tasks - open R3 - - let createObservable (values : 'T array) = Observable.ToObservable values - - let toArrayTask (source : Observable<'T>) = ObservableExtensions.ToArrayAsync source - - let waitTask (task : Task<'T>) = task.Result - - let waitAsync (work : Async<'T>) = Async.RunSynchronously work - - let cancellationToken = CancellationToken.None - - let stringComparer = StringComparer.OrdinalIgnoreCase From c071baf689e077fa6d35d72d0e5a895409b80e64 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 15:08:56 +0000 Subject: [PATCH 17/18] Convert remaining tests to task CE pattern Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/732d168a-55cb-4c6c-be57-7f8ffe91a8dd Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../TransformationCategoryTests.fs | 13 +++-- .../Observable/AsObservableTests.fs | 12 ++--- .../Observable/CastTests.fs | 7 +-- .../Observable/CatchTests.fs | 6 ++- .../Observable/ChunkSkipTakeTests.fs | 48 ++++++++----------- .../Observable/ConcatTests.fs | 9 ++-- .../Observable/DistinctTests.fs | 10 ++-- .../Observable/EmptyTests.fs | 9 ++-- .../Observable/FilterCategoryTests.fs | 19 ++++---- .../Observable/MapCategoryTests.fs | 16 +++---- .../Observable/MapiTests.fs | 10 ++-- .../Observable/MergeTests.fs | 10 ++-- .../Observable/OfTypeTests.fs | 7 +-- .../Observable/SingletonTests.fs | 9 ++-- tests/FSharp.Control.R3.Tests/TestHelpers.fs | 5 -- 15 files changed, 88 insertions(+), 102 deletions(-) diff --git a/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs b/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs index 25b1117..afd9560 100644 --- a/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/AsyncObservable/TransformationCategoryTests.fs @@ -13,7 +13,7 @@ type TransformationCategoryTests () = member _.``mapAsync should match direct SelectAwait`` () : Task = task { let options = ProcessingOptions.Default let source = TestHelpers.createObservable [| 1; 2; 3 |] - let expected = + let! expected = ObservableExtensions.SelectAwait ( source, (fun x (_ : System.Threading.CancellationToken) -> ValueTask.FromResult (x + 1)), @@ -23,13 +23,11 @@ type TransformationCategoryTests () = options.MaxConcurrent ) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - let actual = + let! actual = source |> Observable.mapAsync options (fun x -> async { return x + 1 }) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual (expected, actual, "Async mapAsync must match direct SelectAwait behavior.") } @@ -54,9 +52,10 @@ type TransformationCategoryTests () = } [] - member _.``ofAsync should emit computation result`` () = - let actual = + member _.``ofAsync should emit computation result`` () : Task = task { + let! actual = Observable.ofAsync (async { return 7 }) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + CollectionAssert.AreEqual ([| 7 |], actual, "ofAsync must emit the async computation result.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs b/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs index 884792b..288fae6 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,12 +10,9 @@ open FSharp.Control.R3.Tests [] type AsObservableTests () = [] - member _.``asObservable should keep source values`` () = + member _.``asObservable should keep source values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] - let expected = source |> TestHelpers.toArrayTask |> TestHelpers.waitTask - let actual = - source - |> Observable.asObservable - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + let! expected = source |> TestHelpers.toArrayTask + let! actual = source |> Observable.asObservable |> TestHelpers.toArrayTask CollectionAssert.AreEqual (expected, actual, "asObservable must preserve source values.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs b/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs index d0f14df..4c4ac05 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,11 +10,11 @@ open FSharp.Control.R3.Tests [] type CastTests () = [] - member _.``cast should convert values to target type`` () = + member _.``cast should convert values to target type`` () : Task = task { let source = TestHelpers.createObservable [| box 1; box 2 |] - let actual = + let! actual = source |> Observable.cast |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 1; 2 |], actual, "cast must convert boxed integers.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs b/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs index bc583a1..3365a78 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,7 +10,7 @@ open FSharp.Control.R3.Tests [] type CatchTests () = [] - member _.``catch should continue with fallback observable`` () = + member _.``catch should continue with fallback observable`` () : Task = task { use subject = new Subject () let recovered = subject @@ -17,5 +18,6 @@ type CatchTests () = let pending = TestHelpers.toArrayTask recovered subject.OnNext 1 subject.OnCompleted (Result.Failure (Exception ("boom"))) - let actual = TestHelpers.waitTask pending + let! actual = pending CollectionAssert.AreEqual ([| 1; 99 |], actual, "catch must append fallback values after source error.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs index 32de635..a62b58f 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,54 +10,47 @@ open FSharp.Control.R3.Tests [] type ChunkSkipTakeTests () = [] - member _.``chunkBySize should split by fixed size`` () = + member _.``chunkBySize should split by fixed size`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4; 5 |] - let actual = + let! actual = source |> Observable.chunkBySize 2 |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - |> Array.map Seq.toArray - Assert.AreEqual (3, actual.Length, "chunkBySize should produce expected number of chunks.") - CollectionAssert.AreEqual ([| 1; 2 |], actual[0], "First chunk should contain first two items.") - CollectionAssert.AreEqual ([| 3; 4 |], actual[1], "Second chunk should contain next two items.") - CollectionAssert.AreEqual ([| 5 |], actual[2], "Third chunk should contain the remaining item.") + let chunks = actual |> Array.map Seq.toArray + Assert.AreEqual (3, chunks.Length, "chunkBySize should produce expected number of chunks.") + CollectionAssert.AreEqual ([| 1; 2 |], chunks[0], "First chunk should contain first two items.") + CollectionAssert.AreEqual ([| 3; 4 |], chunks[1], "Second chunk should contain next two items.") + CollectionAssert.AreEqual ([| 5 |], chunks[2], "Third chunk should contain the remaining item.") + } [] - member _.``chunkBy ChunkCount should match chunkBySize`` () = + member _.``chunkBy ChunkCount should match chunkBySize`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let expected = + let! expectedChunks = source |> Observable.chunkBySize 2 |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - |> Array.map Seq.toArray - let actual = + let! actualChunks = source |> Observable.chunkBy (ChunkCount 2) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - |> Array.map Seq.toArray + let expected = expectedChunks |> Array.map Seq.toArray + let actual = actualChunks |> Array.map Seq.toArray Assert.AreEqual (expected.Length, actual.Length, "chunkBy ChunkCount must produce same chunk count as chunkBySize.") CollectionAssert.AreEqual (expected[0], actual[0], "First chunk must match chunkBySize output.") CollectionAssert.AreEqual (expected[1], actual[1], "Second chunk must match chunkBySize output.") + } [] - member _.``skip should skip leading values`` () = + member _.``skip should skip leading values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = - source - |> Observable.skip 2 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + let! actual = source |> Observable.skip 2 |> TestHelpers.toArrayTask CollectionAssert.AreEqual ([| 3; 4 |], actual, "skip must ignore the configured number of leading values.") + } [] - member _.``take should keep leading values`` () = + member _.``take should keep leading values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = - source - |> Observable.take 2 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + let! actual = source |> Observable.take 2 |> TestHelpers.toArrayTask CollectionAssert.AreEqual ([| 1; 2 |], actual, "take must emit only the configured number of leading values.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs index 91ff5b0..fed8699 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,11 +10,9 @@ open FSharp.Control.R3.Tests [] type ConcatTests () = [] - member _.``concat should append second sequence`` () = + member _.``concat should append second sequence`` () : Task = task { let first = TestHelpers.createObservable [| 1; 2 |] let second = TestHelpers.createObservable [| 3; 4 |] - let actual = - Observable.concat first second - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + let! actual = Observable.concat first second |> TestHelpers.toArrayTask CollectionAssert.AreEqual ([| 1; 2; 3; 4 |], actual, "concat must append second observable after first completes.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs b/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs index 042c7df..735f54c 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,11 +10,8 @@ open FSharp.Control.R3.Tests [] type DistinctTests () = [] - member _.``distinct should remove duplicate values`` () = + member _.``distinct should remove duplicate values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 1; 2; 2; 3 |] - let actual = - source - |> Observable.distinct - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + let! actual = source |> Observable.distinct |> TestHelpers.toArrayTask CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "distinct must emit unique values in encounter order.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs b/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs index 060102c..f86f892 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,9 +10,7 @@ open FSharp.Control.R3.Tests [] type EmptyTests () = [] - member _.``empty should emit no values`` () = - let actual = - Observable.empty () - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + member _.``empty should emit no values`` () : Task = task { + let! actual = Observable.empty () |> TestHelpers.toArrayTask Assert.AreEqual (0, actual.Length, "empty must complete without values.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs index fafcede..6d77785 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,31 +10,31 @@ open FSharp.Control.R3.Tests [] type FilterCategoryTests () = [] - member _.``where should keep matching values`` () = + member _.``where should keep matching values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = + let! actual = source |> Observable.where (fun x -> x > 2) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 3; 4 |], actual, "where must keep values matching the predicate.") + } [] - member _.``filter should keep matching values`` () = + member _.``filter should keep matching values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = + let! actual = source |> Observable.filter (fun x -> x % 2 = 0) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 2; 4 |], actual, "filter must keep only matching elements.") + } [] - member _.``choose should keep only Some values`` () = + member _.``choose should keep only Some values`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let actual = + let! actual = source |> FSharp.Control.R3.Observable.OptionExtensions.Observable.choose (fun x -> if x % 2 = 0 then Some (x * 10) else None) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 20; 40 |], actual, "choose must emit only mapped values from Some results.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs index 69ebcd0..93d1b39 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,25 +10,24 @@ open FSharp.Control.R3.Tests [] type MapCategoryTests () = [] - member _.``map should transform each value`` () = + member _.``map should transform each value`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] - let actual = + let! actual = source |> Observable.map (fun x -> x * 10) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 10; 20; 30 |], actual, "map must transform each emitted value.") + } [] - member _.``bind should match SelectMany behavior`` () = + member _.``bind should match SelectMany behavior`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2 |] - let expected = + let! expected = ObservableExtensions.SelectMany (source, fun x -> Observable.Return (x * 10)) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - let actual = + let! actual = source |> Observable.bind (fun x -> Observable.singleton (x * 10)) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual (expected, actual, "bind must match SelectMany behavior.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs index e52ab8c..08607f4 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,15 +10,14 @@ open FSharp.Control.R3.Tests [] type MapiTests () = [] - member _.``mapi should pass index as first argument`` () = + member _.``mapi should pass index as first argument`` () : Task = task { let source = TestHelpers.createObservable [| 4; 5; 6 |] - let expected = + let! expected = ObservableExtensions.Select (source, fun value index -> (index * 10) + value) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - let actual = + let! actual = source |> Observable.mapi (fun index value -> (index * 10) + value) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual (expected, actual, "mapi must pass index first and value second.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs index 8066eb3..cb44956 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,15 +10,14 @@ open FSharp.Control.R3.Tests [] type MergeTests () = [] - member _.``merge should match direct R3 merge`` () = + member _.``merge should match direct R3 merge`` () : Task = task { let source1 = TestHelpers.createObservable [| 1; 2 |] let source2 = TestHelpers.createObservable [| 10; 20 |] - let expected = + let! expected = ObservableExtensions.Merge (source1, source2) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask - let actual = + let! actual = Observable.merge (source1, source2) |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual (expected, actual, "merge wrapper must match direct R3 merge output.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs index 1f1e69d..68421f2 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,11 +10,11 @@ open FSharp.Control.R3.Tests [] type OfTypeTests () = [] - member _.``ofType should keep only requested runtime type`` () = + member _.``ofType should keep only requested runtime type`` () : Task = task { let source = TestHelpers.createObservable [| box 1; box "x"; box 2 |] - let actual = + let! actual = source |> Observable.ofType |> TestHelpers.toArrayTask - |> TestHelpers.waitTask CollectionAssert.AreEqual ([| 1; 2 |], actual, "ofType must emit only values of requested type.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs b/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs index 9431432..b2e7118 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs @@ -1,6 +1,7 @@ namespace FSharp.Control.R3.Tests.Observable open System +open System.Threading.Tasks open Microsoft.VisualStudio.TestTools.UnitTesting open R3 open FSharp.Control.R3 @@ -9,9 +10,7 @@ open FSharp.Control.R3.Tests [] type SingletonTests () = [] - member _.``singleton should emit one value`` () = - let actual = - Observable.singleton 42 - |> TestHelpers.toArrayTask - |> TestHelpers.waitTask + member _.``singleton should emit one value`` () : Task = task { + let! actual = Observable.singleton 42 |> TestHelpers.toArrayTask CollectionAssert.AreEqual ([| 42 |], actual, "singleton must emit exactly one value.") + } diff --git a/tests/FSharp.Control.R3.Tests/TestHelpers.fs b/tests/FSharp.Control.R3.Tests/TestHelpers.fs index ec7e818..28f3985 100644 --- a/tests/FSharp.Control.R3.Tests/TestHelpers.fs +++ b/tests/FSharp.Control.R3.Tests/TestHelpers.fs @@ -2,17 +2,12 @@ module FSharp.Control.R3.Tests.TestHelpers open System open System.Threading -open System.Threading.Tasks open R3 let createObservable (values : 'T array) = Observable.ToObservable values let toArrayTask (source : Observable<'T>) = ObservableExtensions.ToArrayAsync source -let waitTask (task : Task<'T>) = task.Result - -let waitAsync (work : Async<'T>) = Async.RunSynchronously work - let cancellationToken = CancellationToken.None let stringComparer = StringComparer.OrdinalIgnoreCase From 4935a5ba79cad622808812685d07b6941d302d54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 15:16:52 +0000 Subject: [PATCH 18/18] Regroup Observable tests by category Agent-Logs-Url: https://github.com/fsprojects/FSharp.Control.R3/sessions/408d36f0-589c-4395-8c1e-75d8acdbe389 Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> --- .../Observable/AggregationCategoryTests.fs | 44 +++++++++++++++++++ ...eTests.fs => AllSelectionCategoryTests.fs} | 39 +++++++++++++++- .../Observable/AsObservableTests.fs | 18 -------- .../Observable/CastTests.fs | 20 --------- .../Observable/CatchTests.fs | 23 ---------- .../Observable/ConcatTests.fs | 18 -------- .../Observable/ConversionCategoryTests.fs | 38 ++++++++++++++++ .../Observable/DistinctTests.fs | 17 ------- .../Observable/EmptyTests.fs | 16 ------- .../Observable/FilterCategoryTests.fs | 40 ----------------- .../Observable/MapiTests.fs | 23 ---------- .../Observable/MergeTests.fs | 23 ---------- .../Observable/OfTypeTests.fs | 20 --------- ...Tests.fs => SingleElementCategoryTests.fs} | 8 +++- ...ests.fs => TransformationCategoryTests.fs} | 15 ++++++- 15 files changed, 141 insertions(+), 221 deletions(-) create mode 100644 tests/FSharp.Control.R3.Tests/Observable/AggregationCategoryTests.fs rename tests/FSharp.Control.R3.Tests/Observable/{ChunkSkipTakeTests.fs => AllSelectionCategoryTests.fs} (60%) delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/CastTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs create mode 100644 tests/FSharp.Control.R3.Tests/Observable/ConversionCategoryTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs delete mode 100644 tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs rename tests/FSharp.Control.R3.Tests/Observable/{SingletonTests.fs => SingleElementCategoryTests.fs} (62%) rename tests/FSharp.Control.R3.Tests/Observable/{MapCategoryTests.fs => TransformationCategoryTests.fs} (65%) diff --git a/tests/FSharp.Control.R3.Tests/Observable/AggregationCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/AggregationCategoryTests.fs new file mode 100644 index 0000000..f0a2132 --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/AggregationCategoryTests.fs @@ -0,0 +1,44 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type AggregationCategoryTests () = + [] + member _.``concat should append second sequence`` () : Task = task { + let first = TestHelpers.createObservable [| 1; 2 |] + let second = TestHelpers.createObservable [| 3; 4 |] + let! actual = Observable.concat first second |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 1; 2; 3; 4 |], actual, "concat must append second observable after first completes.") + } + + [] + member _.``merge should match direct R3 merge`` () : Task = task { + let source1 = TestHelpers.createObservable [| 1; 2 |] + let source2 = TestHelpers.createObservable [| 10; 20 |] + let! expected = + ObservableExtensions.Merge (source1, source2) + |> TestHelpers.toArrayTask + let! actual = + Observable.merge (source1, source2) + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual (expected, actual, "merge wrapper must match direct R3 merge output.") + } + + [] + member _.``catch should continue with fallback observable`` () : Task = task { + use subject = new Subject () + let recovered = + subject + |> Observable.catch (fun (_ : exn) -> Observable.singleton 99) + let pending = TestHelpers.toArrayTask recovered + subject.OnNext 1 + subject.OnCompleted (Result.Failure (Exception ("boom"))) + let! actual = pending + CollectionAssert.AreEqual ([| 1; 99 |], actual, "catch must append fallback values after source error.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/AllSelectionCategoryTests.fs similarity index 60% rename from tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs rename to tests/FSharp.Control.R3.Tests/Observable/AllSelectionCategoryTests.fs index a62b58f..d8c045d 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/ChunkSkipTakeTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/AllSelectionCategoryTests.fs @@ -8,7 +8,44 @@ open FSharp.Control.R3 open FSharp.Control.R3.Tests [] -type ChunkSkipTakeTests () = +type AllSelectionCategoryTests () = + [] + member _.``where should keep matching values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = + source + |> Observable.where (fun x -> x > 2) + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 3; 4 |], actual, "where must keep values matching the predicate.") + } + + [] + member _.``filter should keep matching values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = + source + |> Observable.filter (fun x -> x % 2 = 0) + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 2; 4 |], actual, "filter must keep only matching elements.") + } + + [] + member _.``choose should keep only Some values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] + let! actual = + source + |> FSharp.Control.R3.Observable.OptionExtensions.Observable.choose (fun x -> if x % 2 = 0 then Some (x * 10) else None) + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 20; 40 |], actual, "choose must emit only mapped values from Some results.") + } + + [] + member _.``distinct should remove duplicate values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 1; 2; 2; 3 |] + let! actual = source |> Observable.distinct |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "distinct must emit unique values in encounter order.") + } + [] member _.``chunkBySize should split by fixed size`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3; 4; 5 |] diff --git a/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs b/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs deleted file mode 100644 index 288fae6..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/AsObservableTests.fs +++ /dev/null @@ -1,18 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type AsObservableTests () = - [] - member _.``asObservable should keep source values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3 |] - let! expected = source |> TestHelpers.toArrayTask - let! actual = source |> Observable.asObservable |> TestHelpers.toArrayTask - CollectionAssert.AreEqual (expected, actual, "asObservable must preserve source values.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs b/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs deleted file mode 100644 index 4c4ac05..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/CastTests.fs +++ /dev/null @@ -1,20 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type CastTests () = - [] - member _.``cast should convert values to target type`` () : Task = task { - let source = TestHelpers.createObservable [| box 1; box 2 |] - let! actual = - source - |> Observable.cast - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 1; 2 |], actual, "cast must convert boxed integers.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs b/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs deleted file mode 100644 index 3365a78..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/CatchTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type CatchTests () = - [] - member _.``catch should continue with fallback observable`` () : Task = task { - use subject = new Subject () - let recovered = - subject - |> Observable.catch (fun (_ : exn) -> Observable.singleton 99) - let pending = TestHelpers.toArrayTask recovered - subject.OnNext 1 - subject.OnCompleted (Result.Failure (Exception ("boom"))) - let! actual = pending - CollectionAssert.AreEqual ([| 1; 99 |], actual, "catch must append fallback values after source error.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs deleted file mode 100644 index fed8699..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/ConcatTests.fs +++ /dev/null @@ -1,18 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type ConcatTests () = - [] - member _.``concat should append second sequence`` () : Task = task { - let first = TestHelpers.createObservable [| 1; 2 |] - let second = TestHelpers.createObservable [| 3; 4 |] - let! actual = Observable.concat first second |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 1; 2; 3; 4 |], actual, "concat must append second observable after first completes.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/ConversionCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/ConversionCategoryTests.fs new file mode 100644 index 0000000..a5f755d --- /dev/null +++ b/tests/FSharp.Control.R3.Tests/Observable/ConversionCategoryTests.fs @@ -0,0 +1,38 @@ +namespace FSharp.Control.R3.Tests.Observable + +open System +open System.Threading.Tasks +open Microsoft.VisualStudio.TestTools.UnitTesting +open R3 +open FSharp.Control.R3 +open FSharp.Control.R3.Tests + +[] +type ConversionCategoryTests () = + [] + member _.``asObservable should keep source values`` () : Task = task { + let source = TestHelpers.createObservable [| 1; 2; 3 |] + let! expected = source |> TestHelpers.toArrayTask + let! actual = source |> Observable.asObservable |> TestHelpers.toArrayTask + CollectionAssert.AreEqual (expected, actual, "asObservable must preserve source values.") + } + + [] + member _.``cast should convert values to target type`` () : Task = task { + let source = TestHelpers.createObservable [| box 1; box 2 |] + let! actual = + source + |> Observable.cast + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 1; 2 |], actual, "cast must convert boxed integers.") + } + + [] + member _.``ofType should keep only requested runtime type`` () : Task = task { + let source = TestHelpers.createObservable [| box 1; box "x"; box 2 |] + let! actual = + source + |> Observable.ofType + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual ([| 1; 2 |], actual, "ofType must emit only values of requested type.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs b/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs deleted file mode 100644 index 735f54c..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/DistinctTests.fs +++ /dev/null @@ -1,17 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type DistinctTests () = - [] - member _.``distinct should remove duplicate values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 1; 2; 2; 3 |] - let! actual = source |> Observable.distinct |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 1; 2; 3 |], actual, "distinct must emit unique values in encounter order.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs b/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs deleted file mode 100644 index f86f892..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/EmptyTests.fs +++ /dev/null @@ -1,16 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type EmptyTests () = - [] - member _.``empty should emit no values`` () : Task = task { - let! actual = Observable.empty () |> TestHelpers.toArrayTask - Assert.AreEqual (0, actual.Length, "empty must complete without values.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs deleted file mode 100644 index 6d77785..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/FilterCategoryTests.fs +++ /dev/null @@ -1,40 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type FilterCategoryTests () = - [] - member _.``where should keep matching values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let! actual = - source - |> Observable.where (fun x -> x > 2) - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 3; 4 |], actual, "where must keep values matching the predicate.") - } - - [] - member _.``filter should keep matching values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let! actual = - source - |> Observable.filter (fun x -> x % 2 = 0) - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 2; 4 |], actual, "filter must keep only matching elements.") - } - - [] - member _.``choose should keep only Some values`` () : Task = task { - let source = TestHelpers.createObservable [| 1; 2; 3; 4 |] - let! actual = - source - |> FSharp.Control.R3.Observable.OptionExtensions.Observable.choose (fun x -> if x % 2 = 0 then Some (x * 10) else None) - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 20; 40 |], actual, "choose must emit only mapped values from Some results.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs deleted file mode 100644 index 08607f4..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/MapiTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type MapiTests () = - [] - member _.``mapi should pass index as first argument`` () : Task = task { - let source = TestHelpers.createObservable [| 4; 5; 6 |] - let! expected = - ObservableExtensions.Select (source, fun value index -> (index * 10) + value) - |> TestHelpers.toArrayTask - let! actual = - source - |> Observable.mapi (fun index value -> (index * 10) + value) - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual (expected, actual, "mapi must pass index first and value second.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs deleted file mode 100644 index cb44956..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/MergeTests.fs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type MergeTests () = - [] - member _.``merge should match direct R3 merge`` () : Task = task { - let source1 = TestHelpers.createObservable [| 1; 2 |] - let source2 = TestHelpers.createObservable [| 10; 20 |] - let! expected = - ObservableExtensions.Merge (source1, source2) - |> TestHelpers.toArrayTask - let! actual = - Observable.merge (source1, source2) - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual (expected, actual, "merge wrapper must match direct R3 merge output.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs b/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs deleted file mode 100644 index 68421f2..0000000 --- a/tests/FSharp.Control.R3.Tests/Observable/OfTypeTests.fs +++ /dev/null @@ -1,20 +0,0 @@ -namespace FSharp.Control.R3.Tests.Observable - -open System -open System.Threading.Tasks -open Microsoft.VisualStudio.TestTools.UnitTesting -open R3 -open FSharp.Control.R3 -open FSharp.Control.R3.Tests - -[] -type OfTypeTests () = - [] - member _.``ofType should keep only requested runtime type`` () : Task = task { - let source = TestHelpers.createObservable [| box 1; box "x"; box 2 |] - let! actual = - source - |> Observable.ofType - |> TestHelpers.toArrayTask - CollectionAssert.AreEqual ([| 1; 2 |], actual, "ofType must emit only values of requested type.") - } diff --git a/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs b/tests/FSharp.Control.R3.Tests/Observable/SingleElementCategoryTests.fs similarity index 62% rename from tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs rename to tests/FSharp.Control.R3.Tests/Observable/SingleElementCategoryTests.fs index b2e7118..d5192d4 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/SingletonTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/SingleElementCategoryTests.fs @@ -8,9 +8,15 @@ open FSharp.Control.R3 open FSharp.Control.R3.Tests [] -type SingletonTests () = +type SingleElementCategoryTests () = [] member _.``singleton should emit one value`` () : Task = task { let! actual = Observable.singleton 42 |> TestHelpers.toArrayTask CollectionAssert.AreEqual ([| 42 |], actual, "singleton must emit exactly one value.") } + + [] + member _.``empty should emit no values`` () : Task = task { + let! actual = Observable.empty () |> TestHelpers.toArrayTask + Assert.AreEqual (0, actual.Length, "empty must complete without values.") + } diff --git a/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs b/tests/FSharp.Control.R3.Tests/Observable/TransformationCategoryTests.fs similarity index 65% rename from tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs rename to tests/FSharp.Control.R3.Tests/Observable/TransformationCategoryTests.fs index 93d1b39..3de64c1 100644 --- a/tests/FSharp.Control.R3.Tests/Observable/MapCategoryTests.fs +++ b/tests/FSharp.Control.R3.Tests/Observable/TransformationCategoryTests.fs @@ -8,7 +8,7 @@ open FSharp.Control.R3 open FSharp.Control.R3.Tests [] -type MapCategoryTests () = +type TransformationCategoryTests () = [] member _.``map should transform each value`` () : Task = task { let source = TestHelpers.createObservable [| 1; 2; 3 |] @@ -31,3 +31,16 @@ type MapCategoryTests () = |> TestHelpers.toArrayTask CollectionAssert.AreEqual (expected, actual, "bind must match SelectMany behavior.") } + + [] + member _.``mapi should pass index as first argument`` () : Task = task { + let source = TestHelpers.createObservable [| 4; 5; 6 |] + let! expected = + ObservableExtensions.Select (source, fun value index -> (index * 10) + value) + |> TestHelpers.toArrayTask + let! actual = + source + |> Observable.mapi (fun index value -> (index * 10) + value) + |> TestHelpers.toArrayTask + CollectionAssert.AreEqual (expected, actual, "mapi must pass index first and value second.") + }