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.")
+ }