Преглед изворни кода

4.x: Refactor Linq operator unit tests into separate files/classes (#527)

* 4.x: Refactor Linq operator unit tests into separate files/clases

* Remove utfile
David Karnok пре 7 година
родитељ
комит
1a4c2f2691
100 измењених фајлова са 62356 додато и 1889 уклоњено
  1. 531 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AggregateTest.cs
  2. 260 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AllTest.cs
  3. 380 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AmbTest.cs
  4. 12 688
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AndTest.cs
  5. 329 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AnyTest.cs
  6. 187 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AsObservableTest.cs
  7. 1660 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AverageTest.cs
  8. 192 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AwaitTest.cs
  9. 1318 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/BufferTest.cs
  10. 466 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CaseTest.cs
  11. 232 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CastTest.cs
  12. 1008 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CatchTest.cs
  13. 187 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ChunkifyTest.cs
  14. 299 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CollectTest.cs
  15. 3815 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CombineLatestTest.cs
  16. 954 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ConcatTest.cs
  17. 317 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ContainsTest.cs
  18. 483 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CountTest.cs
  19. 707 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CreateAsyncTest.cs
  20. 998 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CreateTest.cs
  21. 180 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DefaultIfEmptyTest.cs
  22. 82 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DeferAsyncTest.cs
  23. 153 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DeferTest.cs
  24. 166 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DelaySubscriptionTest.cs
  25. 1840 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DelayTest.cs
  26. 229 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DematerializeTest.cs
  27. 456 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DistinctTest.cs
  28. 426 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DistinctUntilChangedTest.cs
  29. 737 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DoTest.cs
  30. 228 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DoWhileTest.cs
  31. 135 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ElementAtOrDefaultTest.cs
  32. 130 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ElementAtTest.cs
  33. 109 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/EmptyTest.cs
  34. 192 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ExpandTest.cs
  35. 157 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FinallyTest.cs
  36. 240 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstAsyncTest.cs
  37. 246 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstOrDefaultAsyncTest.cs
  38. 133 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstOrDefaultTest.cs
  39. 99 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstTest.cs
  40. 576 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForEachAsyncTest.cs
  41. 128 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForEachTest.cs
  42. 126 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForTest.cs
  43. 638 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForkJoinTest.cs
  44. 1192 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromAsyncPatternTest.cs
  45. 402 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromAsyncTest.cs
  46. 204 551
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromEventPatternTest.cs
  47. 410 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromEventTest.cs
  48. 463 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GenerateTest.cs
  49. 172 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GetEnumeratorTest.cs
  50. 3454 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GroupByTest.cs
  51. 3763 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GroupByUntilTest.cs
  52. 1235 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GroupJoinTest.cs
  53. 313 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IfTest.cs
  54. 152 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IgnoreElementsTest.cs
  55. 125 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IntervalTest.cs
  56. 127 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IsEmptyTest.cs
  57. 1119 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/JoinTest.cs
  58. 240 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastAsyncTest.cs
  59. 242 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastOrDefaultAsyncTest.cs
  60. 75 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastOrDefaultTest.cs
  61. 75 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastTest.cs
  62. 183 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LatestTest.cs
  63. 47 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LetTest.cs
  64. 461 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LongCountTest.cs
  65. 133 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ManySelectTest.cs
  66. 121 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MaterializeTest.cs
  67. 381 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MaxByTest.cs
  68. 2428 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MaxTest.cs
  69. 1966 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MergeTest.cs
  70. 409 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MinByTest.cs
  71. 2399 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MinTest.cs
  72. 208 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MostRecentTest.cs
  73. 483 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MulticastTest.cs
  74. 68 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/NeverTest.cs
  75. 177 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/NextTest.cs
  76. 19 476
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ObserveOnTest.cs
  77. 160 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/OfTypeTest.cs
  78. 735 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/OnErrorResumeNextTest.cs
  79. 370 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/PublishLastTest.cs
  80. 856 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/PublishTest.cs
  81. 220 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RangeTest.cs
  82. 188 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RefCountTest.cs
  83. 548 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RepeatTest.cs
  84. 1005 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ReplayTest.cs
  85. 363 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RetryTest.cs
  86. 119 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ReturnTest.cs
  87. 8 174
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RunAsyncTest.cs
  88. 615 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SampleTest.cs
  89. 348 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ScanTest.cs
  90. 6842 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SelectManyTest.cs
  91. 636 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SelectTest.cs
  92. 1088 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SequenceEqualTest.cs
  93. 264 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleAsyncTest.cs
  94. 300 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleOrDefaultAsyncTest.cs
  95. 89 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleOrDefaultTest.cs
  96. 83 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleTest.cs
  97. 659 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipLastTest.cs
  98. 714 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipTest.cs
  99. 645 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipUntilTest.cs
  100. 514 0
      Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipWhileTest.cs

+ 531 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AggregateTest.cs

@@ -0,0 +1,531 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class AggregateTest : ReactiveTest
+    {
+        [Fact]
+        public void Aggregate_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int, int>(default(IObservable<int>), 1, (x, y) => x + y));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int, int>(DummyObservable<int>.Instance, 1, default(Func<int, int, int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int>(default(IObservable<int>), (x, y) => x + y));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int>(DummyObservable<int>.Instance, default(Func<int, int, int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int, int, int>(default(IObservable<int>), 1, (x, y) => x + y, x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int, int, int>(DummyObservable<int>.Instance, 1, default(Func<int, int, int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Aggregate<int, int, int>(DummyObservable<int>.Instance, 1, (x, y) => x + y, default(Func<int, int>)));
+        }
+
+        [Fact]
+        public void AggregateWithSeed_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 42),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeed_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 24),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250, 42 + 24),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeed_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeed_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeed_Range()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 42 + Enumerable.Range(0, 5).Sum()),
+                OnCompleted<int>(260)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 260)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeed_AccumulatorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(0, (acc, x) => { if (x < 3) return acc + x; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(240, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+               Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x, x => x * 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 42 * 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 24),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x, x => x * 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250, (42 + 24) * 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x, x => x * 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x, x => x * 5)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_Range()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(42, (acc, x) => acc + x, x => x * 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, (42 + Enumerable.Range(0, 5).Sum()) * 5),
+                OnCompleted<int>(260)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 260)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_AccumulatorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate(0, (acc, x) => { if (x < 3) return acc + x; throw ex; }, x => x * 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(240, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+               Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithSeedAndResult_ResultSelectorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate<int, int, int>(0, (acc, x) => acc + x, x => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(260, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+               Subscribe(200, 260)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithoutSeed_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithoutSeed_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 24),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250, 24),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithoutSeed_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithoutSeed_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void AggregateWithoutSeed_Range()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, Enumerable.Range(0, 5).Sum()),
+                OnCompleted<int>(260)
+            );
+
+            xs.Subscriptions.AssertEqual(
+               Subscribe(200, 260)
+            );
+        }
+
+        [Fact]
+        public void AggregateWithoutSeed_AccumulatorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 0),
+                OnNext(220, 1),
+                OnNext(230, 2),
+                OnNext(240, 3),
+                OnNext(250, 4),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Aggregate((acc, x) => { if (x < 3) return acc + x; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(240, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+               Subscribe(200, 240)
+            );
+        }
+    }
+}

+ 260 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AllTest.cs

@@ -0,0 +1,260 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class AllTest : ReactiveTest
+    {
+        [Fact]
+        public void All_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.All(DummyObservable<int>.Instance, default(Func<int, bool>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.All(default(IObservable<int>), x => true));
+        }
+
+        [Fact]
+        public void All_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, true),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void All_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, true),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void All_ReturnNotMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, false),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void All_SomeNoneMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnNext(220, -3),
+                OnNext(230, -4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, false),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void All_SomeMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnNext(220, 3),
+                OnNext(230, -4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, false),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void All_SomeAllMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, true),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void All_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void All_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void All_PredicateThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.All(x => { if (x < 4) return true; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+    }
+}

+ 380 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AmbTest.cs

@@ -0,0 +1,380 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class AmbTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Amb_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Amb((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Amb((IEnumerable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Amb(null, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Amb(DummyObservable<int>.Instance, null));
+        }
+
+        [Fact]
+        public void Amb_Never2()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                l.Amb(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Amb_Never3()
+        {
+            var scheduler = new TestScheduler();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n3 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                new[] { n1, n2, n3 }.Amb()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            n2.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            n3.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Amb_Never3_Params()
+        {
+            var scheduler = new TestScheduler();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n3 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Amb(n1, n2, n3)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            n2.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            n3.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Amb_NeverEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var n = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(225)
+            );
+
+            var res = scheduler.Start(() =>
+                n.Amb(e)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(225)
+            );
+
+            n.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void Amb_EmptyNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var n = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(225)
+            );
+
+            var res = scheduler.Start(() =>
+                e.Amb(n)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(225)
+            );
+
+            n.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void Amb_RegularShouldDisposeLoser()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(240)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Amb(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(240)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Amb_WinnerThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Amb(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Amb_LoserThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Amb(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Amb_ThrowsBeforeElectionLeft()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Amb(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Amb_ThrowsBeforeElectionRight()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Amb(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+    }
+}

+ 12 - 688
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/ObservableJoinsTest.cs → Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AndTest.cs

@@ -4,17 +4,24 @@
 
 using System;
 using System.Collections.Generic;
-using System.Reactive.Joins;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
 using System.Reactive.Linq;
 using Microsoft.Reactive.Testing;
 using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
 
 namespace ReactiveTests.Tests
 {
-    
-    public partial class ObservableWhensTest : ReactiveTest
+    public class AndTest : ReactiveTest
     {
-        #region And
 
         [Fact]
         public void And_ArgumentChecking()
@@ -1014,688 +1021,5 @@ namespace ReactiveTests.Tests
             }
         }
 
-        #endregion
-
-        #region Then
-
-        [Fact]
-        public void Then_ArgumentChecking()
-        {
-            var someObservable = Observable.Return(1);
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Then<int, int>(null, _ => _));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Then<int, int>(someObservable, null));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.And<int, int>(someObservable, someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).And(someObservable).Then<int>(null));
-        }
-
-        [Fact]
-        public void Then1()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(210, 1),
-                OnCompleted<int>(220)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(xs.Then(a => a))
-            );
-
-            res.Messages.AssertEqual(
-                OnNext(210, 1),
-                OnCompleted<int>(220)
-            );
-        }
-
-        [Fact]
-        public void Then1Error()
-        {
-            var ex = new Exception();
-
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnError<int>(210, ex)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(xs.Then(a => a))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then1Throws()
-        {
-            var ex = new Exception();
-
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(210, 1),
-                OnCompleted<int>(220)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(xs.Then<int, int>(a => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then2Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 2;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).Then<int>((a, b) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then3Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 3;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).Then<int>((a, b, c) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then4Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 4;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).Then<int>((a, b, c, d) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then5Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 5;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).Then<int>((a, b, c, d, e) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then6Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 6;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).Then<int>((a, b, c, d, e, f) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then7Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 7;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).Then<int>((a, b, c, d, e, f, g) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then8Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 8;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).Then<int>((a, b, c, d, e, f, g, h) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then9Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 9;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).Then<int>((a, b, c, d, e, f, g, h, i_) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then10Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 10;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).Then<int>((a, b, c, d, e, f, g, h, i_, j) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then11Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 11;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).And(obs[10]).Then<int>((a, b, c, d, e, f, g, h, i_, j, k) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then12Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 12;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).And(obs[10]).And(obs[11]).Then<int>((a, b, c, d, e, f, g, h, i_, j, k, l) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then13Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 13;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).And(obs[10]).And(obs[11]).And(obs[12]).Then<int>((a, b, c, d, e, f, g, h, i_, j, k, l, m) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then14Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 14;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).And(obs[10]).And(obs[11]).And(obs[12]).And(obs[13]).Then<int>((a, b, c, d, e, f, g, h, i_, j, k, l, m, n) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then15Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 15;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).And(obs[10]).And(obs[11]).And(obs[12]).And(obs[13]).And(obs[14]).Then<int>((a, b, c, d, e, f, g, h, i_, j, k, l, m, n, o) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        [Fact]
-        public void Then16Throws()
-        {
-            var scheduler = new TestScheduler();
-            var ex = new Exception();
-
-            const int N = 16;
-
-            var obs = new List<IObservable<int>>();
-            for (int i = 0; i < N; i++)
-            {
-                obs.Add(scheduler.CreateHotObservable(
-                    OnNext(210, 1),
-                    OnCompleted<int>(220)
-                ));
-            }
-
-            var res = scheduler.Start(() =>
-                Observable.When(obs[0].And(obs[1]).And(obs[2]).And(obs[3]).And(obs[4]).And(obs[5]).And(obs[6]).And(obs[7]).And(obs[8]).And(obs[9]).And(obs[10]).And(obs[11]).And(obs[12]).And(obs[13]).And(obs[14]).And(obs[15]).Then<int>((a, b, c, d, e, f, g, h, i_, j, k, l, m, n, o, p) => { throw ex; }))
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(210, ex)
-            );
-        }
-
-        #endregion
-
-        #region When
-
-        [Fact]
-        public void When_ArgumentChecking()
-        {
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.When<int>((Plan<int>[])null));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.When<int>((IEnumerable<Plan<int>>)null));
-        }
-
-        [Fact]
-        public void WhenMultipleDataSymmetric()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(210, 1),
-                OnNext(220, 2),
-                OnNext(230, 3),
-                OnCompleted<int>(240)
-            );
-
-            var ys = scheduler.CreateHotObservable(
-                OnNext(240, 4),
-                OnNext(250, 5),
-                OnNext(260, 6),
-                OnCompleted<int>(270)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(
-                    xs.And(ys).Then((x, y) => x + y)
-                )
-            );
-
-            res.Messages.AssertEqual(
-                OnNext(240, 1 + 4),
-                OnNext(250, 2 + 5),
-                OnNext(260, 3 + 6),
-                OnCompleted<int>(270)
-            );
-        }
-
-        [Fact]
-        public void WhenMultipleDataAsymmetric()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(210, 1),
-                OnNext(220, 2),
-                OnNext(230, 3),
-                OnCompleted<int>(240)
-            );
-
-            var ys = scheduler.CreateHotObservable(
-                OnNext(240, 4),
-                OnNext(250, 5),
-                OnCompleted<int>(270)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(
-                    xs.And(ys).Then((x, y) => x + y)
-                )
-            );
-
-            res.Messages.AssertEqual(
-                OnNext(240, 1 + 4),
-                OnNext(250, 2 + 5),
-                OnCompleted<int>(270)
-            );
-        }
-
-        [Fact]
-        public void WhenEmptyEmpty()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnCompleted<int>(240)
-            );
-
-            var ys = scheduler.CreateHotObservable(
-                OnCompleted<int>(270)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(
-                    xs.And(ys).Then((x, y) => x + y)
-                )
-            );
-
-            res.Messages.AssertEqual(
-                OnCompleted<int>(270)
-            );
-        }
-
-        [Fact]
-        public void WhenNeverNever()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = Observable.Never<int>();
-            var ys = Observable.Never<int>();
-
-            var res = scheduler.Start(() =>
-                Observable.When(
-                    xs.And(ys).Then((x, y) => x + y)
-                )
-            );
-
-            res.Messages.AssertEqual(
-            );
-        }
-
-        [Fact]
-        public void WhenThrowNonEmpty()
-        {
-            var ex = new Exception();
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnError<int>(240, ex)
-            );
-
-            var ys = scheduler.CreateHotObservable(
-                OnCompleted<int>(270)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(
-                    xs.And(ys).Then((x, y) => x + y)
-                )
-            );
-
-            res.Messages.AssertEqual(
-                OnError<int>(240, ex)
-            );
-        }
-
-        [Fact]
-        public void ComplicatedWhen()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(210, 1),
-                OnNext(220, 2),
-                OnNext(230, 3),
-                OnCompleted<int>(240)
-            );
-
-            var ys = scheduler.CreateHotObservable(
-                OnNext(240, 4),
-                OnNext(250, 5),
-                OnNext(260, 6),
-                OnCompleted<int>(270)
-            );
-
-            var zs = scheduler.CreateHotObservable(
-                OnNext(220, 7),
-                OnNext(230, 8),
-                OnNext(240, 9),
-                OnCompleted<int>(300)
-            );
-
-            var res = scheduler.Start(() =>
-                Observable.When(
-                    xs.And(ys).Then((x, y) => x + y),
-                    xs.And(zs).Then((x, z) => x * z),
-                    ys.And(zs).Then((y, z) => y - z)
-                )
-            );
-
-            res.Messages.AssertEqual(
-                OnNext(220, 1 * 7),
-                OnNext(230, 2 * 8),
-                OnNext(240, 3 + 4),
-                OnNext(250, 5 - 9),
-                OnCompleted<int>(300)
-            );
-        }
-
-        [Fact]
-        public void When_PlansIteratorThrows()
-        {
-            var ex = new Exception();
-            var _e = default(Exception);
-
-            GetPlans(ex).When().Subscribe(_ => { }, e => { _e = e; });
-            Assert.Same(_e, ex);
-        }
-
-        private IEnumerable<Plan<int>> GetPlans(Exception ex)
-        {
-            if (ex != null)
-                throw ex;
-            
-            yield break;
-        }
-
-        #endregion
     }
-}
+}

+ 329 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AnyTest.cs

@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class AnyTest : ReactiveTest
+    {
+        [Fact]
+        public void Any_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Any(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Any(DummyObservable<int>.Instance, default(Func<int, bool>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Any(default(IObservable<int>), x => true));
+        }
+
+        [Fact]
+        public void Any_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Any_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, true),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Any_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Any_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, true),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_ReturnNotMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_SomeNoneMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnNext(220, -3),
+                OnNext(230, -4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_SomeMatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnNext(220, 3),
+                OnNext(230, -4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, true),
+                OnCompleted<bool>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => x > 0)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Any_Predicate_PredicateThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, -2),
+                OnNext(220, 3),
+                OnNext(230, -4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Any(x => { if (x != -4) return false; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+    }
+}

+ 187 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AsObservableTest.cs

@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class AsObservableTest : ReactiveTest
+    {
+
+        [Fact]
+        public void AsObservable_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.AsObservable<int>(null));
+        }
+
+        [Fact]
+        public void AsObservable_AsObservable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            var ys = xs.AsObservable().AsObservable();
+
+            Assert.NotSame(xs, ys);
+
+            var res = scheduler.Start(() =>
+                ys
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AsObservable_Hides()
+        {
+            var xs = Observable.Empty<int>();
+
+            var res = xs.AsObservable();
+
+            Assert.NotSame(xs, res);
+        }
+
+        [Fact]
+        public void AsObservable_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Never<int>();
+
+            var res = scheduler.Start(() =>
+                xs.AsObservable()
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void AsObservable_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.AsObservable()
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AsObservable_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.AsObservable()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AsObservable_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.AsObservable()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void AsObservable_IsNotEager()
+        {
+            var scheduler = new TestScheduler();
+
+            bool subscribed = false;
+            var xs = Observable.Create<int>(obs =>
+            {
+                subscribed = true;
+
+                var disp = scheduler.CreateHotObservable(
+                    OnNext(150, 1),
+                    OnNext(220, 2),
+                    OnCompleted<int>(250)
+                ).Subscribe(obs);
+
+                return disp.Dispose;
+            });
+
+            xs.AsObservable();
+            Assert.False(subscribed);
+
+            var res = scheduler.Start(() =>
+                xs.AsObservable()
+            );
+            Assert.True(subscribed);
+        }
+
+    }
+}

+ 1660 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AverageTest.cs

@@ -0,0 +1,1660 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class AverageTest : ReactiveTest
+    {
+        [Fact]
+        public void Average_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<double>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<float>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<decimal>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<long>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<int?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<double?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<float?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<decimal?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<long?>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(int)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(double)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(float)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(decimal)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(long)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(int?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(double?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(float?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(decimal?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(default(IObservable<DateTime>), _ => default(long?)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, double>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, float>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, decimal>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, long>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, int?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, double?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, float?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, decimal?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Average(Observable.Empty<DateTime>(), default(Func<DateTime, long?>)));
+        }
+
+        [Fact]
+        public void Average_Int32_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Int32_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Int32_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 3),
+                OnNext(220, 4),
+                OnNext(230, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Int32_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Int32_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Int64_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Int64_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnNext(210, 2L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Int64_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnNext(210, 3L),
+                OnNext(220, 4L),
+                OnNext(230, 2L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Int64_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnError<long>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Int64_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Double_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Double_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnNext(210, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Double_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnNext(210, 3.0),
+                OnNext(220, 4.0),
+                OnNext(230, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Double_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnError<double>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Double_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Float_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Float_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnNext(210, 2f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2f),
+                OnCompleted<float>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Float_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnNext(210, 3f),
+                OnNext(220, 4f),
+                OnNext(230, 2f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3f),
+                OnCompleted<float>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Float_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnError<float>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Float_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Decimal_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Decimal_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnNext(210, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Decimal_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnNext(210, 3m),
+                OnNext(220, 4m),
+                OnNext(230, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3m),
+                OnCompleted<decimal>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Decimal_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnError<decimal>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Decimal_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int32_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)null),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int32_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int32_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)3),
+                OnNext(220, (int?)null),
+                OnNext(230, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.5),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int32_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnError<int?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int32_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int64_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)null),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int64_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int64_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)3L),
+                OnNext(220, (long?)null),
+                OnNext(230, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.5),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int64_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnError<long?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Int64_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Double_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)null),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Double_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Double_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)3.0),
+                OnNext(220, (double?)null),
+                OnNext(230, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.5),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Double_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnError<double?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Double_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Float_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)null),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Float_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Float_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)3f),
+                OnNext(220, (float?)null),
+                OnNext(230, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)2.5f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Float_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnError<float?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Float_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Decimal_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)null),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Decimal_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Decimal_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)3m),
+                OnNext(220, (decimal?)null),
+                OnNext(230, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)2.5m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Decimal_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnError<decimal?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Average_Nullable_Decimal_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Average()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+#if !NO_PERF
+#if !NO_THREAD
+        [Fact]
+        public void Average_InjectOverflow_Int32()
+        {
+            var xs = Observable.Return(42, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<int>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Int64()
+        {
+            var xs = Observable.Return(42L, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<long>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Double()
+        {
+            var xs = Observable.Return(42.0, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<double>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Single()
+        {
+            var xs = Observable.Return(42.0f, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<float>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Decimal()
+        {
+            var xs = Observable.Return(42.0m, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<decimal>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Int32_Nullable()
+        {
+            var xs = Observable.Return((int?)42, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<int?>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Int64_Nullable()
+        {
+            var xs = Observable.Return((long?)42L, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<long?>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Double_Nullable()
+        {
+            var xs = Observable.Return((double?)42.0, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<double?>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Single_Nullable()
+        {
+            var xs = Observable.Return((float?)42.0f, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<float?>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+
+        [Fact]
+        public void Average_InjectOverflow_Decimal_Nullable()
+        {
+            var xs = Observable.Return((decimal?)42.0m, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<decimal?>(xs, long.MaxValue).Average();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+#endif
+#if !CRIPPLED_REFLECTION || NETCOREAPP1_1 || NETCOREAPP1_0
+        class OverflowInjection<T> : IObservable<T>
+        {
+            private readonly IObservable<T> _source;
+            private readonly object _initialCount;
+
+            public OverflowInjection(IObservable<T> source, object initialCount)
+            {
+                _source = source;
+                _initialCount = initialCount;
+            }
+
+            public IDisposable Subscribe(IObserver<T> observer)
+            {
+                var f = observer.GetType().GetField("_count", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
+                f.SetValue(observer, _initialCount);
+
+                return _source.Subscribe(observer);
+            }
+        }
+#endif
+#endif
+
+        [Fact]
+        public void Average_Selector_Regular_Int32()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => (int)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 2.0),
+                OnCompleted<double>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Int64()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => (long)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 2.0),
+                OnCompleted<double>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Single()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => (float)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 2.0f),
+                OnCompleted<float>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Double()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => (double)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 2.0),
+                OnCompleted<double>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Decimal()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => (decimal)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 2.0m),
+                OnCompleted<decimal>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Int32_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => x == "fo" ? default(int?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (double?)2.0),
+                OnCompleted<double?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Int64_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => x == "fo" ? default(long?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (double?)2.0),
+                OnCompleted<double?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Single_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => x == "fo" ? default(float?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (float?)2.0),
+                OnCompleted<float?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Double_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => x == "fo" ? default(double?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (double?)2.0),
+                OnCompleted<double?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Average_Selector_Regular_Decimal_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "b"),
+                OnNext(220, "fo"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Average(x => x == "fo" ? default(decimal?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (decimal?)2.0),
+                OnCompleted<decimal?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+    }
+}

+ 192 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AwaitTest.cs

@@ -0,0 +1,192 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Threading;
+using System.Reactive.Subjects;
+using System.Reactive.Disposables;
+
+namespace ReactiveTests.Tests
+{
+    public class AwaitTest : ReactiveTest
+    {
+        [Fact]
+        public void Await_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetAwaiter<int>(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetAwaiter<int>(default(IConnectableObservable<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetAwaiter(Observable.Empty<int>()).OnCompleted(null));
+        }
+
+        [Fact]
+        public void Await()
+        {
+            SynchronizationContext.SetSynchronizationContext(null);
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(20, -1),
+                OnNext(150, 0),
+                OnNext(220, 1),
+                OnNext(290, 2),
+                OnNext(340, 3),
+                OnCompleted<int>(410)
+            );
+
+            var awaiter = default(AsyncSubject<int>);
+            var result = default(int);
+            var t = long.MaxValue;
+
+            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
+            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; result = awaiter.GetResult(); }));
+
+            scheduler.Start();
+
+            Assert.Equal(410, t);
+            Assert.Equal(3, result);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100)
+            );
+        }
+
+        [Fact]
+        public void Await_Connectable()
+        {
+            SynchronizationContext.SetSynchronizationContext(null);
+
+            var scheduler = new TestScheduler();
+
+            var s = default(long);
+
+            var xs = Observable.Create<int>(observer =>
+            {
+                s = scheduler.Clock;
+
+                return StableCompositeDisposable.Create(
+                    scheduler.ScheduleAbsolute(250, () => { observer.OnNext(42); }),
+                    scheduler.ScheduleAbsolute(260, () => { observer.OnCompleted(); })
+                );
+            });
+
+            var ys = xs.Publish();
+
+            var awaiter = default(AsyncSubject<int>);
+            var result = default(int);
+            var t = long.MaxValue;
+
+            scheduler.ScheduleAbsolute(100, () => awaiter = ys.GetAwaiter());
+            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; result = awaiter.GetResult(); }));
+
+            scheduler.Start();
+
+            Assert.Equal(100, s);
+            Assert.Equal(260, t);
+            Assert.Equal(42, result);
+        }
+
+        [Fact]
+        public void Await_Error()
+        {
+            SynchronizationContext.SetSynchronizationContext(null);
+
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(20, -1),
+                OnNext(150, 0),
+                OnNext(220, 1),
+                OnNext(290, 2),
+                OnNext(340, 3),
+                OnError<int>(410, ex)
+            );
+
+            var awaiter = default(AsyncSubject<int>);
+            var t = long.MaxValue;
+
+            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
+            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; ReactiveAssert.Throws(ex, () => awaiter.GetResult()); }));
+
+            scheduler.Start();
+
+            Assert.Equal(410, t);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100)
+            );
+        }
+
+        [Fact]
+        public void Await_Never()
+        {
+            SynchronizationContext.SetSynchronizationContext(null);
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(20, -1),
+                OnNext(150, 0),
+                OnNext(220, 1),
+                OnNext(290, 2),
+                OnNext(340, 3)
+            );
+
+            var awaiter = default(AsyncSubject<int>);
+            var hasValue = default(bool);
+            var t = long.MaxValue;
+
+            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
+            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; awaiter.GetResult(); hasValue = true; }));
+
+            scheduler.Start();
+
+            Assert.Equal(long.MaxValue, t);
+            Assert.False(hasValue);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100)
+            );
+        }
+
+        [Fact]
+        public void Await_Empty()
+        {
+            SynchronizationContext.SetSynchronizationContext(null);
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnCompleted<int>(300)
+            );
+
+            var awaiter = default(AsyncSubject<int>);
+            var t = long.MaxValue;
+
+            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
+            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; ReactiveAssert.Throws<InvalidOperationException>(() => awaiter.GetResult()); }));
+
+            scheduler.Start();
+
+            Assert.Equal(300, t);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100)
+            );
+        }
+    }
+}

+ 1318 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/BufferTest.cs

@@ -0,0 +1,1318 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class BufferTest : ReactiveTest
+    {
+        #region + Boundary +
+
+        [Fact]
+        public void Buffer_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, default(Func<IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, default(IObservable<int>), DummyFunc<int, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, DummyObservable<int>.Instance, default(Func<int, IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void Buffer_Closings_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var window = 1;
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(() => Observable.Timer(TimeSpan.FromTicks((window++) * 100), scheduler))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(300, b => b.SequenceEqual(new int[] { 3, 4 })),
+                OnNext<IList<int>>(500, b => b.SequenceEqual(new int[] { 5, 6, 7, 8, 9 })),
+                OnNext<IList<int>>(590, b => b.SequenceEqual(new int[] { 10 })),
+                OnCompleted<IList<int>>(590)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Closings_InnerSubscriptions()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var closings = new ITestableObservable<bool>[] {
+                scheduler.CreateHotObservable(
+                    OnNext(300, true),
+                    OnNext(350, false),
+                    OnCompleted<bool>(380)
+                ),
+                scheduler.CreateHotObservable(
+                    OnNext(400, true),
+                    OnNext(510, false),
+                    OnNext(620, false)
+                ),
+                scheduler.CreateHotObservable(
+                    OnCompleted<bool>(500)
+                ),
+                scheduler.CreateHotObservable(
+                    OnNext(600, true)
+                )
+            };
+
+            var window = 0;
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(() => closings[window++])
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(300, b => b.SequenceEqual(new int[] { 3, 4 })),
+                OnNext<IList<int>>(400, b => b.SequenceEqual(new int[] { 5, 6 })),
+                OnNext<IList<int>>(500, b => b.SequenceEqual(new int[] { 7, 8, 9 })),
+                OnNext<IList<int>>(590, b => b.SequenceEqual(new int[] { 10 })),
+                OnCompleted<IList<int>>(590)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+
+            closings[0].Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            closings[1].Subscriptions.AssertEqual(
+                Subscribe(300, 400)
+            );
+
+            closings[2].Subscriptions.AssertEqual(
+                Subscribe(400, 500)
+            );
+
+            closings[3].Subscriptions.AssertEqual(
+                Subscribe(500, 590)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Closings_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var window = 1;
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(() => Observable.Empty<int>().Delay(TimeSpan.FromTicks((window++) * 100), scheduler))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(300, l => l.SequenceEqual(new int[] { 3, 4 })),
+                OnNext<IList<int>>(500, l => l.SequenceEqual(new int[] { 5, 6, 7, 8, 9 })),
+                OnNext<IList<int>>(590, l => l.SequenceEqual(new int[] { 10 })),
+                OnCompleted<IList<int>>(590)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+        }
+
+
+        [Fact]
+        public void Buffer_Closings_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var window = 1;
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(() => Observable.Timer(TimeSpan.FromTicks((window++) * 100), scheduler)),
+                400
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(300, l => l.SequenceEqual(new int[] { 3, 4 }))
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Closings_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnError<int>(590, ex)
+            );
+
+            var window = 1;
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(() => Observable.Timer(TimeSpan.FromTicks((window++) * 100), scheduler))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(300, l => l.SequenceEqual(new int[] { 3, 4 })),
+                OnNext<IList<int>>(500, l => l.SequenceEqual(new int[] { 5, 6, 7, 8, 9 })),
+                OnError<IList<int>>(590, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Closings_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnError<int>(590, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer<int, int>(() => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<int>>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 200)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Closings_WindowClose_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnError<int>(590, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(() => Observable.Throw<int>(ex, scheduler))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<int>>(201, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 201)
+            );
+        }
+
+        [Fact]
+        public void Buffer_OpeningClosings_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(255, 50),
+                OnNext(330, 100),
+                OnNext(350, 50),
+                OnNext(400, 90),
+                OnCompleted<int>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(ys, x => Observable.Timer(TimeSpan.FromTicks(x), scheduler))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(305, b => b.SequenceEqual(new int[] { 4 })),
+                OnNext<IList<int>>(400, b => b.SequenceEqual(new int[] { })),
+                OnNext<IList<int>>(430, b => b.SequenceEqual(new int[] { 6, 7, 8 })),
+                OnNext<IList<int>>(490, b => b.SequenceEqual(new int[] { 7, 8, 9 })),
+                OnCompleted<IList<int>>(900)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#endif
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Boundaries_Simple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(255, true),
+                OnNext(330, true),
+                OnNext(350, true),
+                OnNext(400, true),
+                OnNext(500, true),
+                OnCompleted<bool>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(255, b => b.SequenceEqual(new int[] { 3 })),
+                OnNext<IList<int>>(330, b => b.SequenceEqual(new int[] { 4, 5 })),
+                OnNext<IList<int>>(350, b => b.SequenceEqual(new int[] { 6 })),
+                OnNext<IList<int>>(400, b => b.SequenceEqual(new int[] { })),
+                OnNext<IList<int>>(500, b => b.SequenceEqual(new int[] { 7, 8, 9 })),
+                OnNext<IList<int>>(590, b => b.SequenceEqual(new int[] { 10 })),
+                OnCompleted<IList<int>>(590)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 590)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Boundaries_OnCompletedBoundaries()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(255, true),
+                OnNext(330, true),
+                OnNext(350, true),
+                OnCompleted<bool>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(255, b => b.SequenceEqual(new int[] { 3 })),
+                OnNext<IList<int>>(330, b => b.SequenceEqual(new int[] { 4, 5 })),
+                OnNext<IList<int>>(350, b => b.SequenceEqual(new int[] { 6 })),
+                OnNext<IList<int>>(400, b => b.SequenceEqual(new int[] { })),
+                OnCompleted<IList<int>>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Boundaries_OnErrorSource()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(380, 7),
+                OnError<int>(400, ex)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(255, true),
+                OnNext(330, true),
+                OnNext(350, true),
+                OnCompleted<bool>(500)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(255, b => b.SequenceEqual(new int[] { 3 })),
+                OnNext<IList<int>>(330, b => b.SequenceEqual(new int[] { 4, 5 })),
+                OnNext<IList<int>>(350, b => b.SequenceEqual(new int[] { 6 })),
+                OnError<IList<int>>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Boundaries_OnErrorBoundaries()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(180, 2),
+                OnNext(250, 3),
+                OnNext(260, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(410, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnNext(550, 10),
+                OnCompleted<int>(590)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(255, true),
+                OnNext(330, true),
+                OnNext(350, true),
+                OnError<bool>(400, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(255, b => b.SequenceEqual(new int[] { 3 })),
+                OnNext<IList<int>>(330, b => b.SequenceEqual(new int[] { 4, 5 })),
+                OnNext<IList<int>>(350, b => b.SequenceEqual(new int[] { 6 })),
+                OnError<IList<int>>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        #endregion
+
+        #region + Count +
+
+        [Fact]
+        public void Buffer_Single_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(someObservable, 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(someObservable, -1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), 1, 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(someObservable, 1, 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(someObservable, 0, 1));
+        }
+
+        [Fact]
+        public void Buffer_Count_PartialWindow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new[] { 2, 3, 4, 5 })),
+                OnCompleted<IList<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Count_FullWindows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(220, l => l.SequenceEqual(new[] { 2, 3 })),
+                OnNext<IList<int>>(240, l => l.SequenceEqual(new[] { 4, 5 })),
+                OnCompleted<IList<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Count_FullAndPartialWindows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(230, l => l.SequenceEqual(new int[] { 2, 3, 4 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new int[] { 5 })),
+                OnCompleted<IList<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Count_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(5)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<int>>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Count_Skip_Less()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(3, 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(230, l => l.SequenceEqual(new int[] { 2, 3, 4 })),
+                OnNext<IList<int>>(240, l => l.SequenceEqual(new int[] { 3, 4, 5 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new int[] { 4, 5 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new int[] { 5 })),
+                OnCompleted<IList<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Buffer_Count_Skip_More()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(2, 3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(220, l => l.SequenceEqual(new int[] { 2, 3 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new int[] { 5 })),
+                OnCompleted<IList<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void BufferWithCount_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), 1, 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, 0, 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, 1, 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, 0));
+        }
+
+        [Fact]
+        public void BufferWithCount_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(3, 2).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, "2,3,4"),
+                OnNext(350, "4,5,6"),
+                OnNext(420, "6,7,8"),
+                OnNext(600, "8,9"),
+                OnCompleted<string>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithCount_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(3, 2).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray())), 370
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, "2,3,4"),
+                OnNext(350, "4,5,6")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 370)
+            );
+        }
+
+        [Fact]
+        public void BufferWithCount_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(3, 2).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, "2,3,4"),
+                OnNext(350, "4,5,6"),
+                OnNext(420, "6,7,8"),
+                OnError<string>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithCount_Default()
+        {
+            Observable.Range(1, 10).Buffer(3).Skip(1).First().AssertEqual(4, 5, 6);
+            Observable.Range(1, 10).Buffer(3, 2).Skip(1).First().AssertEqual(3, 4, 5);
+        }
+
+        #endregion
+
+        #region + Time +
+
+        [Fact]
+        public void Buffer_Time_ArgumentChecking()
+        {
+            var scheduler = new TestScheduler();
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.Zero));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(someObservable, TimeSpan.Zero, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.Zero, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.Zero, TimeSpan.Zero));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(someObservable, TimeSpan.Zero, TimeSpan.Zero, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.Zero, TimeSpan.Zero, scheduler));
+        }
+
+        [Fact]
+        public void BufferWithTime_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.FromTicks(1), TimeSpan.FromTicks(1), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(-1), TimeSpan.FromTicks(1), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), TimeSpan.FromTicks(-1), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), TimeSpan.FromTicks(1), null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.FromTicks(1), TimeSpan.FromTicks(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(-1), TimeSpan.FromTicks(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), TimeSpan.FromTicks(-1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.FromTicks(1), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(-1), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.FromTicks(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(-1)));
+        }
+
+        [Fact]
+        public void BufferWithTime_Basic1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(100), TimeSpan.FromTicks(70), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, "2,3,4"),
+                OnNext(370, "4,5,6"),
+                OnNext(440, "6,7,8"),
+                OnNext(510, "8,9"),
+                OnNext(580, ""),
+                OnNext(600, ""),
+                OnCompleted<string>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTime_Basic2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(70), TimeSpan.FromTicks(100), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(270, "2,3"),
+                OnNext(370, "5,6"),
+                OnNext(470, "8,9"),
+                OnNext(570, ""),
+                OnCompleted<string>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTime_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(100), TimeSpan.FromTicks(70), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, "2,3,4"),
+                OnNext(370, "4,5,6"),
+                OnNext(440, "6,7,8"),
+                OnNext(510, "8,9"),
+                OnNext(580, ""),
+                OnError<string>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTime_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(100), TimeSpan.FromTicks(70), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray())),
+                370
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, "2,3,4")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 370)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTime_Basic_Same()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(100), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, "2,3,4"),
+                OnNext(400, "5,6,7"),
+                OnNext(500, "8,9"),
+                OnNext(600, ""),
+                OnCompleted<string>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTime_Basic_Same_Periodic()
+        {
+            var scheduler = new PeriodicTestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(100), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, "2,3,4"),
+                OnNext(400, "5,6,7"),
+                OnNext(500, "8,9"),
+                OnNext(600, ""),
+                OnCompleted<string>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 600) { 300, 400, 500 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void BufferWithTime_Basic_Same_Periodic_Error()
+        {
+            var ex = new Exception();
+
+            var scheduler = new PeriodicTestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnError<int>(480, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(100), scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, "2,3,4"),
+                OnNext(400, "5,6,7"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 480) { 300, 400 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void BufferWithTime_Default()
+        {
+            Observable.Range(0, 10).Buffer(TimeSpan.FromDays(1), TimeSpan.FromDays(1)).First().AssertEqual(Enumerable.Range(0, 10));
+            Observable.Range(0, 10).Buffer(TimeSpan.FromDays(1)).First().AssertEqual(Enumerable.Range(0, 10));
+        }
+
+        [Fact]
+        public void BufferWithTimeOrCount_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.FromTicks(1), 1, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(-1), 1, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), 0, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), 1, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Buffer(default(IObservable<int>), TimeSpan.FromTicks(1), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(-1), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Buffer(DummyObservable<int>.Instance, TimeSpan.FromTicks(1), 0));
+        }
+
+        [Fact]
+        public void BufferWithTimeOrCount_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(370, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(70), 3, scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, "1,2,3"),
+                OnNext(310, "4"),
+                OnNext(370, "5,6,7"),
+                OnNext(440, "8"),
+                OnNext(510, "9"),
+                OnNext(580, ""),
+                OnNext(600, ""),
+                OnCompleted<string>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTimeOrCount_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(370, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(70), 3, scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray()))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, "1,2,3"),
+                OnNext(310, "4"),
+                OnNext(370, "5,6,7"),
+                OnNext(440, "8"),
+                OnNext(510, "9"),
+                OnNext(580, ""),
+                OnError<string>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTimeOrCount_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(280, 4),
+                OnNext(320, 5),
+                OnNext(350, 6),
+                OnNext(370, 7),
+                OnNext(420, 8),
+                OnNext(470, 9),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Buffer(TimeSpan.FromTicks(70), 3, scheduler).Select(x => string.Join(",", x.Select(xx => xx.ToString()).ToArray())),
+                370
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, "1,2,3"),
+                OnNext(310, "4"),
+                OnNext(370, "5,6,7")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 370)
+            );
+        }
+
+        [Fact]
+        public void BufferWithTimeOrCount_Default()
+        {
+            Observable.Range(1, 10, DefaultScheduler.Instance).Buffer(TimeSpan.FromDays(1), 3).Skip(1).First().AssertEqual(4, 5, 6);
+        }
+
+        #endregion
+
+    }
+}

+ 466 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CaseTest.cs

@@ -0,0 +1,466 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+using static System.Reactive.Linq.Observable;
+
+namespace ReactiveTests.Tests
+{
+    public class CaseTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Case_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case(null, new Dictionary<int, IObservable<int>>(), DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case(DummyFunc<int>.Instance, null, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case(DummyFunc<int>.Instance, new Dictionary<int, IObservable<int>>(), default(IObservable<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case(null, new Dictionary<int, IObservable<int>>(), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case<int, int>(DummyFunc<int>.Instance, null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case(DummyFunc<int>.Instance, new Dictionary<int, IObservable<int>>(), default(IScheduler)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case(null, new Dictionary<int, IObservable<int>>()));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Case<int, int>(DummyFunc<int>.Instance, null));
+        }
+
+        [Fact]
+        public void Case_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var zs = scheduler.CreateHotObservable(
+                OnNext(230, 21),
+                OnNext(240, 22),
+                OnNext(290, 23),
+                OnCompleted<int>(320)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 1, map, zs));
+
+            results.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+
+            zs.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Case_Two()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var zs = scheduler.CreateHotObservable(
+                OnNext(230, 21),
+                OnNext(240, 22),
+                OnNext(290, 23),
+                OnCompleted<int>(320)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 2, map, zs));
+
+            results.Messages.AssertEqual(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            zs.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Case_Three()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var zs = scheduler.CreateHotObservable(
+                OnNext(230, 21),
+                OnNext(240, 22),
+                OnNext(290, 23),
+                OnCompleted<int>(320)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 3, map, zs));
+
+            results.Messages.AssertEqual(
+                OnNext(230, 21),
+                OnNext(240, 22),
+                OnNext(290, 23),
+                OnCompleted<int>(320)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(200, 320)
+            );
+        }
+
+        [Fact]
+        public void Case_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var zs = scheduler.CreateHotObservable(
+                OnNext(230, 21),
+                OnNext(240, 22),
+                OnNext(290, 23),
+                OnCompleted<int>(320)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.Case(() => Throw<int>(ex), map, zs));
+
+            results.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+
+            zs.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void CaseWithDefault_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 1, map, scheduler));
+
+            results.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void CaseWithDefault_Two()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 2, map, scheduler));
+
+            results.Messages.AssertEqual(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void CaseWithDefault_Three()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 3, map, scheduler));
+
+            results.Messages.AssertEqual(
+                OnCompleted<int>(201)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void CaseWithDefault_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.Case(() => Throw<int>(ex), map, scheduler));
+
+            results.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void CaseWithDefault_CheckDefault()
+        {
+            Observable.Case(() => 1, new Dictionary<int, IObservable<int>>(), DefaultScheduler.Instance)
+                .AssertEqual(Observable.Case(() => 1, new Dictionary<int, IObservable<int>>()));
+        }
+
+        [Fact]
+        public void Case_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnError<int>(300, ex)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, 11),
+                OnNext(250, 12),
+                OnNext(280, 13),
+                OnCompleted<int>(310)
+            );
+
+            var map = new Dictionary<int, IObservable<int>>
+            {
+                { 1, xs },
+                { 2, ys }
+            };
+
+            var results = scheduler.Start(() => Observable.Case(() => 1, map, scheduler));
+
+            results.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnNext(270, 3),
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        static T Throw<T>(Exception ex)
+        {
+            throw ex;
+        }
+    }
+}

+ 232 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CastTest.cs

@@ -0,0 +1,232 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class CastTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Cast_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Cast<bool>(default(IObservable<object>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Cast<bool>(DummyObservable<object>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Cast_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new D(1)),
+                OnNext<object>(240, new B(2)),
+                OnNext<object>(270, new D(3)),
+                OnCompleted<object>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Cast<B>()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(220, new D(1)),
+                OnNext<B>(240, new B(2)),
+                OnNext<B>(270, new D(3)),
+                OnCompleted<B>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void Cast_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new D(1)),
+                OnNext<object>(240, new B(2)),
+                OnNext<object>(270, new D(3)),
+                OnError<object>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Cast<B>()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(220, new D(1)),
+                OnNext<B>(240, new B(2)),
+                OnNext<B>(270, new D(3)),
+                OnError<B>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void Cast_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new D(1)),
+                OnNext<object>(240, new B(2)),
+                OnNext<object>(270, new D(3)),
+                OnCompleted<object>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Cast<B>(),
+                250
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(220, new D(1)),
+                OnNext<B>(240, new B(2))
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Cast_NotValid()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new D(1)),
+                OnNext<object>(240, new B(2)),
+                OnNext<object>(250, new A(-1)),
+                OnNext<object>(270, new D(3)),
+                OnCompleted<object>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Cast<B>()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(220, new D(1)),
+                OnNext<B>(240, new B(2)),
+                OnError<B>(250, e => e is InvalidCastException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+    }
+
+    class A : IEquatable<A>
+    {
+        int id;
+
+        public A(int id)
+        {
+            this.id = id;
+        }
+
+        public bool Equals(A other)
+        {
+            if (other == null)
+                return false;
+            return id == other.id && GetType().Equals(other.GetType());
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as A);
+        }
+
+        public override int GetHashCode()
+        {
+            return id;
+        }
+    }
+
+    class B : A
+    {
+        public B(int id)
+            : base(id)
+        {
+        }
+    }
+
+    class C : A
+    {
+        public C(int id)
+            : base(id)
+        {
+        }
+    }
+
+    class D : B
+    {
+        public D(int id)
+            : base(id)
+        {
+        }
+    }
+
+    class E : IEquatable<E>
+    {
+        int id;
+
+        public E(int id)
+        {
+            this.id = id;
+        }
+
+        public bool Equals(E other)
+        {
+            if (other == null)
+                return false;
+            return id == other.id && GetType().Equals(other.GetType());
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as E);
+        }
+
+        public override int GetHashCode()
+        {
+            return id;
+        }
+    }
+}

+ 1008 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CatchTest.cs

@@ -0,0 +1,1008 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class CatchTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Catch_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Catch<int>((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Catch<int>((IEnumerable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Catch<int>(DummyObservable<int>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Catch<int>((IObservable<int>)null, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Catch<int, Exception>(null, _ => DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Catch<int, Exception>(DummyObservable<int>.Instance, null));
+        }
+
+        [Fact]
+        public void Catch_IEofIO_GetEnumeratorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xss = new RogueEnumerable<IObservable<int>>(ex);
+
+            var res = scheduler.Start(() =>
+                Observable.Catch(xss)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void Catch_IEofIO()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs1 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnError<int>(40, new Exception())
+            );
+
+            var xs2 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 4),
+                OnNext(20, 5),
+                OnError<int>(30, new Exception())
+            );
+
+            var xs3 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 6),
+                OnNext(20, 7),
+                OnNext(30, 8),
+                OnNext(40, 9),
+                OnCompleted<int>(50)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Catch(new[] { xs1, xs2, xs3 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(250, 4),
+                OnNext(260, 5),
+                OnNext(280, 6),
+                OnNext(290, 7),
+                OnNext(300, 8),
+                OnNext(310, 9),
+                OnCompleted<int>(320)
+            );
+
+            xs1.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+
+            xs2.Subscriptions.AssertEqual(
+                Subscribe(240, 270)
+            );
+
+            xs3.Subscriptions.AssertEqual(
+                Subscribe(270, 320)
+            );
+        }
+
+        [Fact]
+        public void Catch_NoErrors()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(230)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Catch_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Catch_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Catch_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Catch_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Catch_Error_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 1000)
+            );
+        }
+
+        [Fact]
+        public void Catch_Error_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, new Exception())
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Catch(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnError<int>(250, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Catch_Multiple()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(215, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(220, 3),
+                OnError<int>(225, ex)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(230, 4),
+                OnCompleted<int>(235)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Catch(o1, o2, o3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(235)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(215, 225)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(225, 235)
+            );
+        }
+
+        [Fact]
+        public void Catch_ErrorSpecific_Caught()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new ArgumentException("x");
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var handlerCalled = default(long?);
+
+            var res = scheduler.Start(() =>
+                o1.Catch((ArgumentException ex_) => { handlerCalled = scheduler.Clock; return o2; })
+            );
+
+            Assert.Equal(230, handlerCalled);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Catch_ErrorSpecific_Uncaught()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new InvalidOperationException("x");
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var handlerCalled = default(long?);
+
+            var res = scheduler.Start(() =>
+                o1.Catch((ArgumentException ex_) => { handlerCalled = scheduler.Clock; return o2; })
+            );
+
+            Assert.Equal(null, handlerCalled);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Catch_HandlerThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new ArgumentException("x");
+            var ex2 = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex1)
+            );
+
+            var handlerCalled = default(long?);
+
+            var res = scheduler.Start(() =>
+                o1.Catch((ArgumentException ex_) => { handlerCalled = scheduler.Clock; throw ex2; })
+            );
+
+            Assert.Equal(230, handlerCalled);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, ex2)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Catch_Nested_OuterCatches()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new ArgumentException("x");
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(215, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(220, 3),
+                OnCompleted<int>(225)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(220, 4), //!
+                OnCompleted<int>(225)
+            );
+
+            var firstHandlerCalled = default(long?);
+            var secondHandlerCalled = default(long?);
+
+            var res = scheduler.Start(() =>
+                o1
+                .Catch((InvalidOperationException ex_) => { firstHandlerCalled = scheduler.Clock; return o2; })
+                .Catch((ArgumentException ex_) => { secondHandlerCalled = scheduler.Clock; return o3; })
+            );
+
+            Assert.Equal(null, firstHandlerCalled);
+            Assert.Equal(215, secondHandlerCalled);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 4),
+                OnCompleted<int>(225)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(215, 225)
+            );
+        }
+
+        [Fact]
+        public void Catch_Nested_InnerCatches()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new ArgumentException("x");
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(215, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(220, 3), //!
+                OnCompleted<int>(225)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnCompleted<int>(225)
+            );
+
+            var firstHandlerCalled = default(long?);
+            var secondHandlerCalled = default(long?);
+
+            var res = scheduler.Start(() =>
+                o1
+                .Catch((ArgumentException ex_) => { firstHandlerCalled = scheduler.Clock; return o2; })
+                .Catch((InvalidOperationException ex_) => { secondHandlerCalled = scheduler.Clock; return o3; })
+            );
+
+            Assert.Equal(215, firstHandlerCalled);
+            Assert.Equal(null, secondHandlerCalled);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(225)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(215, 225)
+            );
+
+            o3.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Catch_ThrowFromNestedCatch()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new ArgumentException("x1");
+            var ex2 = new ArgumentException("x2");
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(215, ex1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(220, 3), //!
+                OnError<int>(225, ex2)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(230, 4),
+                OnCompleted<int>(235)
+            );
+
+            var firstHandlerCalled = default(long?);
+            var secondHandlerCalled = default(long?);
+
+            var res = scheduler.Start(() =>
+                o1
+                .Catch((ArgumentException ex_) => { firstHandlerCalled = scheduler.Clock; Assert.True(ex1 == ex_, "Expected ex1"); return o2; })
+                .Catch((ArgumentException ex_) => { secondHandlerCalled = scheduler.Clock; Assert.True(ex2 == ex_, "Expected ex2"); return o3; })
+            );
+
+            Assert.Equal(215, firstHandlerCalled);
+            Assert.Equal(225, secondHandlerCalled);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(235)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(215, 225)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(225, 235)
+            );
+        }
+
+        [Fact]
+        public void Catch_DefaultScheduler_Binary()
+        {
+            var evt = new ManualResetEvent(false);
+
+            int res = 0;
+            Observable.Return(1).Catch(Observable.Return(2)).Subscribe(x =>
+            {
+                res = x;
+                evt.Set();
+            });
+
+            evt.WaitOne();
+            Assert.Equal(1, res);
+        }
+
+        [Fact]
+        public void Catch_DefaultScheduler_Nary()
+        {
+            var evt = new ManualResetEvent(false);
+
+            int res = 0;
+            Observable.Catch(Observable.Return(1), Observable.Return(2), Observable.Return(3)).Subscribe(x =>
+            {
+                res = x;
+                evt.Set();
+            });
+
+            evt.WaitOne();
+            Assert.Equal(1, res);
+        }
+
+        [Fact]
+        public void Catch_DefaultScheduler_NaryEnumerable()
+        {
+            var evt = new ManualResetEvent(false);
+
+            IEnumerable<IObservable<int>> sources = new[] { Observable.Return(1), Observable.Return(2), Observable.Return(3) };
+
+            int res = 0;
+            Observable.Catch(sources).Subscribe(x =>
+            {
+                res = x;
+                evt.Set();
+            });
+
+            evt.WaitOne();
+            Assert.Equal(1, res);
+        }
+
+        [Fact]
+        public void Catch_EmptyIterator()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Catch<int>((IEnumerable<IObservable<int>>)new IObservable<int>[0])
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(200)
+            );
+        }
+
+        [Fact]
+        public void Catch_IteratorThrows()
+        {
+            var scheduler = new TestScheduler();
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Catch<int>(Catch_IteratorThrows_Source(ex, true))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        private IEnumerable<IObservable<int>> Catch_IteratorThrows_Source(Exception ex, bool b)
+        {
+            if (b)
+                throw ex;
+            else
+                yield break;
+        }
+
+        [Fact]
+        public void Catch_EnumerableThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(225)
+            );
+
+            var ex = new Exception();
+            var xss = new MockEnumerable<IObservable<int>>(scheduler, GetObservablesForCatchThrow(o, ex));
+
+            var res = scheduler.Start(() =>
+                xss.Catch()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(225)
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        private IEnumerable<IObservable<int>> GetObservablesForCatchThrow(IObservable<int> first, Exception ex)
+        {
+            yield return first;
+            throw ex;
+        }
+
+        [Fact]
+        public void Catch_EnumerableTiming()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2), // !
+                OnNext(220, 3), // !
+                OnError<int>(230, new Exception())
+            );
+
+            var o2 = scheduler.CreateColdObservable(
+                OnNext(50, 4),  // !
+                OnNext(60, 5),  // !
+                OnNext(70, 6),  // !
+                OnError<int>(80, new Exception())
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(200, 2),
+                OnNext(210, 3),
+                OnNext(220, 4),
+                OnNext(230, 5),
+                OnNext(270, 6),
+                OnNext(320, 7), // !
+                OnNext(330, 8), // !
+                OnCompleted<int>(340)
+            );
+
+            var xss = new MockEnumerable<ITestableObservable<int>>(scheduler, new[] { o1, o2, o3, o2 });
+
+            var res = scheduler.Start(() =>
+                xss.Select(xs => (IObservable<int>)xs).Catch()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 5),
+                OnNext(300, 6),
+                OnNext(320, 7),
+                OnNext(330, 8),
+                OnCompleted<int>(340)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 310)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(310, 340)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 340)
+            );
+        }
+
+        [Fact]
+        public void Catch_Enumerable_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, new Exception())
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(200, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(320, 6),
+                OnNext(330, 7),
+                OnCompleted<int>(340)
+            );
+
+            var xss = new MockEnumerable<ITestableObservable<int>>(scheduler, new[] { o1, o2 });
+
+            var res = scheduler.Start(() =>
+                xss.Select(xs => (IObservable<int>)xs).Catch(),
+                300
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnNext(270, 5)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+#if !NO_PERF
+        [Fact]
+        public void Catch_TailRecursive1()
+        {
+            var create = 0L;
+            var start = 200L;
+            var end = 1000L;
+
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateColdObservable<int>(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnError<int>(40, new Exception())
+            );
+
+            var f = default(Func<IObservable<int>>);
+            f = () => Observable.Defer(() => o.Catch(f()));
+
+            var res = scheduler.Start(() => f(), create, start, end);
+
+            var expected = new List<Recorded<Notification<int>>>();
+
+            var t = start;
+            while (t <= end)
+            {
+                var n = (t - start) / 10;
+                if (n % 4 != 0)
+                {
+                    expected.Add(OnNext(t, (int)(n % 4)));
+                }
+
+                t += 10;
+            }
+
+            res.Messages.AssertEqual(expected);
+        }
+
+#if HAS_STACKTRACE && !NO_THREAD
+        [Fact]
+        public void Catch_TailRecursive2()
+        {
+            var f = default(Func<int, IObservable<int>>);
+            f = x => Observable.Defer(() => Observable.Throw<int>(new Exception(), ThreadPoolScheduler.Instance).StartWith(x).Catch(f(x + 1)));
+
+            var lst = new List<int>();
+            f(0).Select(x => new StackTrace().FrameCount).Take(10).ForEach(lst.Add);
+
+            Assert.True(lst.Last() - lst.First() < 10);
+        }
+#endif
+
+        [Fact]
+        public void Catch_TailRecursive3()
+        {
+            var ex = new Exception();
+
+            var res =
+                Observable.Catch(
+                    Observable.Defer(() =>
+                    {
+                        if (ex != null)
+                        {
+                            throw ex;
+                        }
+
+                        return Observable.Return(-2);
+                    }),
+                    Observable.Defer(() =>
+                    {
+                        if (ex != null)
+                        {
+                            throw ex;
+                        }
+
+                        return Observable.Return(-1);
+                    }),
+                    Observable.Return(42)
+                );
+
+            Assert.Equal(42, res.Wait());
+        }
+#endif
+
+    }
+}

+ 187 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ChunkifyTest.cs

@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class ChunkifyTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Chunkify_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Chunkify(default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void Chunkify_Regular1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnNext(700, 7),
+                OnNext(800, 8),
+                OnCompleted<int>(900)
+            );
+
+            var ys = xs.Chunkify();
+            var e = default(IEnumerator<IList<int>>);
+
+            var res = new List<IList<int>>();
+
+            var log = new Action(() =>
+            {
+                Assert.True(e.MoveNext());
+                res.Add(e.Current);
+            });
+
+            scheduler.ScheduleAbsolute(250, () => { e = ys.GetEnumerator(); });
+            scheduler.ScheduleAbsolute(270, log);
+            scheduler.ScheduleAbsolute(310, log);
+            scheduler.ScheduleAbsolute(450, log);
+            scheduler.ScheduleAbsolute(470, log);
+            scheduler.ScheduleAbsolute(750, log);
+            scheduler.ScheduleAbsolute(850, log);
+            scheduler.ScheduleAbsolute(950, log);
+            scheduler.ScheduleAbsolute(980, () => Assert.False(e.MoveNext()));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 900)
+            );
+
+            Assert.Equal(7, res.Count);
+            Assert.True(res[0].SequenceEqual(new int[] { }));
+            Assert.True(res[1].SequenceEqual(new int[] { 3 }));
+            Assert.True(res[2].SequenceEqual(new int[] { 4 }));
+            Assert.True(res[3].SequenceEqual(new int[] { }));
+            Assert.True(res[4].SequenceEqual(new int[] { 5, 6, 7 }));
+            Assert.True(res[5].SequenceEqual(new int[] { 8 }));
+            Assert.True(res[6].SequenceEqual(new int[] { }));
+        }
+
+        [Fact]
+        public void Chunkify_Regular2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnNext(700, 7),
+                OnNext(800, 8),
+                OnCompleted<int>(900)
+            );
+
+            var ys = xs.Chunkify();
+            var e = default(IEnumerator<IList<int>>);
+
+            var res = new List<IList<int>>();
+
+            var log = new Action(() =>
+            {
+                Assert.True(e.MoveNext());
+                res.Add(e.Current);
+            });
+
+            scheduler.ScheduleAbsolute(250, () => { e = ys.GetEnumerator(); });
+            scheduler.ScheduleAbsolute(550, log);
+            scheduler.ScheduleAbsolute(950, log);
+            scheduler.ScheduleAbsolute(980, () => Assert.False(e.MoveNext()));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 900)
+            );
+
+            Assert.Equal(2, res.Count);
+            Assert.True(res[0].SequenceEqual(new int[] { 3, 4, 5 }));
+            Assert.True(res[1].SequenceEqual(new int[] { 6, 7, 8 }));
+        }
+
+        [Fact]
+        public void Chunkify_Error()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnError<int>(700, ex)
+            );
+
+            var ys = xs.Chunkify();
+            var e = default(IEnumerator<IList<int>>);
+
+            var res = new List<IList<int>>();
+
+            var log = new Action(() =>
+            {
+                Assert.True(e.MoveNext());
+                res.Add(e.Current);
+            });
+
+            scheduler.ScheduleAbsolute(250, () => { e = ys.GetEnumerator(); });
+            scheduler.ScheduleAbsolute(270, log);
+            scheduler.ScheduleAbsolute(310, log);
+            scheduler.ScheduleAbsolute(450, log);
+            scheduler.ScheduleAbsolute(470, log);
+            scheduler.ScheduleAbsolute(750, () =>
+            {
+                try
+                {
+                    e.MoveNext();
+                    Assert.True(false);
+                }
+                catch (Exception error)
+                {
+                    Assert.Same(ex, error);
+                }
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 700)
+            );
+
+            Assert.Equal(4, res.Count);
+            Assert.True(res[0].SequenceEqual(new int[] { }));
+            Assert.True(res[1].SequenceEqual(new int[] { 3 }));
+            Assert.True(res[2].SequenceEqual(new int[] { 4 }));
+            Assert.True(res[3].SequenceEqual(new int[] { }));
+        }
+
+    }
+}

+ 299 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CollectTest.cs

@@ -0,0 +1,299 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class CollectTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Collect_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(default(IObservable<int>), () => 0, (x, y) => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(someObservable, default(Func<int>), (x, y) => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(someObservable, () => 0, default(Func<int, int, int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(default(IObservable<int>), () => 0, (x, y) => x, x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(someObservable, default(Func<int>), (x, y) => x, x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(someObservable, () => 0, default(Func<int, int, int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Collect(someObservable, () => 0, (x, y) => x, default(Func<int, int>)));
+        }
+
+        [Fact]
+        public void Collect_Regular1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnNext(700, 7),
+                OnNext(800, 8),
+                OnCompleted<int>(900)
+            );
+
+            var ys = xs.Collect(() => 0, (x, y) => x + y);
+            var e = default(IEnumerator<int>);
+
+            var res = new List<int>();
+
+            var log = new Action(() =>
+            {
+                Assert.True(e.MoveNext());
+                res.Add(e.Current);
+            });
+
+            scheduler.ScheduleAbsolute(250, () => { e = ys.GetEnumerator(); });
+            scheduler.ScheduleAbsolute(270, log);
+            scheduler.ScheduleAbsolute(310, log);
+            scheduler.ScheduleAbsolute(450, log);
+            scheduler.ScheduleAbsolute(470, log);
+            scheduler.ScheduleAbsolute(750, log);
+            scheduler.ScheduleAbsolute(850, log);
+            scheduler.ScheduleAbsolute(950, log);
+            scheduler.ScheduleAbsolute(980, () => Assert.False(e.MoveNext()));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 900)
+            );
+
+            Assert.Equal(7, res.Count);
+            Assert.Equal(res[0], new int[] { }.Sum());
+            Assert.Equal(res[1], new int[] { 3 }.Sum());
+            Assert.Equal(res[2], new int[] { 4 }.Sum());
+            Assert.Equal(res[3], new int[] { }.Sum());
+            Assert.Equal(res[4], new int[] { 5, 6, 7 }.Sum());
+            Assert.Equal(res[5], new int[] { 8 }.Sum());
+            Assert.Equal(res[6], new int[] { }.Sum());
+        }
+
+        [Fact]
+        public void Collect_Regular2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnNext(700, 7),
+                OnNext(800, 8),
+                OnCompleted<int>(900)
+            );
+
+            var ys = xs.Collect(() => 0, (x, y) => x + y);
+            var e = default(IEnumerator<int>);
+
+            var res = new List<int>();
+
+            var log = new Action(() =>
+            {
+                Assert.True(e.MoveNext());
+                res.Add(e.Current);
+            });
+
+            scheduler.ScheduleAbsolute(250, () => { e = ys.GetEnumerator(); });
+            scheduler.ScheduleAbsolute(550, log);
+            scheduler.ScheduleAbsolute(950, log);
+            scheduler.ScheduleAbsolute(980, () => Assert.False(e.MoveNext()));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 900)
+            );
+
+            Assert.Equal(2, res.Count);
+            Assert.Equal(res[0], new int[] { 3, 4, 5 }.Sum());
+            Assert.Equal(res[1], new int[] { 6, 7, 8 }.Sum());
+        }
+
+        [Fact]
+        public void Collect_InitialCollectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(500)
+            );
+
+            var ex = new Exception();
+            var ys = xs.Collect<int, int>(() => { throw ex; }, (x, y) => x + y);
+
+            var ex_ = default(Exception);
+
+            scheduler.ScheduleAbsolute(250, () =>
+            {
+                try
+                {
+                    ys.GetEnumerator();
+                }
+                catch (Exception err)
+                {
+                    ex_ = err;
+                }
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            Assert.Same(ex_, ex);
+        }
+
+        [Fact]
+        public void Collect_SecondCollectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(500)
+            );
+
+            var ex = new Exception();
+            var n = 0;
+            var ys = xs.Collect<int, int>(() => { if (n++ == 0) return 0; else throw ex; }, (x, y) => x + y);
+            var e = default(IEnumerator<int>);
+
+            var ex_ = default(Exception);
+
+            scheduler.ScheduleAbsolute(250, () => e = ys.GetEnumerator());
+            scheduler.ScheduleAbsolute(350, () =>
+            {
+                try
+                {
+                    e.MoveNext();
+                }
+                catch (Exception err)
+                {
+                    ex_ = err;
+                }
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 350)
+            );
+
+            Assert.Same(ex_, ex);
+        }
+
+        [Fact]
+        public void Collect_NewCollectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(500)
+            );
+
+            var ex = new Exception();
+            var ys = xs.Collect<int, int>(() => 0, (x, y) => x + y, x => { throw ex; });
+            var e = default(IEnumerator<int>);
+
+            var ex_ = default(Exception);
+
+            scheduler.ScheduleAbsolute(250, () => e = ys.GetEnumerator());
+            scheduler.ScheduleAbsolute(350, () =>
+            {
+                try
+                {
+                    e.MoveNext();
+                }
+                catch (Exception err)
+                {
+                    ex_ = err;
+                }
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 350)
+            );
+
+            Assert.Same(ex_, ex);
+        }
+
+        [Fact]
+        public void Collect_MergeThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(500)
+            );
+
+            var ex = new Exception();
+            var ys = xs.Collect<int, int>(() => 0, (x, y) => { throw ex; });
+            var e = default(IEnumerator<int>);
+
+            var ex_ = default(Exception);
+
+            scheduler.ScheduleAbsolute(250, () => { e = ys.GetEnumerator(); });
+            scheduler.ScheduleAbsolute(350, () =>
+            {
+                try
+                {
+                    e.MoveNext();
+                }
+                catch (Exception err)
+                {
+                    ex_ = err;
+                }
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(250, 300)
+            );
+
+            Assert.Same(ex_, ex);
+        }
+
+    }
+}

+ 3815 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CombineLatestTest.cs

@@ -0,0 +1,3815 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class CombineLatestTest : ReactiveTest
+    {
+
+        #region ArgumentChecking
+
+        [Fact]
+        public void CombineLatest_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest<int, int, int>(DummyObservable<int>.Instance, DummyObservable<int>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest<int, int, int>(null, DummyObservable<int>.Instance, (_, __) => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest<int, int, int>(DummyObservable<int>.Instance, null, (_, __) => 0));
+        }
+
+        [Fact]
+        public void CombineLatest_ArgumentCheckingHighArity()
+        {
+            var xs = DummyObservable<int>.Instance;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, (_0, _1) => _0 + _1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), (_0, _1) => _0 + _1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(Func<int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, (_0, _1, _2) => _0 + _1 + _2));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, (_0, _1, _2) => _0 + _1 + _2));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), (_0, _1, _2) => _0 + _1 + _2));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(Func<int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3) => _0 + _1 + _2 + _3));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3) => _0 + _1 + _2 + _3));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3) => _0 + _1 + _2 + _3));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3) => _0 + _1 + _2 + _3));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(Func<int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4) => _0 + _1 + _2 + _3 + _4));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4) => _0 + _1 + _2 + _3 + _4));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4) => _0 + _1 + _2 + _3 + _4));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4) => _0 + _1 + _2 + _3 + _4));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4) => _0 + _1 + _2 + _3 + _4));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), xs, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(IObservable<int>), (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, xs, default(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>)));
+        }
+
+        #endregion
+
+        #region Never
+
+        [Fact]
+        public void CombineLatest_NeverN()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => xs.Sum())
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never2()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, (_0, _1) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never3()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, (_0, _1, _2) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never4()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, (_0, _1, _2, _3) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never5()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, (_0, _1, _2, _3, _4) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never6()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, (_0, _1, _2, _3, _4, _5) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never7()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, (_0, _1, _2, _3, _4, _5, _6) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never8()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, (_0, _1, _2, _3, _4, _5, _6, _7) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never9()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never10()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never11()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never12()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never13()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never14()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never15()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        [Fact]
+        public void CombineLatest_Never16()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+            var e15 = scheduler.CreateHotObservable(new[] { OnNext(150, 1) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => 42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 1000));
+        }
+
+        #endregion
+
+        #region Never/Empty
+
+        [Fact]
+        public void CombineLatest_NeverEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var n = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(210)
+            );
+
+            var res = scheduler.Start(() =>
+                n.CombineLatest(e, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_EmptyNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(210)
+            );
+
+            var n = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                e.CombineLatest(n, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        #endregion
+
+        #region Empty
+
+        [Fact]
+        public void CombineLatest_EmptyN()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => xs.Sum())
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty2()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, (_0, _1) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(220)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty3()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, (_0, _1, _2) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty4()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, (_0, _1, _2, _3) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(240)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty5()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, (_0, _1, _2, _3, _4) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty6()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, (_0, _1, _2, _3, _4, _5) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(260)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty7()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, (_0, _1, _2, _3, _4, _5, _6) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(270)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty8()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, (_0, _1, _2, _3, _4, _5, _6, _7) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(280)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty9()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(290)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty10()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(300)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty11()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(310)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty12()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(320)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty13()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(330)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty14()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(340)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty15()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(350)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        [Fact]
+        public void CombineLatest_Empty16()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(210) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(220) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(230) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(240) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+            var e15 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(360) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(360)
+            );
+
+            var i = 0;
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + (++i * 10)));
+        }
+
+        #endregion
+
+        #region Empty/Return
+
+        [Fact]
+        public void CombineLatest_EmptyReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(210)
+            );
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var res = scheduler.Start(() =>
+                e.CombineLatest(o, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(215)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ReturnEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(210)
+            );
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var res = scheduler.Start(() =>
+                o.CombineLatest(e, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(215)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+        }
+
+        #endregion
+
+        #region Never/Return
+
+        [Fact]
+        public void CombineLatest_NeverReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var n = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                n.CombineLatest(o, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            n.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ReturnNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var n = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                o.CombineLatest(n, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            n.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        #endregion
+
+        #region Return/Return
+
+        [Fact]
+        public void CombineLatest_ReturnReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(240)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2 + 3),
+                OnCompleted<int>(240)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        #endregion
+
+        #region Empty/Error
+
+        [Fact]
+        public void CombineLatest_EmptyError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ErrorEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var e = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var f = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                f.CombineLatest(e, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            e.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            f.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        #endregion
+
+        #region Return/Throw
+
+        [Fact]
+        public void CombineLatest_ReturnThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ThrowReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        #endregion
+
+        #region Throw/Throw
+
+        [Fact]
+        public void CombineLatest_ThrowThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+            var ex2 = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex2)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex1)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ErrorThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+            var ex2 = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, ex1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex2)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex1)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ThrowError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+            var ex2 = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, ex1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex2)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex1)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        #endregion
+
+        #region Never/Throw
+
+        [Fact]
+        public void CombineLatest_NeverThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ThrowNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        #endregion
+
+        #region Some/Throw
+
+        [Fact]
+        public void CombineLatest_SomeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ThrowSome()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        #endregion
+
+        #region ThrowAfterCompleted
+
+        [Fact]
+        public void CombineLatest_ThrowAfterCompleteLeft()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ThrowAfterCompleteRight()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.CombineLatest(o2, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        #endregion
+
+        #region Basics
+
+        [Fact]
+        public void CombineLatest_InterleavedWithTail()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnNext(230, 5),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2 + 3),
+                OnNext(225, 3 + 4),
+                OnNext(230, 4 + 5),
+                OnNext(235, 4 + 6),
+                OnNext(240, 4 + 7),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_Consecutive()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(235, 4 + 6),
+                OnNext(240, 4 + 7),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ConsecutiveEndWithErrorLeft()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_ConsecutiveEndWithErrorRight()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(250)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnError<int>(245, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest(o1, (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(235, 4 + 6),
+                OnNext(240, 4 + 7),
+                OnError<int>(245, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 245)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 245)
+            );
+        }
+
+        #endregion
+
+        #region SelectorThrows
+
+        [Fact]
+        public void CombineLatest_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(240)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                o2.CombineLatest<int, int, int>(o1, (x, y) => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrowsN()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<IList<int>, int> f = xs => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, f)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            var es = new[] { e0, e1, e2 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows2()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, (_0, _1) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            var es = new[] { e0, e1 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows3()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, (_0, _1, _2) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            var es = new[] { e0, e1, e2 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows4()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, (_0, _1, _2, _3) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(240, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows5()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, (_0, _1, _2, _3, _4) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows6()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, (_0, _1, _2, _3, _4, _5) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(260, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows7()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, (_0, _1, _2, _3, _4, _5, _6) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(270, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows8()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, (_0, _1, _2, _3, _4, _5, _6, _7) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(280, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows9()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(290, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows10()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows11()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(310, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows12()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnCompleted<int>(400) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(320, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows13()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnCompleted<int>(400) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnCompleted<int>(400) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(330, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows14()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnCompleted<int>(400) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnCompleted<int>(400) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnCompleted<int>(400) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(340, 14), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(340, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows15()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnCompleted<int>(400) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnCompleted<int>(400) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnCompleted<int>(400) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(340, 14), OnCompleted<int>(400) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(350, 15), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(350, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        [Fact]
+        public void CombineLatest_SelectorThrows16()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnCompleted<int>(400) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnCompleted<int>(400) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnCompleted<int>(400) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnCompleted<int>(400) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnCompleted<int>(400) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnCompleted<int>(400) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnCompleted<int>(400) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnCompleted<int>(400) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnCompleted<int>(400) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnCompleted<int>(400) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(340, 14), OnCompleted<int>(400) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(350, 15), OnCompleted<int>(400) });
+            var e15 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(360, 16), OnCompleted<int>(400) });
+
+            var ex = new Exception();
+            Func<int> f = () => { throw ex; };
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => f())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(360, ex)
+            );
+
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15 };
+            foreach (var e in es)
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 10 * es.Length));
+        }
+
+        #endregion
+
+        #region AllEmptyButOne
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombineN()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine2()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, (_0, _1) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine3()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, (_0, _1, _2) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine4()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, (_0, _1, _2, _3) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine5()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, (_0, _1, _2, _3, _4) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine6()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, (_0, _1, _2, _3, _4, _5) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine7()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, (_0, _1, _2, _3, _4, _5, _6) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine8()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, (_0, _1, _2, _3, _4, _5, _6, _7) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine9()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine10()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine11()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine12()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine13()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(360) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine14()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(360) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(370) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine15()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(360) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(370) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(380) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        [Fact]
+        public void CombineLatest_WillNeverBeAbleToCombine16()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(250) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(260) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(270) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(280) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(290) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(300) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(310) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(320) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(330) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(340) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(350) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(360) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(370) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(380) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnCompleted<int>(390) });
+            var e15 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(500, 2), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(500)
+            );
+
+            var i = 0;
+            var es = new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15 };
+            foreach (var e in es.Take(es.Length - 1))
+                e.Subscriptions.AssertEqual(Subscribe(200, 200 + 50 + (i++ * 10)));
+
+            es.Last().Subscriptions.AssertEqual(Subscribe(200, 500));
+        }
+
+        #endregion
+
+        #region Typical
+
+        [Fact]
+        public void CombineLatest_TypicalN()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 4), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 5), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 6), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => xs.Sum())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(230, 6),
+                OnNext(410, 9),
+                OnNext(420, 12),
+                OnNext(430, 15),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical2()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 3), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 4), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, (_0, _1) => _0 + _1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 3),
+                OnNext(410, 5),
+                OnNext(420, 7),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical3()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 4), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 5), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 6), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, (_0, _1, _2) => _0 + _1 + _2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(230, 6),
+                OnNext(410, 9),
+                OnNext(420, 12),
+                OnNext(430, 15),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical4()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 5), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 6), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 7), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 8), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, (_0, _1, _2, _3) => _0 + _1 + _2 + _3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, 10),
+                OnNext(410, 14),
+                OnNext(420, 18),
+                OnNext(430, 22),
+                OnNext(440, 26),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical5()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 6), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 7), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 8), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 9), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 10), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, (_0, _1, _2, _3, _4) => _0 + _1 + _2 + _3 + _4)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 15),
+                OnNext(410, 20),
+                OnNext(420, 25),
+                OnNext(430, 30),
+                OnNext(440, 35),
+                OnNext(450, 40),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical6()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 7), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 8), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 9), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 10), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 11), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 12), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, (_0, _1, _2, _3, _4, _5) => _0 + _1 + _2 + _3 + _4 + _5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 21),
+                OnNext(410, 27),
+                OnNext(420, 33),
+                OnNext(430, 39),
+                OnNext(440, 45),
+                OnNext(450, 51),
+                OnNext(460, 57),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical7()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 8), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 9), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 10), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 11), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 12), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 13), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 14), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, (_0, _1, _2, _3, _4, _5, _6) => _0 + _1 + _2 + _3 + _4 + _5 + _6)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(270, 28),
+                OnNext(410, 35),
+                OnNext(420, 42),
+                OnNext(430, 49),
+                OnNext(440, 56),
+                OnNext(450, 63),
+                OnNext(460, 70),
+                OnNext(470, 77),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical8()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 9), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 10), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 11), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 12), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 13), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 14), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 15), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 16), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, (_0, _1, _2, _3, _4, _5, _6, _7) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 36),
+                OnNext(410, 44),
+                OnNext(420, 52),
+                OnNext(430, 60),
+                OnNext(440, 68),
+                OnNext(450, 76),
+                OnNext(460, 84),
+                OnNext(470, 92),
+                OnNext(480, 100),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical9()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 10), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 11), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 12), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 13), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 14), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 15), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 16), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 17), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 18), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, (_0, _1, _2, _3, _4, _5, _6, _7, _8) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(290, 45),
+                OnNext(410, 54),
+                OnNext(420, 63),
+                OnNext(430, 72),
+                OnNext(440, 81),
+                OnNext(450, 90),
+                OnNext(460, 99),
+                OnNext(470, 108),
+                OnNext(480, 117),
+                OnNext(490, 126),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical10()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 11), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 12), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 13), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 14), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 15), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 16), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 17), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 18), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 19), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 20), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 55),
+                OnNext(410, 65),
+                OnNext(420, 75),
+                OnNext(430, 85),
+                OnNext(440, 95),
+                OnNext(450, 105),
+                OnNext(460, 115),
+                OnNext(470, 125),
+                OnNext(480, 135),
+                OnNext(490, 145),
+                OnNext(500, 155),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical11()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 12), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 13), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 14), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 15), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 16), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 17), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 18), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 19), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 20), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 21), OnCompleted<int>(800) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnNext(510, 22), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 66),
+                OnNext(410, 77),
+                OnNext(420, 88),
+                OnNext(430, 99),
+                OnNext(440, 110),
+                OnNext(450, 121),
+                OnNext(460, 132),
+                OnNext(470, 143),
+                OnNext(480, 154),
+                OnNext(490, 165),
+                OnNext(500, 176),
+                OnNext(510, 187),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical12()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 13), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 14), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 15), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 16), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 17), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 18), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 19), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 20), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 21), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 22), OnCompleted<int>(800) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnNext(510, 23), OnCompleted<int>(800) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnNext(520, 24), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(320, 78),
+                OnNext(410, 90),
+                OnNext(420, 102),
+                OnNext(430, 114),
+                OnNext(440, 126),
+                OnNext(450, 138),
+                OnNext(460, 150),
+                OnNext(470, 162),
+                OnNext(480, 174),
+                OnNext(490, 186),
+                OnNext(500, 198),
+                OnNext(510, 210),
+                OnNext(520, 222),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical13()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 14), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 15), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 16), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 17), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 18), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 19), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 20), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 21), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 22), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 23), OnCompleted<int>(800) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnNext(510, 24), OnCompleted<int>(800) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnNext(520, 25), OnCompleted<int>(800) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnNext(530, 26), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(330, 91),
+                OnNext(410, 104),
+                OnNext(420, 117),
+                OnNext(430, 130),
+                OnNext(440, 143),
+                OnNext(450, 156),
+                OnNext(460, 169),
+                OnNext(470, 182),
+                OnNext(480, 195),
+                OnNext(490, 208),
+                OnNext(500, 221),
+                OnNext(510, 234),
+                OnNext(520, 247),
+                OnNext(530, 260),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical14()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 15), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 16), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 17), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 18), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 19), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 20), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 21), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 22), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 23), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 24), OnCompleted<int>(800) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnNext(510, 25), OnCompleted<int>(800) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnNext(520, 26), OnCompleted<int>(800) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnNext(530, 27), OnCompleted<int>(800) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(340, 14), OnNext(540, 28), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(340, 105),
+                OnNext(410, 119),
+                OnNext(420, 133),
+                OnNext(430, 147),
+                OnNext(440, 161),
+                OnNext(450, 175),
+                OnNext(460, 189),
+                OnNext(470, 203),
+                OnNext(480, 217),
+                OnNext(490, 231),
+                OnNext(500, 245),
+                OnNext(510, 259),
+                OnNext(520, 273),
+                OnNext(530, 287),
+                OnNext(540, 301),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical15()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 16), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 17), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 18), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 19), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 20), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 21), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 22), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 23), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 24), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 25), OnCompleted<int>(800) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnNext(510, 26), OnCompleted<int>(800) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnNext(520, 27), OnCompleted<int>(800) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnNext(530, 28), OnCompleted<int>(800) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(340, 14), OnNext(540, 29), OnCompleted<int>(800) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(350, 15), OnNext(550, 30), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, 120),
+                OnNext(410, 135),
+                OnNext(420, 150),
+                OnNext(430, 165),
+                OnNext(440, 180),
+                OnNext(450, 195),
+                OnNext(460, 210),
+                OnNext(470, 225),
+                OnNext(480, 240),
+                OnNext(490, 255),
+                OnNext(500, 270),
+                OnNext(510, 285),
+                OnNext(520, 300),
+                OnNext(530, 315),
+                OnNext(540, 330),
+                OnNext(550, 345),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        [Fact]
+        public void CombineLatest_Typical16()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(410, 17), OnCompleted<int>(800) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(420, 18), OnCompleted<int>(800) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(430, 19), OnCompleted<int>(800) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnNext(440, 20), OnCompleted<int>(800) });
+            var e4 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(250, 5), OnNext(450, 21), OnCompleted<int>(800) });
+            var e5 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(260, 6), OnNext(460, 22), OnCompleted<int>(800) });
+            var e6 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(270, 7), OnNext(470, 23), OnCompleted<int>(800) });
+            var e7 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(280, 8), OnNext(480, 24), OnCompleted<int>(800) });
+            var e8 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(290, 9), OnNext(490, 25), OnCompleted<int>(800) });
+            var e9 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(300, 10), OnNext(500, 26), OnCompleted<int>(800) });
+            var e10 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(310, 11), OnNext(510, 27), OnCompleted<int>(800) });
+            var e11 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(320, 12), OnNext(520, 28), OnCompleted<int>(800) });
+            var e12 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(330, 13), OnNext(530, 29), OnCompleted<int>(800) });
+            var e13 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(340, 14), OnNext(540, 30), OnCompleted<int>(800) });
+            var e14 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(350, 15), OnNext(550, 31), OnCompleted<int>(800) });
+            var e15 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(360, 16), OnNext(560, 32), OnCompleted<int>(800) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) => _0 + _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 + _10 + _11 + _12 + _13 + _14 + _15)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(360, 136),
+                OnNext(410, 152),
+                OnNext(420, 168),
+                OnNext(430, 184),
+                OnNext(440, 200),
+                OnNext(450, 216),
+                OnNext(460, 232),
+                OnNext(470, 248),
+                OnNext(480, 264),
+                OnNext(490, 280),
+                OnNext(500, 296),
+                OnNext(510, 312),
+                OnNext(520, 328),
+                OnNext(530, 344),
+                OnNext(540, 360),
+                OnNext(550, 376),
+                OnNext(560, 392),
+                OnCompleted<int>(800)
+            );
+
+            foreach (var e in new[] { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15 })
+                e.Subscriptions.AssertEqual(Subscribe(200, 800));
+        }
+
+        #endregion
+
+        #region NAry
+
+        [Fact]
+        public void CombineLatest_List_Regular()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(240, 4), OnCompleted<int>(270) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(250, 5), OnCompleted<int>(280) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnCompleted<int>(290) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest<int>(new IObservable<int>[] { e0, e1, e2 }.AsEnumerable())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(230, l => l.SequenceEqual(new[] { 1, 2, 3 })),
+                OnNext<IList<int>>(240, l => l.SequenceEqual(new[] { 4, 2, 3 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new[] { 4, 5, 3 })),
+                OnNext<IList<int>>(260, l => l.SequenceEqual(new[] { 4, 5, 6 })),
+                OnCompleted<IList<int>>(290)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IEnumerable<IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IEnumerable<IObservable<int>>), _ => 42));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(new[] { Observable.Return(42) }, default(Func<IList<int>, string>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.CombineLatest(default(IObservable<int>[])));
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_Symmetric()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(250, 4), OnCompleted<int>(420) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(240, 5), OnCompleted<int>(410) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnCompleted<int>(400) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(230, l => l.SequenceEqual(new[] { 1, 2, 3 })),
+                OnNext<IList<int>>(240, l => l.SequenceEqual(new[] { 1, 5, 3 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new[] { 4, 5, 3 })),
+                OnNext<IList<int>>(260, l => l.SequenceEqual(new[] { 4, 5, 6 })),
+                OnCompleted<IList<int>>(420)
+            );
+
+            e0.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(200, 410)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_Symmetric_Selector()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(250, 4), OnCompleted<int>(420) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(240, 5), OnCompleted<int>(410) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnCompleted<int>(400) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => xs.Sum())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(230, new[] { 1, 2, 3 }.Sum()),
+                OnNext<int>(240, new[] { 1, 5, 3 }.Sum()),
+                OnNext<int>(250, new[] { 4, 5, 3 }.Sum()),
+                OnNext<int>(260, new[] { 4, 5, 6 }.Sum()),
+                OnCompleted<int>(420)
+            );
+
+            e0.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(200, 410)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_Asymmetric()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(250, 4), OnCompleted<int>(270) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(240, 5), OnNext(290, 7), OnNext(310, 9), OnCompleted<int>(410) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnNext(280, 8), OnCompleted<int>(300) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(230, l => l.SequenceEqual(new[] { 1, 2, 3 })),
+                OnNext<IList<int>>(240, l => l.SequenceEqual(new[] { 1, 5, 3 })),
+                OnNext<IList<int>>(250, l => l.SequenceEqual(new[] { 4, 5, 3 })),
+                OnNext<IList<int>>(260, l => l.SequenceEqual(new[] { 4, 5, 6 })),
+                OnNext<IList<int>>(280, l => l.SequenceEqual(new[] { 4, 5, 8 })),
+                OnNext<IList<int>>(290, l => l.SequenceEqual(new[] { 4, 7, 8 })),
+                OnNext<IList<int>>(310, l => l.SequenceEqual(new[] { 4, 9, 8 })),
+                OnCompleted<IList<int>>(410)
+            );
+
+            e0.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(200, 410)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_Asymmetric_Selector()
+        {
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnNext(250, 4), OnCompleted<int>(270) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(240, 5), OnNext(290, 7), OnNext(310, 9), OnCompleted<int>(410) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnNext(280, 8), OnCompleted<int>(300) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => xs.Sum())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(230, new[] { 1, 2, 3 }.Sum()),
+                OnNext<int>(240, new[] { 1, 5, 3 }.Sum()),
+                OnNext<int>(250, new[] { 4, 5, 3 }.Sum()),
+                OnNext<int>(260, new[] { 4, 5, 6 }.Sum()),
+                OnNext<int>(280, new[] { 4, 5, 8 }.Sum()),
+                OnNext<int>(290, new[] { 4, 7, 8 }.Sum()),
+                OnNext<int>(310, new[] { 4, 9, 8 }.Sum()),
+                OnCompleted<int>(410)
+            );
+
+            e0.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(200, 410)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_Error()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnError<int>(250, ex) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(240, 5), OnCompleted<int>(410) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnCompleted<int>(400) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<int>>(230, l => l.SequenceEqual(new[] { 1, 2, 3 })),
+                OnNext<IList<int>>(240, l => l.SequenceEqual(new[] { 1, 5, 3 })),
+                OnError<IList<int>>(250, ex)
+            );
+
+            e0.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void CombineLatest_NAry_Error_Selector()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnError<int>(250, ex) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnNext(240, 5), OnCompleted<int>(410) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(230, 3), OnNext(260, 6), OnCompleted<int>(400) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(new[] { e0, e1, e2 }, xs => xs.Sum())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(230, new[] { 1, 2, 3 }.Sum()),
+                OnNext<int>(240, new[] { 1, 5, 3 }.Sum()),
+                OnError<int>(250, ex)
+            );
+
+            e0.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        #endregion
+
+        #region AtLeastOneThrows
+
+        [Fact]
+        public void CombineLatest_AtLeastOneThrows4()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+            var e0 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(210, 1), OnCompleted<int>(400) });
+            var e1 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(220, 2), OnCompleted<int>(400) });
+            var e2 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnError<int>(230, ex) });
+            var e3 = scheduler.CreateHotObservable(new[] { OnNext(150, 1), OnNext(240, 4), OnCompleted<int>(400) });
+
+            var res = scheduler.Start(() =>
+                Observable.CombineLatest(e0, e1, e2, e3, (_0, _1, _2, _3) => 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            e0.Subscriptions.AssertEqual(Subscribe(200, 230));
+            e1.Subscriptions.AssertEqual(Subscribe(200, 230));
+            e2.Subscriptions.AssertEqual(Subscribe(200, 230));
+            e3.Subscriptions.AssertEqual(Subscribe(200, 230));
+        }
+
+        #endregion
+
+    }
+}

+ 954 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ConcatTest.cs

@@ -0,0 +1,954 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+using System.Diagnostics;
+
+namespace ReactiveTests.Tests
+{
+    public class ConcatTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Concat_ArgumentChecking()
+        {
+            var xs = DummyObservable<int>.Instance;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat(xs, (IObservable<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat((IObservable<int>)null, xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat((IEnumerable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat((IEnumerable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat(xs, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat(null, xs));
+        }
+
+        [Fact]
+        public void Concat_DefaultScheduler()
+        {
+            var evt = new ManualResetEvent(false);
+
+            int sum = 0;
+            Observable.Concat(Observable.Return(1), Observable.Return(2), Observable.Return(3)).Subscribe(n =>
+            {
+                sum += n;
+            }, () => evt.Set());
+
+            evt.WaitOne();
+
+            Assert.Equal(6, sum);
+        }
+
+        [Fact]
+        public void Concat_IEofIO_DefaultScheduler()
+        {
+            var evt = new ManualResetEvent(false);
+
+            IEnumerable<IObservable<int>> sources = new[] { Observable.Return(1), Observable.Return(2), Observable.Return(3) };
+
+            int sum = 0;
+            Observable.Concat(sources).Subscribe(n =>
+            {
+                sum += n;
+            }, () => evt.Set());
+
+            evt.WaitOne();
+
+            Assert.Equal(6, sum);
+        }
+
+        [Fact]
+        public void Concat_IEofIO_GetEnumeratorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xss = new RogueEnumerable<IObservable<int>>(ex);
+
+            var res = scheduler.Start(() =>
+                Observable.Concat(xss)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void Concat_IEofIO()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs1 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnCompleted<int>(40)
+            );
+
+            var xs2 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 4),
+                OnNext(20, 5),
+                OnCompleted<int>(30)
+            );
+
+            var xs3 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 6),
+                OnNext(20, 7),
+                OnNext(30, 8),
+                OnNext(40, 9),
+                OnCompleted<int>(50)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Concat(new[] { xs1, xs2, xs3 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(250, 4),
+                OnNext(260, 5),
+                OnNext(280, 6),
+                OnNext(290, 7),
+                OnNext(300, 8),
+                OnNext(310, 9),
+                OnCompleted<int>(320)
+            );
+
+            xs1.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+
+            xs2.Subscriptions.AssertEqual(
+                Subscribe(240, 270)
+            );
+
+            xs3.Subscriptions.AssertEqual(
+                Subscribe(270, 320)
+            );
+        }
+
+        [Fact]
+        public void Concat_EmptyEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_EmptyNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 1000)
+            );
+        }
+
+        [Fact]
+        public void Concat_NeverEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Concat_NeverNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Concat_EmptyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_ThrowEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Concat_ThrowThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Concat_ReturnEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_EmptyReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_ReturnNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 1000)
+            );
+        }
+
+        [Fact]
+        public void Concat_NeverReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Concat_ReturnReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(240, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_ThrowReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(230, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Concat_ReturnThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnError<int>(250, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_SomeDataSomeData()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(225)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.Concat(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(225, 250)
+            );
+        }
+
+        [Fact]
+        public void Concat_EnumerableThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(225)
+            );
+
+            var ex = new Exception();
+            var xss = new MockEnumerable<IObservable<int>>(scheduler, GetObservablesForConcatThrow(o, ex));
+
+            var res = scheduler.Start(() =>
+                xss.Concat()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(225, ex)
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        private IEnumerable<IObservable<int>> GetObservablesForConcatThrow(IObservable<int> first, Exception ex)
+        {
+            yield return first;
+            throw ex;
+        }
+
+        [Fact]
+        public void Concat_EnumerableTiming()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2), // !
+                OnNext(220, 3), // !
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateColdObservable(
+                OnNext(50, 4),  // !
+                OnNext(60, 5),  // !
+                OnNext(70, 6),  // !
+                OnCompleted<int>(80)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(200, 2),
+                OnNext(210, 3),
+                OnNext(220, 4),
+                OnNext(230, 5),
+                OnNext(270, 6),
+                OnNext(320, 7), // !
+                OnNext(330, 8), // !
+                OnCompleted<int>(340)
+            );
+
+            var xss = new MockEnumerable<ITestableObservable<int>>(scheduler, new[] { o1, o2, o3, o2 });
+
+            var res = scheduler.Start(() =>
+                xss.Select(xs => (IObservable<int>)xs).Concat()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 5),
+                OnNext(300, 6),
+                OnNext(320, 7),
+                OnNext(330, 8),
+                OnNext(390, 4),
+                OnNext(400, 5),
+                OnNext(410, 6),
+                OnCompleted<int>(420)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 310),
+                Subscribe(340, 420)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(310, 340)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Concat_Enumerable_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(200, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(320, 6),
+                OnNext(330, 7),
+                OnCompleted<int>(340)
+            );
+
+            var xss = new MockEnumerable<ITestableObservable<int>>(scheduler, new[] { o1, o2 });
+
+            var res = scheduler.Start(() =>
+                xss.Select(xs => (IObservable<int>)xs).Concat(),
+                300
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnNext(270, 5)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void Concat_Optimization_DeferEvalTiming()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateColdObservable(
+                OnNext(10, 4),
+                OnNext(20, 5),
+                OnNext(30, 6),
+                OnCompleted<int>(40)
+            );
+
+            var invoked = default(long);
+
+            var xs = o1;
+            var ys = Observable.Defer(() => { invoked = scheduler.Clock; return o2; });
+
+            var res = scheduler.Start(() =>
+                xs.Concat(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 270)
+            );
+
+            Assert.Equal(230, invoked);
+        }
+
+        [Fact]
+        public void Concat_Optimization_DeferExceptionPropagation()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(220)
+            );
+
+            var ex = new Exception();
+            var invoked = default(long);
+
+            var xs = o1;
+            var ys = Observable.Defer<int>(new Func<IObservable<int>>(() => { invoked = scheduler.Clock; throw ex; }));
+
+            var res = scheduler.Start(() =>
+                xs.Concat(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            Assert.Equal(220, invoked);
+        }
+
+#if !NO_PERF
+        [Fact]
+        public void Concat_TailRecursive1()
+        {
+            var create = 0L;
+            var start = 200L;
+            var end = 1000L;
+
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateColdObservable<int>(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnCompleted<int>(40)
+            );
+
+            var f = default(Func<IObservable<int>>);
+            f = () => Observable.Defer(() => o.Concat(f()));
+
+            var res = scheduler.Start(() => f(), create, start, end);
+
+            var expected = new List<Recorded<Notification<int>>>();
+
+            var t = start;
+            while (t <= end)
+            {
+                var n = (t - start) / 10;
+                if (n % 4 != 0)
+                {
+                    expected.Add(OnNext(t, (int)(n % 4)));
+                }
+
+                t += 10;
+            }
+
+            res.Messages.AssertEqual(expected);
+        }
+
+#if !NO_THREAD && !NETCOREAPP1_1 && !NETCOREAPP1_0
+        [Fact]
+        public void Concat_TailRecursive2()
+        {
+            var f = default(Func<int, IObservable<int>>);
+            f = x => Observable.Defer(() => Observable.Return(x, ThreadPoolScheduler.Instance).Concat(f(x + 1)));
+
+            var lst = new List<int>();
+            f(0).Select(x => new StackTrace().FrameCount).Take(10).ForEach(lst.Add);
+
+            Assert.True(lst.Last() - lst.First() < 10);
+        }
+#endif
+#endif
+
+        [Fact]
+        public void Concat_Task()
+        {
+            var tss = Observable.Concat(new[] { Task.Factory.StartNew(() => 1), Task.Factory.StartNew(() => 2), Task.Factory.StartNew(() => 3) }.ToObservable());
+
+            var res = tss.ToArray().Single();
+
+            Assert.True(res.SequenceEqual(new[] { 1, 2, 3 }));
+        }
+
+    }
+}

+ 317 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ContainsTest.cs

@@ -0,0 +1,317 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class ContainsTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Contains_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Contains(default(IObservable<int>), 42));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Contains(default(IObservable<int>), 42, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Contains(DummyObservable<int>.Instance, 42, null));
+        }
+
+        [Fact]
+        public void Contains_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(42)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Contains_ReturnPositive()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, true),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Contains_ReturnNegative()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(-2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Contains_SomePositive()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, true),
+                OnCompleted<bool>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Contains_SomeNegative()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(-3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Contains_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(42)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Contains_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(42)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Contains_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(42, new ContainsComparerThrows())
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(210, e => e is NotImplementedException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        class ContainsComparerThrows : IEqualityComparer<int>
+        {
+            public bool Equals(int x, int y)
+            {
+                throw new NotImplementedException();
+            }
+
+            public int GetHashCode(int obj)
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        [Fact]
+        public void Contains_ComparerContainsValue()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 3),
+                OnNext(220, 4),
+                OnNext(230, 8),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(42, new ContainsComparerMod2())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, true),
+                OnCompleted<bool>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Contains_ComparerDoesNotContainValue()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 4),
+                OnNext(230, 8),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Contains(21, new ContainsComparerMod2())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, false),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        class ContainsComparerMod2 : IEqualityComparer<int>
+        {
+            public bool Equals(int x, int y)
+            {
+                return x % 2 == y % 2;
+            }
+
+            public int GetHashCode(int obj)
+            {
+                return obj.GetHashCode();
+            }
+        }
+
+    }
+}

+ 483 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CountTest.cs

@@ -0,0 +1,483 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class CountTest : ReactiveTest
+    {
+        [Fact]
+        public void Count_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Count(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Count(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Count(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void Count_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 1),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Count_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+#if !NO_PERF && !NO_THREAD
+        [Fact]
+        public void Count_InjectOverflow()
+        {
+            var xs = Observable.Return(42, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<int>(xs, int.MaxValue).Count();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+#endif
+
+        [Fact]
+        public void Count_Predicate_Empty_True()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Empty_False()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => false)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Return_True()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 1),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Return_False()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => false)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Some_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(x => x < 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Some_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Some_Even()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(x => x % 2 == 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Throw_True()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Throw_False()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => false)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Count(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Count_Predicate_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnCompleted<int>(240)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Count(x =>
+                {
+                    if (x == 3)
+                        throw ex;
+
+                    return true;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+#if !NO_PERF && !NO_THREAD && !CRIPPLED_REFLECTION
+
+        [Fact]
+        public void Count_Predicate_InjectOverflow()
+        {
+            var xs = Observable.Return(42, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<int>(xs, int.MaxValue).Count(_ => true);
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+#endif
+
+    }
+
+#if !CRIPPLED_REFLECTION || NETCOREAPP1_1 || NETCOREAPP1_0
+    class OverflowInjection<T> : IObservable<T>
+    {
+        private readonly IObservable<T> _source;
+        private readonly object _initialCount;
+
+        public OverflowInjection(IObservable<T> source, object initialCount)
+        {
+            _source = source;
+            _initialCount = initialCount;
+        }
+
+        public IDisposable Subscribe(IObserver<T> observer)
+        {
+            var f = observer.GetType().GetField("_count", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
+            f.SetValue(observer, _initialCount);
+
+            return _source.Subscribe(observer);
+        }
+    }
+#endif
+}

+ 707 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CreateAsyncTest.cs

@@ -0,0 +1,707 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+
+namespace ReactiveTests.Tests
+{
+    public class CreateAsyncTest : ReactiveTest
+    {
+
+        [Fact]
+        public void CreateAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, Task>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, CancellationToken, Task>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, Task<IDisposable>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, CancellationToken, Task<IDisposable>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, Task<Action>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, CancellationToken, Task<Action>>)));
+        }
+
+        [Fact]
+        public void CreateAsync_NullCoalescingAction1()
+        {
+            var xs = Observable.Create<int>(o =>
+            {
+                o.OnNext(42);
+                return Task.Factory.StartNew(() => default(Action));
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+            d.Dispose();
+
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        [Fact]
+        public void CreateAsync_NullCoalescingAction2()
+        {
+            var xs = Observable.Create<int>((o, ct) =>
+            {
+                o.OnNext(42);
+                return Task.Factory.StartNew(() => default(Action));
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+            d.Dispose();
+
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        [Fact]
+        public void CreateAsync_NullCoalescingDisposable1()
+        {
+            var xs = Observable.Create<int>(o =>
+            {
+                o.OnNext(42);
+                return Task.Factory.StartNew(() => default(IDisposable));
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+            d.Dispose();
+
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        [Fact]
+        public void CreateAsync_NullCoalescingDisposable2()
+        {
+            var xs = Observable.Create<int>((o, ct) =>
+            {
+                o.OnNext(42);
+                return Task.Factory.StartNew(() => default(IDisposable));
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+            d.Dispose();
+
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        Task Producer1(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Never()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer1(observer, token, scheduler))
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4),
+                    OnNext(700, 5),
+                    OnNext(800, 6),
+                    OnNext(900, 7)
+                );
+            });
+        }
+
+        Task Producer2(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    tcs.SetResult(null);
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Completed1()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer2(observer, token, scheduler))
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4),
+                    OnCompleted<int>(700)
+                );
+            });
+        }
+
+        Task Producer3(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    results.OnCompleted();
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Completed2()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer3(observer, token, scheduler))
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4),
+                    OnCompleted<int>(700)
+                );
+            });
+        }
+
+        Task Producer4(IObserver<int> results, CancellationToken token, IScheduler scheduler, Exception exception)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    results.OnError(exception);
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Error1()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var exception = new Exception();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer4(observer, token, scheduler, exception))
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4),
+                    OnError<int>(700, exception)
+                );
+            });
+        }
+
+        Task Producer5(IObserver<int> results, CancellationToken token, IScheduler scheduler, Exception exception)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    tcs.SetException(exception);
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Error2()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var exception = new Exception();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer5(observer, token, scheduler, exception))
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4),
+                    OnError<int>(700, exception)
+                );
+            });
+        }
+
+
+        Task Producer6(IObserver<int> results, CancellationToken token, Exception exception)
+        {
+            throw exception;
+        }
+
+        [Fact]
+        public void CreateAsync_Error3()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var exception = new InvalidOperationException();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer6(observer, token, exception))
+                );
+
+                res.Messages.AssertEqual(
+                    OnError<int>(200, exception)
+                );
+            });
+        }
+
+        Task Producer7(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    tcs.SetResult(null);
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Cancel1()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer7(observer, token, scheduler)),
+                    650
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4)
+                );
+            });
+        }
+
+        Task Producer8(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    results.OnCompleted();
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Cancel2()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer8(observer, token, scheduler)),
+                    650
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4)
+                );
+            });
+        }
+
+        Task Producer9(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    results.OnCompleted();
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Cancel3()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer9(observer, token, scheduler)),
+                    750
+                );
+
+                res.Messages.AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4),
+                    OnCompleted<int>(700)
+                );
+            });
+        }
+
+        Task Producer10(IObserver<int> results, CancellationToken token, IScheduler scheduler)
+        {
+            var tcs = new TaskCompletionSource<object>();
+
+            var x = 0;
+
+            var d = scheduler.Schedule(TimeSpan.FromTicks(100), self =>
+            {
+                if (x == 4)
+                {
+                    tcs.SetCanceled();
+                }
+                results.OnNext(++x);
+                self(TimeSpan.FromTicks(100));
+            });
+
+            token.Register(d.Dispose);
+
+            return tcs.Task;
+        }
+
+        [Fact]
+        public void CreateAsync_Cancel4()
+        {
+            RunSynchronously(() =>
+            {
+                var scheduler = new TestScheduler();
+
+                var res = scheduler.Start(() =>
+                    Observable.Create<int>((observer, token) => Producer10(observer, token, scheduler))
+                );
+
+                res.Messages.Take(4).AssertEqual(
+                    OnNext(300, 1),
+                    OnNext(400, 2),
+                    OnNext(500, 3),
+                    OnNext(600, 4)
+                );
+
+                Assert.Equal(5, res.Messages.Count);
+
+                Assert.Equal(700, res.Messages[4].Time);
+                Assert.Equal(NotificationKind.OnError, res.Messages[4].Value.Kind);
+                Assert.True(res.Messages[4].Value.Exception is OperationCanceledException);
+            });
+        }
+
+        void RunSynchronously(Action action)
+        {
+            var t = new Task(action);
+            t.RunSynchronously(new SynchronousScheduler());
+            t.Wait();
+        }
+
+        class SynchronousScheduler : TaskScheduler
+        {
+            protected override IEnumerable<Task> GetScheduledTasks()
+            {
+                throw new NotImplementedException();
+            }
+
+            protected override void QueueTask(Task task)
+            {
+                TryExecuteTask(task);
+            }
+
+            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
+            {
+                return TryExecuteTask(task);
+            }
+        }
+
+        [Fact]
+        public void CreateAsync_Task_Simple()
+        {
+            var xs = Observable.Create<int>(observer =>
+            {
+                return Task.Factory.StartNew(() =>
+                {
+                    observer.OnNext(42);
+                    observer.OnCompleted();
+                });
+            });
+
+            var lst = new List<int>();
+            xs.ForEach(lst.Add);
+
+            Assert.True(new[] { 42 }.SequenceEqual(lst));
+        }
+
+        [Fact]
+        public void CreateAsync_Task_Token()
+        {
+            var e = new ManualResetEvent(false);
+
+            var xs = Observable.Create<int>((observer, ct) =>
+            {
+                return Task.Factory.StartNew(() =>
+                {
+                    var i = 0;
+
+                    while (!ct.IsCancellationRequested)
+                    {
+                        if (i++ == 10)
+                            e.Set();
+
+                        observer.OnNext(42);
+                    }
+                });
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+
+            e.WaitOne();
+            d.Dispose();
+
+            Assert.True(lst.Take(10).SequenceEqual(Enumerable.Repeat(42, 10)));
+        }
+
+        [Fact]
+        public void CreateAsync_IDisposable_Simple()
+        {
+            var stopped = new ManualResetEvent(false);
+            var s = Disposable.Create(() => stopped.Set());
+
+            var xs = Observable.Create<int>(observer =>
+            {
+                return Task.Factory.StartNew(() =>
+                {
+                    observer.OnNext(42);
+                    observer.OnCompleted();
+
+                    return s;
+                });
+            });
+
+            var lst = new List<int>();
+            xs.ForEach(lst.Add);
+
+            stopped.WaitOne();
+
+            Assert.True(new[] { 42 }.SequenceEqual(lst));
+        }
+
+        [Fact]
+        public void CreateAsync_IDisposable_Token()
+        {
+            var stopped = new ManualResetEvent(false);
+            var s = Disposable.Create(() => stopped.Set());
+
+            var e = new ManualResetEvent(false);
+
+            var xs = Observable.Create<int>((observer, ct) =>
+            {
+                return Task.Factory.StartNew(() =>
+                {
+                    var i = 0;
+
+                    while (!ct.IsCancellationRequested)
+                    {
+                        if (i++ == 10)
+                            e.Set();
+
+                        observer.OnNext(42);
+                    }
+
+                    return s;
+                });
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+
+            e.WaitOne();
+            d.Dispose();
+            stopped.WaitOne();
+
+            Assert.True(lst.Take(10).SequenceEqual(Enumerable.Repeat(42, 10)));
+        }
+
+        [Fact]
+        public void CreateAsync_Action_Simple()
+        {
+            var stopped = new ManualResetEvent(false);
+            var s = new Action(() => stopped.Set());
+
+            var xs = Observable.Create<int>(observer =>
+            {
+                return Task.Factory.StartNew(() =>
+                {
+                    observer.OnNext(42);
+                    observer.OnCompleted();
+
+                    return s;
+                });
+            });
+
+            var lst = new List<int>();
+            xs.ForEach(lst.Add);
+
+            stopped.WaitOne();
+
+            Assert.True(new[] { 42 }.SequenceEqual(lst));
+        }
+
+        [Fact]
+        public void CreateAsync_Action_Token()
+        {
+            var stopped = new ManualResetEvent(false);
+            var s = new Action(() => stopped.Set());
+
+            var e = new ManualResetEvent(false);
+
+            var xs = Observable.Create<int>((observer, ct) =>
+            {
+                return Task.Factory.StartNew(() =>
+                {
+                    var i = 0;
+
+                    while (!ct.IsCancellationRequested)
+                    {
+                        if (i++ == 10)
+                            e.Set();
+
+                        observer.OnNext(42);
+                    }
+
+                    return s;
+                });
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+
+            e.WaitOne();
+            d.Dispose();
+            stopped.WaitOne();
+
+            Assert.True(lst.Take(10).SequenceEqual(Enumerable.Repeat(42, 10)));
+        }
+
+    }
+}

+ 998 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/CreateTest.cs

@@ -0,0 +1,998 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Reactive.Disposables;
+
+namespace ReactiveTests.Tests
+{
+    public class CreateTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Create_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, Action>)));
+
+            //
+            // BREAKING CHANGE v2.0 > v1.x - Returning null from Subscribe means "nothing to do upon unsubscription"
+            //                               all null-coalesces to Disposable.Empty.
+            //
+            //ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(o => default(Action)).Subscribe(DummyObserver<int>.Instance));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(o => () => { }).Subscribe(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(o =>
+            {
+                o.OnError(null);
+                return () => { };
+            }).Subscribe(null));
+        }
+
+        [Fact]
+        public void Create_NullCoalescingAction()
+        {
+            var xs = Observable.Create<int>(o =>
+            {
+                o.OnNext(42);
+                return default(Action);
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+            d.Dispose();
+
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        [Fact]
+        public void Create_Next()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnNext(1);
+                    o.OnNext(2);
+                    return () => { };
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(200, 2)
+            );
+        }
+
+        [Fact]
+        public void Create_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnCompleted();
+                    o.OnNext(100);
+                    o.OnError(new Exception());
+                    o.OnCompleted();
+                    return () => { };
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(200)
+            );
+        }
+
+        [Fact]
+        public void Create_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnError(ex);
+                    o.OnNext(100);
+                    o.OnError(new Exception());
+                    o.OnCompleted();
+                    return () => { };
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void Create_Exception()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Subscribe());
+        }
+
+        [Fact]
+        public void Create_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    var stopped = false;
+
+                    o.OnNext(1);
+                    o.OnNext(2);
+                    scheduler.Schedule(TimeSpan.FromTicks(600), () =>
+                    {
+                        if (!stopped)
+                            o.OnNext(3);
+                    });
+                    scheduler.Schedule(TimeSpan.FromTicks(700), () =>
+                    {
+                        if (!stopped)
+                            o.OnNext(4);
+                    });
+                    scheduler.Schedule(TimeSpan.FromTicks(900), () =>
+                    {
+                        if (!stopped)
+                            o.OnNext(5);
+                    });
+                    scheduler.Schedule(TimeSpan.FromTicks(1100), () =>
+                    {
+                        if (!stopped)
+                            o.OnNext(6);
+                    });
+
+                    return () => { stopped = true; };
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(200, 2),
+                OnNext(800, 3),
+                OnNext(900, 4)
+            );
+        }
+
+        [Fact]
+        public void Create_ObserverThrows()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnNext(1);
+                    return () => { };
+                }).Subscribe(x => { throw new InvalidOperationException(); }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnError(new Exception());
+                    return () => { };
+                }).Subscribe(x => { }, ex => { throw new InvalidOperationException(); }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnCompleted();
+                    return () => { };
+                }).Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); }));
+        }
+
+        [Fact]
+        public void CreateWithDisposable_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(default(Func<IObserver<int>, IDisposable>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(o => DummyDisposable.Instance).Subscribe(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Create<int>(o =>
+            {
+                o.OnError(null);
+                return DummyDisposable.Instance;
+            }).Subscribe(null));
+        }
+
+        [Fact]
+        public void CreateWithDisposable_NullCoalescingAction()
+        {
+            var xs = Observable.Create<int>(o =>
+            {
+                o.OnNext(42);
+                return default(IDisposable);
+            });
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(lst.Add);
+            d.Dispose();
+
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        [Fact]
+        public void CreateWithDisposable_Next()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnNext(1);
+                    o.OnNext(2);
+                    return Disposable.Empty;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(200, 2)
+            );
+        }
+
+        [Fact]
+        public void CreateWithDisposable_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnCompleted();
+                    o.OnNext(100);
+                    o.OnError(new Exception());
+                    o.OnCompleted();
+                    return Disposable.Empty;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(200)
+            );
+        }
+
+        [Fact]
+        public void CreateWithDisposable_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnError(ex);
+                    o.OnNext(100);
+                    o.OnError(new Exception());
+                    o.OnCompleted();
+                    return Disposable.Empty;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void CreateWithDisposable_Exception()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(new Func<IObserver<int>, IDisposable>(o => { throw new InvalidOperationException(); })).Subscribe());
+        }
+
+        [Fact]
+        public void CreateWithDisposable_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Create<int>(o =>
+                {
+                    var d = new BooleanDisposable();
+
+                    o.OnNext(1);
+                    o.OnNext(2);
+                    scheduler.Schedule(TimeSpan.FromTicks(600), () =>
+                    {
+                        if (!d.IsDisposed)
+                            o.OnNext(3);
+                    });
+                    scheduler.Schedule(TimeSpan.FromTicks(700), () =>
+                    {
+                        if (!d.IsDisposed)
+                            o.OnNext(4);
+                    });
+                    scheduler.Schedule(TimeSpan.FromTicks(900), () =>
+                    {
+                        if (!d.IsDisposed)
+                            o.OnNext(5);
+                    });
+                    scheduler.Schedule(TimeSpan.FromTicks(1100), () =>
+                    {
+                        if (!d.IsDisposed)
+                            o.OnNext(6);
+                    });
+
+                    return d;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(200, 2),
+                OnNext(800, 3),
+                OnNext(900, 4)
+            );
+        }
+
+        [Fact]
+        public void CreateWithDisposable_ObserverThrows()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnNext(1);
+                    return Disposable.Empty;
+                }).Subscribe(x => { throw new InvalidOperationException(); }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnError(new Exception());
+                    return Disposable.Empty;
+                }).Subscribe(x => { }, ex => { throw new InvalidOperationException(); }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Create<int>(o =>
+                {
+                    o.OnCompleted();
+                    return Disposable.Empty;
+                }).Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); }));
+        }
+
+        [Fact]
+        public void Iterate_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Create<int>(default(Func<IObserver<int>, IEnumerable<IObservable<Object>>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Create(DummyFunc<IObserver<int>, IEnumerable<IObservable<Object>>>.Instance).Subscribe(null));
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Complete(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs, IObserver<int> observer)
+        {
+            observer.OnNext(1);
+            yield return xs.Select(x => new Object());
+
+            observer.OnNext(2);
+            yield return ys.Select(x => new Object());
+
+            observer.OnNext(3);
+            observer.OnCompleted();
+            yield return zs.Select(x => new Object());
+
+            observer.OnNext(4);
+        }
+
+        [Fact]
+        public void Iterate_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create<int>(observer => ToIterate_Complete(xs, ys, zs, observer)));
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(250, 2),
+                OnNext(280, 3),
+                OnCompleted<int>(280)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(280, 280)
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Complete_Implicit(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs, IObserver<int> observer)
+        {
+            observer.OnNext(1);
+            yield return xs.Select(x => new Object());
+
+            observer.OnNext(2);
+            yield return ys.Select(x => new Object());
+
+            observer.OnNext(3);
+            yield return zs.Select(x => new Object());
+
+            observer.OnNext(4);
+        }
+
+        [Fact]
+        public void Iterate_Complete_Implicit()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create<int>(observer => ToIterate_Complete_Implicit(xs, ys, zs, observer)));
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(250, 2),
+                OnNext(280, 3),
+                OnNext(340, 4),
+                OnCompleted<int>(340)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(280, 340)
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Throw(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs, IObserver<int> observer, Exception ex)
+        {
+            observer.OnNext(1);
+            yield return xs.Select(x => new Object());
+
+            observer.OnNext(2);
+            yield return ys.Select(x => new Object());
+
+            observer.OnNext(3);
+
+            if (xs != null)
+                throw ex;
+
+            yield return zs.Select(x => new Object());
+
+            observer.OnNext(4);
+            observer.OnCompleted();
+        }
+
+        [Fact]
+        public void Iterate_Iterator_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() => ObservableEx.Create<int>(observer => ToIterate_Throw(xs, ys, zs, observer, ex)));
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(250, 2),
+                OnNext(280, 3),
+                OnError<int>(280, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Error(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs, IObserver<int> observer, Exception ex)
+        {
+            observer.OnNext(1);
+            yield return xs.Select(x => new Object());
+
+            observer.OnNext(2);
+            observer.OnError(ex);
+
+            yield return ys.Select(x => new Object());
+
+            observer.OnNext(3);
+
+            yield return zs.Select(x => new Object());
+
+            observer.OnNext(4);
+            observer.OnCompleted();
+        }
+
+        [Fact]
+        public void Iterate_Iterator_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create<int>(observer => ToIterate_Error(xs, ys, zs, observer, ex)));
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(250, 2),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 250)
+            );
+
+            zs.Subscriptions.AssertEqual(
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Complete_Dispose(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs, IObserver<int> observer)
+        {
+            observer.OnNext(1);
+            yield return xs.Select(x => new Object());
+
+            observer.OnNext(2);
+            yield return ys.Select(x => new Object());
+
+            observer.OnNext(3);
+            yield return zs.Select(x => new Object());
+
+            observer.OnNext(4);
+        }
+
+        [Fact]
+        public void Iterate_Complete_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnNext(700, 7),
+                OnNext(800, 8),
+                OnNext(900, 9),
+                OnNext(1000, 10)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create<int>(observer => ToIterate_Complete_Dispose(xs, ys, zs, observer)));
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnNext(250, 2),
+                OnNext(280, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(280, 1000)
+            );
+        }
+
+        [Fact]
+        public void IteratorScenario()
+        {
+            var xs = ObservableEx.Create<int>(o => _IteratorScenario(100, 1000, o));
+
+            xs.AssertEqual(new[] { 100, 1000 }.ToObservable());
+        }
+
+        static IEnumerable<IObservable<Object>> _IteratorScenario(int x, int y, IObserver<int> results)
+        {
+            var xs = Observable.Range(1, x).ToListObservable();
+            yield return xs;
+
+            results.OnNext(xs.Value);
+
+            var ys = Observable.Range(1, y).ToListObservable();
+            yield return ys;
+
+            results.OnNext(ys.Value);
+        }
+
+        [Fact]
+        public void Iterate_Void_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Create(default(Func<IEnumerable<IObservable<object>>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Create(DummyFunc<IEnumerable<IObservable<Object>>>.Instance).Subscribe(null));
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Void_Complete(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs)
+        {
+            yield return xs.Select(x => new Object());
+
+            yield return ys.Select(x => new Object());
+
+            yield return zs.Select(x => new Object());
+        }
+
+        [Fact]
+        public void Iterate_Void_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create(() => ToIterate_Void_Complete(xs, ys, zs)));
+
+            res.Messages.AssertEqual(
+                OnCompleted<Unit>(340)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(280, 340)
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Void_Complete_Implicit(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs)
+        {
+            yield return xs.Select(x => new Object());
+
+            yield return ys.Select(x => new Object());
+
+            yield return zs.Select(x => new Object());
+        }
+
+        [Fact]
+        public void Iterate_Void_Complete_Implicit()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create(() => ToIterate_Void_Complete_Implicit(xs, ys, zs)));
+
+            res.Messages.AssertEqual(
+                OnCompleted<Unit>(340)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(280, 340)
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Void_Throw(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs, Exception ex)
+        {
+            yield return xs.Select(x => new Object());
+
+            yield return ys.Select(x => new Object());
+
+            if (xs != null)
+                throw ex;
+
+            yield return zs.Select(x => new Object());
+        }
+
+        [Fact]
+        public void Iterate_Void_Iterator_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnNext(50, 5),
+                OnCompleted<int>(60)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() => ObservableEx.Create(() => ToIterate_Void_Throw(xs, ys, zs, ex)));
+
+            res.Messages.AssertEqual(
+                OnError<Unit>(280, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+            );
+        }
+
+        IEnumerable<IObservable<Object>> ToIterate_Void_Complete_Dispose(IObservable<int> xs, IObservable<int> ys, IObservable<int> zs)
+        {
+            yield return xs.Select(x => new Object());
+
+            yield return ys.Select(x => new Object());
+
+            yield return zs.Select(x => new Object());
+        }
+
+        [Fact]
+        public void Iterate_Void_Complete_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnNext(40, 4),
+                OnCompleted<int>(50)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnCompleted<int>(30)
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnNext(600, 6),
+                OnNext(700, 7),
+                OnNext(800, 8),
+                OnNext(900, 9),
+                OnNext(1000, 10)
+            );
+
+            var res = scheduler.Start(() => ObservableEx.Create(() => ToIterate_Void_Complete_Dispose(xs, ys, zs)));
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 280)
+            );
+
+            zs.Subscriptions.AssertEqual(
+                Subscribe(280, 1000)
+            );
+        }
+
+        [Fact]
+        public void Iterate_Void_Func_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var obs = scheduler.Start(() => ObservableEx.Create(() => { throw new InvalidOperationException(); }));
+
+            Assert.Equal(1, obs.Messages.Count);
+
+            var notification = obs.Messages[0].Value;
+            Assert.Equal(NotificationKind.OnError, notification.Kind);
+            Assert.IsType<InvalidOperationException>(notification.Exception);
+        }
+
+        static IEnumerable<IObservable<Object>> _IteratorScenario_Void(int x, int y)
+        {
+            var xs = Observable.Range(1, x).ToListObservable();
+            yield return xs;
+
+            var ys = Observable.Range(1, y).ToListObservable();
+            yield return ys;
+        }
+
+        [Fact]
+        public void IteratorScenario_Void()
+        {
+            var xs = ObservableEx.Create(() => _IteratorScenario_Void(100, 1000));
+
+            xs.AssertEqual(new Unit[] { }.ToObservable());
+        }
+
+    }
+}

+ 180 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DefaultIfEmptyTest.cs

@@ -0,0 +1,180 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DefaultIfEmptyTest : ReactiveTest
+    {
+
+        [Fact]
+        public void DefaultIfEmpty_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DefaultIfEmpty(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DefaultIfEmpty(default(IObservable<int>), 42));
+        }
+
+        [Fact]
+        public void DefaultIfEmpty_NonEmpty1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DefaultIfEmpty()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void DefaultIfEmpty_NonEmpty2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DefaultIfEmpty(-1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void DefaultIfEmpty_Empty1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DefaultIfEmpty()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(420, 0),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void DefaultIfEmpty_Empty2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DefaultIfEmpty(-1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(420, -1),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void DefaultIfEmpty_Throw1()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnError<int>(420, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DefaultIfEmpty()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void DefaultIfEmpty_Throw2()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnError<int>(420, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DefaultIfEmpty(42)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+    }
+}

+ 82 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DeferAsyncTest.cs

@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DeferAsyncTest : ReactiveTest
+    {
+
+        [Fact]
+        public void DeferAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Defer(default(Func<Task<IObservable<int>>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DeferAsync(default(Func<CancellationToken, Task<IObservable<int>>>)));
+        }
+
+        [Fact]
+        public void DeferAsync_Simple()
+        {
+            var xs = Observable.Defer<int>(() => Task.Factory.StartNew(() => Observable.Return(42)));
+
+            var res = xs.ToEnumerable().ToList();
+
+            Assert.True(new[] { 42 }.SequenceEqual(res));
+        }
+
+        [Fact]
+        public void DeferAsync_WithCancel_Simple()
+        {
+            var xs = Observable.DeferAsync<int>(ct => Task.Factory.StartNew(() => Observable.Return(42)));
+
+            var res = xs.ToEnumerable().ToList();
+
+            Assert.True(new[] { 42 }.SequenceEqual(res));
+        }
+
+        [Fact]
+        public void DeferAsync_WithCancel_Cancel()
+        {
+            var N = 10;// 0000;
+            for (int i = 0; i < N; i++)
+            {
+                var e = new ManualResetEvent(false);
+                var called = false;
+
+                var xs = Observable.DeferAsync<int>(ct => Task.Factory.StartNew(() =>
+                {
+                    e.Set();
+
+                    while (!ct.IsCancellationRequested)
+                        ;
+
+                    return Observable.Defer(() => { called = true; return Observable.Return(42); });
+                }));
+
+                var d = xs.Subscribe(_ => { });
+
+                e.WaitOne();
+                d.Dispose();
+
+                Assert.False(called);
+            }
+        }
+
+    }
+}

+ 153 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DeferTest.cs

@@ -0,0 +1,153 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DeferTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Defer_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Defer<int>(default(Func<IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Defer(() => DummyObservable<int>.Instance).Subscribe(null));
+            ReactiveAssert.Throws</*some*/Exception>(() => Observable.Defer<int>(() => default(IObservable<int>)).Subscribe());
+        }
+
+        [Fact]
+        public void Defer_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+            var xs = default(ITestableObservable<long>);
+
+            var res = scheduler.Start(() =>
+                Observable.Defer(() =>
+                {
+                    invoked++;
+                    xs = scheduler.CreateColdObservable(
+                        OnNext<long>(100, scheduler.Clock),
+                        OnCompleted<long>(200));
+                    return xs;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 200L),
+                OnCompleted<long>(400)
+            );
+
+            Assert.Equal(1, invoked);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Defer_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+            var xs = default(ITestableObservable<long>);
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Defer(() =>
+                {
+                    invoked++;
+                    xs = scheduler.CreateColdObservable(
+                        OnNext<long>(100, scheduler.Clock),
+                        OnError<long>(200, ex));
+                    return xs;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 200L),
+                OnError<long>(400, ex)
+            );
+
+            Assert.Equal(1, invoked);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Defer_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+            var xs = default(ITestableObservable<long>);
+
+            var res = scheduler.Start(() =>
+                Observable.Defer(() =>
+                {
+                    invoked++;
+                    xs = scheduler.CreateColdObservable(
+                        OnNext<long>(100, scheduler.Clock),
+                        OnNext<long>(200, invoked),
+                        OnNext<long>(1100, 1000));
+                    return xs;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 200L),
+                OnNext(400, 1L)
+            );
+
+            Assert.Equal(1, invoked);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Defer_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Defer<int>(new Func<IObservable<int>>(() =>
+                {
+                    invoked++;
+                    throw ex;
+                }))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+
+            Assert.Equal(1, invoked);
+        }
+
+    }
+}

+ 166 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DelaySubscriptionTest.cs

@@ -0,0 +1,166 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DelaySubscriptionTest : ReactiveTest
+    {
+
+        [Fact]
+        public void DelaySubscription_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DelaySubscription(default(IObservable<int>), DateTimeOffset.Now));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DelaySubscription(default(IObservable<int>), DateTimeOffset.Now, Scheduler.Immediate));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DelaySubscription(DummyObservable<int>.Instance, DateTimeOffset.Now, default(IScheduler)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DelaySubscription(default(IObservable<int>), TimeSpan.Zero));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DelaySubscription(default(IObservable<int>), TimeSpan.Zero, Scheduler.Immediate));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DelaySubscription(DummyObservable<int>.Instance, TimeSpan.Zero, default(IScheduler)));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.DelaySubscription(DummyObservable<int>.Instance, TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.DelaySubscription(DummyObservable<int>.Instance, TimeSpan.FromSeconds(-1), Scheduler.Immediate));
+        }
+
+        [Fact]
+        public void DelaySubscription_TimeSpan_Default()
+        {
+            var lst = new List<int>();
+            Observable.Range(0, 10).DelaySubscription(TimeSpan.FromMilliseconds(1)).ForEach(lst.Add);
+            Assert.True(Enumerable.Range(0, 10).SequenceEqual(lst));
+        }
+
+        [Fact]
+        public void DelaySubscription_TimeSpan_Simple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 42),
+                OnNext(60, 43),
+                OnCompleted<int>(70)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DelaySubscription(TimeSpan.FromTicks(30), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnNext(290, 43),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+        }
+
+        [Fact]
+        public void DelaySubscription_TimeSpan_Error()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 42),
+                OnNext(60, 43),
+                OnError<int>(70, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DelaySubscription(TimeSpan.FromTicks(30), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnNext(290, 43),
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+        }
+
+        [Fact]
+        public void DelaySubscription_DateTimeOffset_Default()
+        {
+            var lst = new List<int>();
+            Observable.Range(0, 10).DelaySubscription(DateTimeOffset.UtcNow.AddMilliseconds(1)).ForEach(lst.Add);
+            Assert.True(Enumerable.Range(0, 10).SequenceEqual(lst));
+        }
+
+        [Fact]
+        public void DelaySubscription_DateTimeOffset_Simple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 42),
+                OnNext(60, 43),
+                OnCompleted<int>(70)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DelaySubscription(new DateTimeOffset(230, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnNext(290, 43),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+        }
+
+        [Fact]
+        public void DelaySubscription_DateTimeOffset_Error()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 42),
+                OnNext(60, 43),
+                OnError<int>(70, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DelaySubscription(new DateTimeOffset(230, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnNext(290, 43),
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+        }
+
+    }
+}

+ 1840 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DelayTest.cs

@@ -0,0 +1,1840 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DelayTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Delay_ArgumentChecking()
+        {
+            var scheduler = new TestScheduler();
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(default(IObservable<int>), DateTimeOffset.Now));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(default(IObservable<int>), TimeSpan.Zero));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Delay(someObservable, TimeSpan.FromSeconds(-1)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(default(IObservable<int>), DateTimeOffset.Now, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(default(IObservable<int>), TimeSpan.Zero, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(someObservable, DateTimeOffset.Now, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(someObservable, TimeSpan.Zero, null));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Delay(someObservable, TimeSpan.FromSeconds(-1), scheduler));
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Simple1()
+        {
+            Delay_TimeSpan_Simple1_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Simple1_Stopwatch()
+        {
+            Delay_TimeSpan_Simple1_Impl(true);
+        }
+
+        private void Delay_TimeSpan_Simple1_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(100), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, 2),
+                OnNext(450, 3),
+                OnNext(550, 4),
+                OnCompleted<int>(650)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Simple1()
+        {
+            Delay_DateTimeOffset_Simple1_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Simple1_Stopwatch()
+        {
+            Delay_DateTimeOffset_Simple1_Impl(true);
+        }
+
+        private void Delay_DateTimeOffset_Simple1_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(new DateTimeOffset(300, TimeSpan.Zero), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, 2),
+                OnNext(450, 3),
+                OnNext(550, 4),
+                OnCompleted<int>(650)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Simple2()
+        {
+            Delay_TimeSpan_Simple2_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Simple2_Stopwatch()
+        {
+            Delay_TimeSpan_Simple2_Impl(true);
+        }
+
+        private void Delay_TimeSpan_Simple2_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(50), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 2),
+                OnNext(400, 3),
+                OnNext(500, 4),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Simple2()
+        {
+            Delay_DateTimeOffset_Simple2_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Simple2_Stopwatch()
+        {
+            Delay_DateTimeOffset_Simple2_Impl(true);
+        }
+
+        private void Delay_DateTimeOffset_Simple2_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(new DateTimeOffset(250, TimeSpan.Zero), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 2),
+                OnNext(400, 3),
+                OnNext(500, 4),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Simple3()
+        {
+            Delay_TimeSpan_Simple3_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Simple3_Stopwatch()
+        {
+            Delay_TimeSpan_Simple3_Impl(true);
+        }
+
+        private void Delay_TimeSpan_Simple3_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(150), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(400, 2),
+                OnNext(500, 3),
+                OnNext(600, 4),
+                OnCompleted<int>(700)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Simple3()
+        {
+            Delay_DateTimeOffset_Simple3_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Simple3_Stopwatch()
+        {
+            Delay_DateTimeOffset_Simple3_Impl(true);
+        }
+
+        private void Delay_DateTimeOffset_Simple3_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(new DateTimeOffset(350, TimeSpan.Zero), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(400, 2),
+                OnNext(500, 3),
+                OnNext(600, 4),
+                OnCompleted<int>(700)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Error1()
+        {
+            Delay_TimeSpan_Error1_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Error1_Stopwatch()
+        {
+            Delay_TimeSpan_Error1_Impl(true);
+        }
+
+        private void Delay_TimeSpan_Error1_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnError<int>(550, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(50), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 2),
+                OnNext(400, 3),
+                OnNext(500, 4),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Error1()
+        {
+            Delay_DateTimeOffset_Error1_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Error1_Stopwatch()
+        {
+            Delay_DateTimeOffset_Error1_Impl(true);
+        }
+
+        private void Delay_DateTimeOffset_Error1_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnError<int>(550, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(new DateTimeOffset(250, TimeSpan.Zero), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 2),
+                OnNext(400, 3),
+                OnNext(500, 4),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Error2()
+        {
+            Delay_TimeSpan_Error2_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Error2_Stopwatch()
+        {
+            Delay_TimeSpan_Error2_Impl(true);
+        }
+
+        private void Delay_TimeSpan_Error2_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnError<int>(550, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(150), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(400, 2),
+                OnNext(500, 3),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Error2()
+        {
+            Delay_DateTimeOffset_Error2_Impl(false);
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_Error2_Stopwatch()
+        {
+            Delay_DateTimeOffset_Error2_Impl(true);
+        }
+
+        private void Delay_DateTimeOffset_Error2_Impl(bool useStopwatch)
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnError<int>(550, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(new DateTimeOffset(350, TimeSpan.Zero), useStopwatch ? scheduler : scheduler.DisableOptimizations())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(400, 2),
+                OnNext(500, 3),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+#if !NO_THREAD
+        [Fact]
+        public void Delay_TimeSpan_Real_Simple1()
+        {
+            Delay_TimeSpan_Real_Simple1_Impl(ThreadPoolScheduler.Instance.DisableOptimizations());
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Real_Simple1_Stopwatch()
+        {
+            Delay_TimeSpan_Real_Simple1_Impl(ThreadPoolScheduler.Instance);
+        }
+#endif
+
+        private void Delay_TimeSpan_Real_Simple1_Impl(IScheduler scheduler)
+        {
+            var s = new Subject<int>();
+
+            var res = s.Delay(TimeSpan.FromMilliseconds(10), scheduler);
+
+            var lst = new List<int>();
+            var e = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => e.Set());
+
+            Task.Run(() =>
+            {
+                s.OnNext(1);
+                s.OnNext(2);
+                s.OnNext(3);
+                s.OnCompleted();
+            });
+
+            e.WaitOne();
+            Assert.True(new[] { 1, 2, 3 }.SequenceEqual(lst));
+        }
+
+#if !NO_THREAD
+        [Fact]
+        public void Delay_TimeSpan_Real_Error1()
+        {
+            Delay_TimeSpan_Real_Error1_Impl(ThreadPoolScheduler.Instance.DisableOptimizations());
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Real_Error1_Stopwatch()
+        {
+            Delay_TimeSpan_Real_Error1_Impl(ThreadPoolScheduler.Instance);
+        }
+#endif
+
+        private void Delay_TimeSpan_Real_Error1_Impl(IScheduler scheduler)
+        {
+            var ex = new Exception();
+
+            var s = new Subject<int>();
+
+            var res = s.Delay(TimeSpan.FromMilliseconds(10), scheduler);
+
+            var e = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(_ => { }, ex_ => { err = ex_; e.Set(); });
+
+            Task.Run(() =>
+            {
+                s.OnNext(1);
+                s.OnNext(2);
+                s.OnNext(3);
+                s.OnError(ex);
+            });
+
+            e.WaitOne();
+            Assert.Same(ex, err);
+        }
+
+#if !NO_THREAD
+        [Fact]
+        public void Delay_TimeSpan_Real_Error2()
+        {
+            Delay_TimeSpan_Real_Error2_Impl(ThreadPoolScheduler.Instance.DisableOptimizations());
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Real_Error2_Stopwatch()
+        {
+            Delay_TimeSpan_Real_Error2_Impl(ThreadPoolScheduler.Instance);
+        }
+#endif
+
+        private void Delay_TimeSpan_Real_Error2_Impl(IScheduler scheduler)
+        {
+            var ex = new Exception();
+
+            var s = new Subject<int>();
+
+            var res = s.Delay(TimeSpan.FromMilliseconds(10), scheduler);
+
+            var next = new ManualResetEvent(false);
+            var e = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(_ => { next.Set(); }, ex_ => { err = ex_; e.Set(); });
+
+            Task.Run(() =>
+            {
+                s.OnNext(1);
+                next.WaitOne();
+
+                s.OnError(ex);
+            });
+
+            e.WaitOne();
+            Assert.Same(ex, err);
+        }
+
+#if !NO_THREAD
+        [Fact]
+        public void Delay_TimeSpan_Real_Error3()
+        {
+            Delay_TimeSpan_Real_Error3_Impl(ThreadPoolScheduler.Instance.DisableOptimizations());
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Real_Error3_Stopwatch()
+        {
+            Delay_TimeSpan_Real_Error3_Impl(ThreadPoolScheduler.Instance);
+        }
+#endif
+
+        private void Delay_TimeSpan_Real_Error3_Impl(IScheduler scheduler)
+        {
+            var ex = new Exception();
+
+            var s = new Subject<int>();
+
+            var res = s.Delay(TimeSpan.FromMilliseconds(10), scheduler);
+
+            var next = new ManualResetEvent(false);
+            var ack = new ManualResetEvent(false);
+
+            var e = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(_ => { next.Set(); ack.WaitOne(); }, ex_ => { err = ex_; e.Set(); });
+
+            Task.Run(() =>
+            {
+                s.OnNext(1);
+                next.WaitOne();
+
+                s.OnError(ex);
+                ack.Set();
+            });
+
+            e.WaitOne();
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_Positive()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs = new[] {
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            };
+
+            var xs = scheduler.CreateHotObservable(msgs);
+
+            const ushort delay = 42;
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(delay), scheduler)
+            );
+
+            var expected = from n in msgs
+                           where n.Time > Subscribed
+                           select new Recorded<Notification<int>>((ushort)(n.Time + delay), n.Value);
+
+            res.Messages.AssertEqual(expected);
+        }
+
+        [Fact]
+        public void Delay_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(10), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(560)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(550, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(10), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(TimeSpan.FromTicks(10), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Delay_TimeSpan_DefaultScheduler()
+        {
+            Assert.True(Observable.Return(1).Delay(TimeSpan.FromMilliseconds(1)).ToEnumerable().SequenceEqual(new[] { 1 }));
+        }
+
+        [Fact]
+        public void Delay_DateTimeOffset_DefaultScheduler()
+        {
+            Assert.True(Observable.Return(1).Delay(DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(1)).ToEnumerable().SequenceEqual(new[] { 1 }));
+        }
+
+        [Fact]
+        public void Delay_CrossingMessages()
+        {
+            var lst = new List<int>();
+
+            var evt = new ManualResetEvent(false);
+
+            var s = new Subject<int>();
+            s.Delay(TimeSpan.FromSeconds(0.01)).Subscribe(x =>
+            {
+                lst.Add(x);
+                if (x < 9)
+                    s.OnNext(x + 1);
+                else
+                    s.OnCompleted();
+            }, () =>
+            {
+                evt.Set();
+            });
+            s.OnNext(0);
+
+            evt.WaitOne();
+
+            Assert.True(Enumerable.Range(0, 10).SequenceEqual(lst));
+        }
+
+        [Fact]
+        public void Delay_Duration_ArgumentChecking()
+        {
+            var someObservable = DummyObservable<int>.Instance;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(default(IObservable<int>), x => someObservable));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(someObservable, default(Func<int, IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(default(IObservable<int>), someObservable, x => someObservable));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(someObservable, default(IObservable<int>), x => someObservable));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Delay(someObservable, someObservable, default(Func<int, IObservable<int>>)));
+        }
+
+        [Fact]
+        public void Delay_Duration_Simple1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 10),
+                OnNext(220, 30),
+                OnNext(230, 50),
+                OnNext(240, 35),
+                OnNext(250, 20),
+                OnCompleted<int>(260)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x => scheduler.CreateColdObservable(new[] { OnNext(x, "!") }))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 10, 10),
+                OnNext(220 + 30, 30),
+                OnNext(250 + 20, 20),
+                OnNext(240 + 35, 35),
+                OnNext(230 + 50, 50),
+                OnCompleted<int>(280)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 260)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_Simple2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 10, 2),
+                OnNext(220 + 10, 3),
+                OnNext(230 + 10, 4),
+                OnNext(240 + 10, 5),
+                OnNext(250 + 10, 6),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 220),
+                Subscribe(220, 230),
+                Subscribe(230, 240),
+                Subscribe(240, 250),
+                Subscribe(250, 260)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_Simple3()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(100, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 100, 2),
+                OnNext(220 + 100, 3),
+                OnNext(230 + 100, 4),
+                OnNext(240 + 100, 5),
+                OnNext(250 + 100, 6),
+                OnCompleted<int>(350)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 310),
+                Subscribe(220, 320),
+                Subscribe(230, 330),
+                Subscribe(240, 340),
+                Subscribe(250, 350)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_Simple4_InnerEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnCompleted<int>(100)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 100, 2),
+                OnNext(220 + 100, 3),
+                OnNext(230 + 100, 4),
+                OnNext(240 + 100, 5),
+                OnNext(250 + 100, 6),
+                OnCompleted<int>(350)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 310),
+                Subscribe(220, 320),
+                Subscribe(230, 330),
+                Subscribe(240, 340),
+                Subscribe(250, 350)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_Dispose1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(200, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys),
+                425
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 200, 2),
+                OnNext(220 + 200, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 410),
+                Subscribe(220, 420),
+                Subscribe(230, 425),
+                Subscribe(240, 425),
+                Subscribe(250, 425)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_Dispose2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(400, 3),
+                OnCompleted<int>(500)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys),
+                300
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 50, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 260)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_OuterError1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnError<int>(300, ex)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(100, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 300),
+                Subscribe(220, 300),
+                Subscribe(230, 300),
+                Subscribe(240, 300),
+                Subscribe(250, 300)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_OuterError2()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnError<int>(300, ex)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 10, 2),
+                OnNext(220 + 10, 3),
+                OnNext(230 + 10, 4),
+                OnNext(240 + 10, 5),
+                OnNext(250 + 10, 6),
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 220),
+                Subscribe(220, 230),
+                Subscribe(230, 240),
+                Subscribe(240, 250),
+                Subscribe(250, 260)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_InnerError1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(30, "!")
+            );
+
+            var zs = scheduler.CreateColdObservable(
+                OnError<string>(25, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x => x != 5 ? ys : zs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210 + 30, 2),
+                OnNext(220 + 30, 3),
+                OnNext(230 + 30, 4),
+                OnError<int>(240 + 25, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 265)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 240),
+                Subscribe(220, 250),
+                Subscribe(230, 260),
+                Subscribe(250, 265)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_InnerError2()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnNext(250, 6),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnError<string>(100, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(_ => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210 + 100, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(210, 310),
+                Subscribe(220, 310),
+                Subscribe(230, 310),
+                Subscribe(240, 310),
+                Subscribe(250, 310)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_SelectorThrows1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(300, 3),
+                OnNext(350, 4),
+                OnNext(400, 5),
+                OnNext(450, 6),
+                OnCompleted<int>(500)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext<string>(80, "")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                {
+                    if (x == 4)
+                        throw ex;
+
+                    return ys;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(330, 2),
+                OnError<int>(350, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(250, 330),
+                Subscribe(300, 350)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_Simple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                    scheduler.CreateColdObservable(
+                        OnNext(x * 10, "Ignore"),
+                        OnNext(x * 10 + 5, "Aargh!")
+                    )
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 2 * 10, 2),
+                OnNext<int>(350 + 3 * 10, 3),
+                OnNext<int>(450 + 4 * 10, 4),
+                OnCompleted<int>(550)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_DeferOnCompleted()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(451)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                    scheduler.CreateColdObservable(
+                        OnNext(x * 10, "Ignore"),
+                        OnNext(x * 10 + 5, "Aargh!")
+                    )
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 2 * 10, 2),
+                OnNext<int>(350 + 3 * 10, 3),
+                OnNext<int>(450 + 4 * 10, 4),
+                OnCompleted<int>(450 + 4 * 10)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 451)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_InnerError()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(451)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                    x < 4 ? scheduler.CreateColdObservable(
+                                OnNext(x * 10, "Ignore"),
+                                OnNext(x * 10 + 5, "Aargh!")
+                            )
+                          : scheduler.CreateColdObservable(
+                                OnError<string>(x * 10, ex)
+                            )
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 2 * 10, 2),
+                OnNext<int>(350 + 3 * 10, 3),
+                OnError<int>(450 + 4 * 10, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 451)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_OuterError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnError<int>(460, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                    scheduler.CreateColdObservable(
+                        OnNext(x * 10, "Ignore"),
+                        OnNext(x * 10 + 5, "Aargh!")
+                    )
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 2 * 10, 2),
+                OnNext<int>(350 + 3 * 10, 3),
+                OnError<int>(460, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 460)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_SelectorThrows2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                {
+                    if (x < 4)
+                    {
+                        return scheduler.CreateColdObservable(
+                                OnNext(x * 10, "Ignore"),
+                                OnNext(x * 10 + 5, "Aargh!")
+                            );
+                    }
+                    else
+                        throw ex;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 2 * 10, 2),
+                OnNext<int>(350 + 3 * 10, 3),
+                OnError<int>(450, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_InnerDone()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(250, 2),
+                OnNext(350, 3),
+                OnNext(450, 4),
+                OnCompleted<int>(550)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x =>
+                    scheduler.CreateColdObservable(
+                        OnCompleted<string>(x * 10)
+                    )
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 2 * 10, 2),
+                OnNext<int>(350 + 3 * 10, 3),
+                OnNext<int>(450 + 4 * 10, 4),
+                OnCompleted<int>(550)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+        }
+
+        [Fact]
+        public void Delay_Duration_InnerSubscriptionTimes()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, -1),
+                OnNext(250, 0),
+                OnNext(350, 1),
+                OnNext(450, 2),
+                OnCompleted<int>(550)
+            );
+
+            var ys = new[] {
+                scheduler.CreateColdObservable(
+                    OnNext(20, 42),
+                    OnNext(25, 99)
+                ),
+                scheduler.CreateColdObservable(
+                    OnNext(10, 43),
+                    OnNext(15, 99)
+                ),
+                scheduler.CreateColdObservable(
+                    OnNext(30, 44),
+                    OnNext(35, 99)
+                ),
+            };
+
+            var res = scheduler.Start(() =>
+                xs.Delay(x => ys[x])
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<int>(250 + 20, 0),
+                OnNext<int>(350 + 10, 1),
+                OnNext<int>(450 + 30, 2),
+                OnCompleted<int>(550)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+
+            ys[0].Subscriptions.AssertEqual(Subscribe(250, 250 + 20));
+            ys[1].Subscriptions.AssertEqual(Subscribe(350, 350 + 10));
+            ys[2].Subscriptions.AssertEqual(Subscribe(450, 450 + 30));
+        }
+
+        [Fact]
+        public void Delay_DurationAndSubscription_Simple1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 10),
+                OnNext(220, 30),
+                OnNext(230, 50),
+                OnNext(240, 35),
+                OnNext(250, 20),
+                OnCompleted<int>(260)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(10, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(ys, x => scheduler.CreateColdObservable(new[] { OnNext(x, "!") }))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220 + 30, 30),
+                OnNext(250 + 20, 20),
+                OnNext(240 + 35, 35),
+                OnNext(230 + 50, 50),
+                OnCompleted<int>(280)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(210, 260)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Delay_DurationAndSubscription_Simple2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 10),
+                OnNext(220, 30),
+                OnNext(230, 50),
+                OnNext(240, 35),
+                OnNext(250, 20),
+                OnCompleted<int>(260)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnCompleted<string>(10)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(ys, x => scheduler.CreateColdObservable(new[] { OnNext(x, "!") }))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220 + 30, 30),
+                OnNext(250 + 20, 20),
+                OnNext(240 + 35, 35),
+                OnNext(230 + 50, 50),
+                OnCompleted<int>(280)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(210, 260)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Delay_DurationAndSubscription_Dispose1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 10),
+                OnNext(220, 30),
+                OnNext(230, 50),
+                OnNext(240, 35),
+                OnNext(250, 20),
+                OnCompleted<int>(260)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext<string>(10, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(ys, x => scheduler.CreateColdObservable(new[] { OnNext(x, "!") })),
+                255
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220 + 30, 30)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(210, 255)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Delay_DurationAndSubscription_Dispose2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 10),
+                OnNext(220, 30),
+                OnNext(230, 50),
+                OnNext(240, 35),
+                OnNext(250, 20),
+                OnCompleted<int>(260)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext<string>(100, "!")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(ys, x => scheduler.CreateColdObservable(new[] { OnNext(x, "!") })),
+                255
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 255)
+            );
+        }
+
+        [Fact]
+        public void Delay_DurationAndSubscription_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 10),
+                OnNext(220, 30),
+                OnNext(230, 50),
+                OnNext(240, 35),
+                OnNext(250, 20),
+                OnCompleted<int>(260)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnError<string>(10, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Delay(ys, x => scheduler.CreateColdObservable(new[] { OnNext(x, "!") }))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200 + 10, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Delay_ErrorHandling1()
+        {
+            //
+            // Checks for race condition between OnNext and OnError where the latter has a chance to
+            // send out the OnError message before the former gets a chance to run in the delayed
+            // queue. In that case, the OnNext message should not come out. 
+            //
+            // See DrainQueue's first _hasFailed check.
+            //
+
+            var xs = Observable.Create<int>(observer =>
+            {
+                observer.OnNext(42);
+                observer.OnError(new Exception());
+                return () => { };
+            });
+
+            var s = new ImpulseScheduler();
+
+            var called = false;
+            var failed = new ManualResetEvent(false);
+            xs.Delay(TimeSpan.FromDays(1), s).Subscribe(_ => { called = true; }, ex => { failed.Set(); });
+
+            failed.WaitOne();
+            s.Event.Set();
+            s.Done.WaitOne();
+
+            Assert.False(called);
+        }
+
+        class ImpulseScheduler : IScheduler
+        {
+            public DateTimeOffset Now
+            {
+                get { return DateTimeOffset.UtcNow; }
+            }
+
+            public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
+            {
+                throw new NotImplementedException();
+            }
+
+            private ManualResetEvent _event = new ManualResetEvent(false);
+            private ManualResetEvent _done = new ManualResetEvent(false);
+
+            public ManualResetEvent Event { get { return _event; } }
+            public ManualResetEvent Done { get { return _done; } }
+
+            public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
+            {
+                Scheduler.Default.Schedule(() =>
+                {
+                    _event.WaitOne();
+                    action(this, state);
+                    _done.Set();
+                });
+
+                return Disposable.Empty;
+            }
+
+            public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        [Fact]
+        public void Delay_LongRunning_CancelEarly()
+        {
+            var xs = Observable.Create<int>(observer =>
+            {
+                return Scheduler.Default.Schedule(TimeSpan.FromHours(1), () =>
+                {
+                    observer.OnNext(42);
+                });
+            });
+
+            var s = new ManualResetEvent(false);
+            var e = new ManualResetEvent(false);
+
+            var ys = xs.Delay(TimeSpan.Zero, new MyLongRunning1(s, e));
+
+            var d = ys.Subscribe(_ => { });
+
+            s.WaitOne();
+            d.Dispose();
+            e.WaitOne();
+        }
+
+        class MyLongRunning1 : LocalScheduler, ISchedulerLongRunning
+        {
+            private ManualResetEvent _start;
+            private ManualResetEvent _stop;
+
+            public MyLongRunning1(ManualResetEvent start, ManualResetEvent stop)
+            {
+                _start = start;
+                _stop = stop;
+            }
+
+            public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
+            {
+                var b = new BooleanDisposable();
+                Task.Run(() =>
+                {
+                    _start.Set();
+                    action(state, b);
+                    _stop.Set();
+                });
+                return b;
+            }
+
+            public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        [Fact]
+        public void Delay_LongRunning_CancelLate()
+        {
+            var xs = Observable.Return(42);
+
+            var s = new ManualResetEvent(false);
+            var e = new ManualResetEvent(false);
+
+            var ys = xs.Delay(TimeSpan.FromHours(1), new MyLongRunning2(s, e));
+
+            var d = ys.Subscribe(_ => { });
+
+            s.WaitOne();
+            d.Dispose();
+            e.WaitOne();
+        }
+
+        class MyLongRunning2 : LocalScheduler, ISchedulerLongRunning
+        {
+            private ManualResetEvent _start;
+            private ManualResetEvent _stop;
+
+            public MyLongRunning2(ManualResetEvent start, ManualResetEvent stop)
+            {
+                _start = start;
+                _stop = stop;
+            }
+
+            public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
+            {
+                var b = new BooleanDisposable();
+                Task.Run(() =>
+                {
+                    action(state, b);
+                    _stop.Set();
+                });
+                return b;
+            }
+
+            public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
+            {
+                _start.Set();
+                return Disposable.Empty;
+            }
+        }
+
+    }
+}

+ 229 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DematerializeTest.cs

@@ -0,0 +1,229 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DematerializeTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Dematerialize_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Dematerialize<int>(null));
+        }
+
+        [Fact]
+        public void Dematerialize_Range1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, Notification.CreateOnNext(41)),
+                OnNext(210, Notification.CreateOnNext(42)),
+                OnNext(220, Notification.CreateOnNext(43)),
+                OnCompleted<Notification<int>>(250)
+            );
+            var res = scheduler.Start(() =>
+                xs.Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 42),
+                OnNext(220, 43),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Dematerialize_Range2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, Notification.CreateOnNext(41)),
+                OnNext(210, Notification.CreateOnNext(42)),
+                OnNext(220, Notification.CreateOnNext(43)),
+                OnNext(230, Notification.CreateOnCompleted<int>())
+            );
+            var res = scheduler.Start(() =>
+                xs.Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 42),
+                OnNext(220, 43),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Dematerialize_Error1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, Notification.CreateOnNext(41)),
+                OnNext(210, Notification.CreateOnNext(42)),
+                OnNext(220, Notification.CreateOnNext(43)),
+                OnError<Notification<int>>(230, ex)
+            );
+            var res = scheduler.Start(() =>
+                xs.Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 42),
+                OnNext(220, 43),
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Dematerialize_Error2()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, Notification.CreateOnNext(41)),
+                OnNext(210, Notification.CreateOnNext(42)),
+                OnNext(220, Notification.CreateOnNext(43)),
+                OnNext(230, Notification.CreateOnError<int>(ex))
+            );
+            var res = scheduler.Start(() =>
+                xs.Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 42),
+                OnNext(220, 43),
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Materialize_Dematerialize_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Never<int>();
+
+            var res = scheduler.Start(() =>
+                xs.Materialize().Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Materialize_Dematerialize_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Materialize().Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Materialize_Dematerialize_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Materialize().Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Materialize_Dematerialize_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Materialize().Dematerialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+    }
+}

+ 456 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DistinctTest.cs

@@ -0,0 +1,456 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DistinctTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Distinct_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(default(IObservable<int>), EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(DummyObservable<int>.Instance, default(EqualityComparer<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(DummyObservable<int>.Instance, default(Func<int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(default(IObservable<int>), x => x, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(DummyObservable<int>.Instance, default(Func<int, int>), EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Distinct(DummyObservable<int>.Instance, x => x, default(EqualityComparer<int>)));
+        }
+
+        [Fact]
+        public void Distinct_DefaultComparer_AllDistinct()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 1),
+                OnNext(380, 3),
+                OnNext(400, 5),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 1),
+                OnNext(380, 3),
+                OnNext(400, 5),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_DefaultComparer_SomeDuplicates()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 2),
+                OnNext(380, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(380, 3),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_CustomComparer_AllDistinct()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 1),
+                OnNext(380, 3),
+                OnNext(400, 5),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(new ModComparer(10))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 1),
+                OnNext(380, 3),
+                OnNext(400, 5),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_CustomComparer_SomeDuplicates()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 12),
+                OnNext(380, 3),
+                OnNext(400, 24),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(new ModComparer(10))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(380, 3),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        class ModComparer : IEqualityComparer<int>
+        {
+            private readonly int _mod;
+
+            public ModComparer(int mod)
+            {
+                _mod = mod;
+            }
+
+            public bool Equals(int x, int y)
+            {
+                return EqualityComparer<int>.Default.Equals(x % _mod, y % _mod);
+            }
+
+            public int GetHashCode(int obj)
+            {
+                return EqualityComparer<int>.Default.GetHashCode(obj % _mod);
+            }
+        }
+
+        [Fact]
+        public void Distinct_KeySelector_DefaultComparer_AllDistinct()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 8),
+                OnNext(300, 4),
+                OnNext(350, 2),
+                OnNext(380, 6),
+                OnNext(400, 10),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(x => x / 2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 8),
+                OnNext(300, 4),
+                OnNext(350, 2),
+                OnNext(380, 6),
+                OnNext(400, 10),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_KeySelector_DefaultComparer_SomeDuplicates()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnNext(380, 7),
+                OnNext(400, 5),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(x => x / 2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 4),
+                OnNext(300, 2),
+                OnNext(380, 7),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_KeySelector_CustomComparer_AllDistinct()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 8),
+                OnNext(300, 4),
+                OnNext(350, 2),
+                OnNext(380, 6),
+                OnNext(400, 10),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(x => x / 2, new ModComparer(10))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 8),
+                OnNext(300, 4),
+                OnNext(350, 2),
+                OnNext(380, 6),
+                OnNext(400, 10),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_KeySelector_CustomComparer_SomeDuplicates()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 8),
+                OnNext(300, 4),
+                OnNext(350, 2),
+                OnNext(380, 6),
+                OnNext(400, 10),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(x => x / 2, new ModComparer(3))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 8),
+                OnNext(300, 4),
+                OnNext(380, 6),
+                OnCompleted<int>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void Distinct_KeySelector_Throws()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 3),
+                OnNext(300, 2),
+                OnNext(350, 1),
+                OnNext(380, 0),
+                OnNext(400, 4),
+                OnCompleted<int>(420)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(x => { if (x == 0) throw ex; return x / 2; })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 3),
+                OnNext(350, 1),
+                OnError<int>(380, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 380)
+            );
+        }
+
+        [Fact]
+        public void Distinct_CustomComparer_Throws()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnNext(380, 4),
+                OnNext(400, 5),
+                OnCompleted<int>(420)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Distinct(new ThrowComparer(4, ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnError<int>(380, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 380)
+            );
+        }
+
+        class ThrowComparer : IEqualityComparer<int>
+        {
+            private readonly int _err;
+            private readonly Exception _ex;
+
+            public ThrowComparer(int err, Exception ex)
+            {
+                _err = err;
+                _ex = ex;
+            }
+
+            public bool Equals(int x, int y)
+            {
+                return EqualityComparer<int>.Default.Equals(x, y);
+            }
+
+            public int GetHashCode(int obj)
+            {
+                if (obj == _err)
+                    throw _ex;
+
+                return EqualityComparer<int>.Default.GetHashCode(obj);
+            }
+        }
+
+        [Fact]
+        public void Distinct_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnError<int>(380, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnError<int>(380, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 380)
+            );
+        }
+
+        [Fact]
+        public void Distinct_Null()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, default(string)),
+                OnNext(260, "bar"),
+                OnNext(280, "foo"),
+                OnNext(300, default(string)),
+                OnCompleted<string>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Distinct()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "bar"),
+                OnNext(240, default(string)),
+                OnNext(280, "foo"),
+                OnCompleted<string>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+    }
+}

+ 426 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DistinctUntilChangedTest.cs

@@ -0,0 +1,426 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DistinctUntilChangedTest : ReactiveTest
+    {
+
+        [Fact]
+        public void DistinctUntilChanged_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int>(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int>(null, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int>(someObservable, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int, int>(null, _ => _));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int, int>(someObservable, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int, int>(someObservable, _ => _, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int, int>(null, _ => _, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DistinctUntilChanged<int, int>(someObservable, null, EqualityComparer<int>.Default));
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Never<int>();
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_AllChanges()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_AllSame()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 2),
+                OnNext(230, 2),
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_SomeChanges()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2), //*
+                OnNext(215, 3), //*
+                OnNext(220, 3),
+                OnNext(225, 2), //*
+                OnNext(230, 2),
+                OnNext(230, 1), //*
+                OnNext(240, 2), //*
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(225, 2),
+                OnNext(230, 1),
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_Comparer_AllEqual()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged(new FuncComparer<int>((x, y) => true))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_Comparer_AllDifferent()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 2),
+                OnNext(230, 2),
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged(new FuncComparer<int>((x, y) => false))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 2),
+                OnNext(230, 2),
+                OnNext(240, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_KeySelector_Div2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2), //*
+                OnNext(220, 4),
+                OnNext(230, 3), //*
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged(x => x % 2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        class FuncComparer<T> : IEqualityComparer<T>
+        {
+            private Func<T, T, bool> _equals;
+
+            public FuncComparer(Func<T, T, bool> equals)
+            {
+                _equals = equals;
+            }
+
+            public bool Equals(T x, T y)
+            {
+                return _equals(x, y);
+            }
+
+            public int GetHashCode(T obj)
+            {
+                return 0;
+            }
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_KeySelectorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged(new Func<int, int>(x => { throw ex; }))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged(new ThrowComparer<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+        }
+
+        class ThrowComparer<T> : IEqualityComparer<T>
+        {
+            private Exception _ex;
+
+            public ThrowComparer(Exception ex)
+            {
+                _ex = ex;
+            }
+
+            public bool Equals(T x, T y)
+            {
+                throw _ex;
+            }
+
+            public int GetHashCode(T obj)
+            {
+                return 0;
+            }
+        }
+
+        [Fact]
+        public void DistinctUntilChanged_KeySelector_Comparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2), // * key = 1    % 3 = 1
+                OnNext(220, 8), //   key = 4    % 3 = 1   same
+                OnNext(230, 2), //   key = 1    % 3 = 1   same
+                OnNext(240, 5), // * key = 2    % 3 = 2
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.DistinctUntilChanged(x => x / 2, new FuncComparer<int>((x, y) => x % 3 == y % 3))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+    }
+}

+ 737 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DoTest.cs

@@ -0,0 +1,737 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DoTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Do_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, (Action<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(null, _ => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, x => { }, (Action)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, (Action<int>)null, () => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(null, x => { }, () => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, x => { }, (Action<Exception>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, (Action<int>)null, (Exception _) => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(null, x => { }, (Exception _) => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, x => { }, (Exception _) => { }, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, x => { }, (Action<Exception>)null, () => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, (Action<int>)null, (Exception _) => { }, () => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(null, x => { }, (Exception _) => { }, () => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(null, Observer.Create<int>(i => { })));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Do<int>(someObservable, default(IObserver<int>)));
+        }
+
+        [Fact]
+        public void Do_ShouldSeeAllValues()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; sum -= x; })
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_PlainAction()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            int i = 0;
+            var res = scheduler.Start(() =>
+                xs.Do(_ => { i++; })
+            );
+
+            Assert.Equal(4, i);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_NextCompleted()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool completed = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; sum -= x; }, () => { completed = true; })
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.True(completed);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_NextCompleted_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>();
+
+            int i = 0;
+            bool completed = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; }, () => { completed = true; })
+            );
+
+            Assert.Equal(0, i);
+            Assert.False(completed);
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Do_NextError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool sawError = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; sum -= x; }, e => { sawError = e == ex; })
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.True(sawError);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_NextErrorNot()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool sawError = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; sum -= x; }, _ => { sawError = true; })
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.False(sawError);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_NextErrorCompleted()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool sawError = false;
+            bool hasCompleted = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; sum -= x; }, e => { sawError = true; }, () => { hasCompleted = true; })
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.False(sawError);
+            Assert.True(hasCompleted);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_NextErrorCompletedError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool sawError = false;
+            bool hasCompleted = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; sum -= x; }, e => { sawError = e == ex; }, () => { hasCompleted = true; })
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.True(sawError);
+            Assert.False(hasCompleted);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_NextErrorCompletedNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>();
+
+            int i = 0;
+            bool sawError = false;
+            bool hasCompleted = false;
+            var res = scheduler.Start(() =>
+                xs.Do(x => { i++; }, e => { sawError = true; }, () => { hasCompleted = true; })
+            );
+
+            Assert.Equal(0, i);
+            Assert.False(sawError);
+            Assert.False(hasCompleted);
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Do_Observer_SomeDataWithError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool sawError = false;
+            bool hasCompleted = false;
+            var res = scheduler.Start(() =>
+                xs.Do(Observer.Create<int>(x => { i++; sum -= x; }, e => { sawError = e == ex; }, () => { hasCompleted = true; }))
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.True(sawError);
+            Assert.False(hasCompleted);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do_Observer_SomeDataWithoutError()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            int i = 0;
+            int sum = 2 + 3 + 4 + 5;
+            bool sawError = false;
+            bool hasCompleted = false;
+            var res = scheduler.Start(() =>
+                xs.Do(Observer.Create<int>(x => { i++; sum -= x; }, e => { sawError = true; }, () => { hasCompleted = true; }))
+            );
+
+            Assert.Equal(4, i);
+            Assert.Equal(0, sum);
+            Assert.False(sawError);
+            Assert.True(hasCompleted);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do1422_Next_NextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextCompleted_NextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { throw ex; }, () => { })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextCompleted_CompletedThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { }, () => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextError_NextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { throw ex; }, _ => { })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextError_ErrorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+            var ex2 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { }, _ => { throw ex2; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextErrorCompleted_NextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { throw ex; }, _ => { }, () => { })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextErrorCompleted_ErrorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+            var ex2 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { }, _ => { throw ex2; }, () => { })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_NextErrorCompleted_CompletedThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(x => { }, _ => { }, () => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Do1422_Observer_NextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(Observer.Create<int>(x => { throw ex; }, _ => { }, () => { }))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_Observer_ErrorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+            var ex2 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(Observer.Create<int>(x => { }, _ => { throw ex2; }, () => { }))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Do1422_Observer_CompletedThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Do(Observer.Create<int>(x => { }, _ => { }, () => { throw ex; }))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+    }
+}

+ 228 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/DoWhileTest.cs

@@ -0,0 +1,228 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class DoWhileTest : ReactiveTest
+    {
+
+        [Fact]
+        public void DoWhile_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DoWhile(DummyObservable<int>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.DoWhile(default(IObservable<int>), DummyFunc<bool>.Instance));
+        }
+
+        [Fact]
+        public void DoWhile_AlwaysFalse()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(150, 3),
+                OnNext(200, 4),
+                OnCompleted<int>(250)
+            );
+
+            var results = scheduler.Start(() => Observable.DoWhile(xs, () => false));
+
+            results.Messages.AssertEqual(
+                OnNext(250, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(450)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void DoWhile_AlwaysTrue()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(150, 3),
+                OnNext(200, 4),
+                OnCompleted<int>(250)
+            );
+
+            var results = scheduler.Start(() => Observable.DoWhile(xs, () => true));
+
+            results.Messages.AssertEqual(
+                OnNext(250, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnNext(400, 4),
+                OnNext(500, 1),
+                OnNext(550, 2),
+                OnNext(600, 3),
+                OnNext(650, 4),
+                OnNext(750, 1),
+                OnNext(800, 2),
+                OnNext(850, 3),
+                OnNext(900, 4)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450),
+                Subscribe(450, 700),
+                Subscribe(700, 950),
+                Subscribe(950, 1000)
+            );
+        }
+
+        [Fact]
+        public void DoWhile_AlwaysTrue_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnError<int>(50, ex)
+            );
+
+            var results = scheduler.Start(() => Observable.DoWhile(xs, () => true));
+
+            results.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void DoWhile_AlwaysTrue_Infinite()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 1)
+            );
+
+            var results = scheduler.Start(() => Observable.DoWhile(xs, () => true));
+
+            results.Messages.AssertEqual(
+                OnNext(250, 1)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void DoWhile_SometimesTrue()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(150, 3),
+                OnNext(200, 4),
+                OnCompleted<int>(250)
+            );
+
+            int n = 0;
+
+            var results = scheduler.Start(() => Observable.DoWhile(xs, () => ++n < 3));
+
+            results.Messages.AssertEqual(
+                OnNext(250, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnNext(400, 4),
+                OnNext(500, 1),
+                OnNext(550, 2),
+                OnNext(600, 3),
+                OnNext(650, 4),
+                OnNext(750, 1),
+                OnNext(800, 2),
+                OnNext(850, 3),
+                OnNext(900, 4),
+                OnCompleted<int>(950)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450),
+                Subscribe(450, 700),
+                Subscribe(700, 950)
+            );
+        }
+
+        [Fact]
+        public void DoWhile_SometimesThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(150, 3),
+                OnNext(200, 4),
+                OnCompleted<int>(250)
+            );
+
+            int n = 0;
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.DoWhile(xs, () => ++n < 3 ? true : Throw<bool>(ex)));
+
+            results.Messages.AssertEqual(
+                OnNext(250, 1),
+                OnNext(300, 2),
+                OnNext(350, 3),
+                OnNext(400, 4),
+                OnNext(500, 1),
+                OnNext(550, 2),
+                OnNext(600, 3),
+                OnNext(650, 4),
+                OnNext(750, 1),
+                OnNext(800, 2),
+                OnNext(850, 3),
+                OnNext(900, 4),
+                OnError<int>(950, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450),
+                Subscribe(450, 700),
+                Subscribe(700, 950)
+            );
+        }
+
+        static T Throw<T>(Exception ex)
+        {
+            throw ex;
+        }
+    }
+}

+ 135 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ElementAtOrDefaultTest.cs

@@ -0,0 +1,135 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class ElementAtOrDefaultTest : ReactiveTest
+    {
+
+        [Fact]
+        public void ElementAtOrDefault_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ElementAtOrDefault(default(IObservable<int>), 2));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.ElementAtOrDefault(DummyObservable<int>.Instance, -1));
+        }
+
+        [Fact]
+        public void ElementAtOrDefault_First()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnNext(470, 44),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAtOrDefault(0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnCompleted<int>(280)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 280)
+            );
+        }
+
+        [Fact]
+        public void ElementAtOrDefault_Other()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnNext(470, 44),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAtOrDefault(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(470, 44),
+                OnCompleted<int>(470)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+        [Fact]
+        public void ElementAtOrDefault_OutOfRange()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnNext(470, 44),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAtOrDefault(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(600, 0),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void ElementAtOrDefault_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnError<int>(420, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAtOrDefault(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+    }
+}

+ 130 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ElementAtTest.cs

@@ -0,0 +1,130 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class ElementAtTest : ReactiveTest
+    {
+
+        [Fact]
+        public void ElementAt_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ElementAt(default(IObservable<int>), 2));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.ElementAt(DummyObservable<int>.Instance, -1));
+        }
+
+        [Fact]
+        public void ElementAt_First()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnNext(470, 44),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAt(0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 42),
+                OnCompleted<int>(280)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 280)
+            );
+        }
+
+        [Fact]
+        public void ElementAt_Other()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnNext(470, 44),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAt(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(470, 44),
+                OnCompleted<int>(470)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+        [Fact]
+        public void ElementAt_OutOfRange()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnNext(470, 44),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAt(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(600, e => e is ArgumentOutOfRangeException)
+            );
+        }
+
+        [Fact]
+        public void ElementAt_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(280, 42),
+                OnNext(360, 43),
+                OnError<int>(420, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ElementAt(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+    }
+}

+ 109 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/EmptyTest.cs

@@ -0,0 +1,109 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class EmptyTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Empty_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Empty<int>(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Empty<int>(null, 42));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Empty<int>(DummyScheduler.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Empty_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Empty<int>(scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(201)
+            );
+        }
+
+        [Fact]
+        public void Empty_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Empty<int>(scheduler),
+                200
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Empty_ObserverThrows()
+        {
+            var scheduler1 = new TestScheduler();
+
+            var xs = Observable.Empty<int>(scheduler1);
+
+            xs.Subscribe(x => { }, exception => { }, () => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler1.Start());
+        }
+
+        [Fact]
+        public void Empty_DefaultScheduler()
+        {
+            Observable.Empty<int>().AssertEqual(Observable.Empty<int>(DefaultScheduler.Instance));
+        }
+
+        [Fact]
+        public void Empty_Basic_Witness1()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Empty<int>(scheduler, 42)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(201)
+            );
+        }
+
+        [Fact]
+        public void Empty_Basic_Witness2()
+        {
+            var e = new ManualResetEvent(false);
+
+            Observable.Empty<int>(42).Subscribe(
+                _ => { Assert.True(false); },
+                _ => { Assert.True(false); },
+                () => e.Set()
+            );
+
+            e.WaitOne();
+        }
+
+    }
+}

+ 192 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ExpandTest.cs

@@ -0,0 +1,192 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ExpandTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Expand_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Expand(null, DummyFunc<int, IObservable<int>>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Expand(DummyObservable<int>.Instance, null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Expand(DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Expand(null, DummyFunc<int, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Expand(DummyObservable<int>.Instance, null));
+        }
+
+        [Fact]
+        public void Expand_Default()
+        {
+            var b = Observable.Return(1).Expand(x => x < 10 ? Observable.Return(x + 1) : Observable.Empty<int>())
+                        .SequenceEqual(Observable.Range(1, 10)).First();
+
+            Assert.True(b);
+        }
+
+        [Fact]
+        public void Expand_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Expand(x => scheduler.CreateColdObservable(
+                    OnNext(100, 1),
+                    OnNext(200, 2),
+                    OnCompleted<int>(300)
+                ), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(201, 300)
+            );
+        }
+
+        [Fact]
+        public void Expand_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnError<int>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Expand(x => scheduler.CreateColdObservable<int>(
+                    OnNext(100 + x, 2 * x),
+                    OnNext(200 + x, 3 * x),
+                    OnCompleted<int>(300 + x)
+                ), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(201, 300)
+            );
+        }
+
+        [Fact]
+        public void Expand_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Expand(x => scheduler.CreateColdObservable<int>(
+                    OnNext(100 + x, 2 * x),
+                    OnNext(200 + x, 3 * x),
+                    OnCompleted<int>(300 + x)
+                ), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(201, 1000)
+            );
+        }
+
+        [Fact]
+        public void Expand_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(550, 1),
+                OnNext(850, 2),
+                OnCompleted<int>(950)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Expand(x => scheduler.CreateColdObservable(
+                    OnNext(100, 2 * x),
+                    OnNext(200, 3 * x),
+                    OnCompleted<int>(300)
+                ), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(550, 1),
+                OnNext(651, 2),
+                OnNext(751, 3),
+                OnNext(752, 4),
+                OnNext(850, 2),
+                OnNext(852, 6),
+                OnNext(852, 6),
+                OnNext(853, 8),
+                OnNext(951, 4),
+                OnNext(952, 9),
+                OnNext(952, 12),
+                OnNext(953, 12),
+                OnNext(953, 12),
+                OnNext(954, 16)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(201, 950)
+            );
+        }
+
+        [Fact]
+        public void Expand_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(550, 1),
+                OnNext(850, 2),
+                OnCompleted<int>(950)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Expand(x => { throw ex; }, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(550, 1),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(201, 550)
+            );
+        }
+
+    }
+}

+ 157 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FinallyTest.cs

@@ -0,0 +1,157 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class FinallyTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Finally_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Finally<int>(null, () => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Finally<int>(someObservable, null));
+        }
+
+        [Fact]
+        public void Finally_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            bool invoked = false;
+            var res = scheduler.Start(() =>
+                Observable.Never<int>().Finally(() => { invoked = true; })
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            Assert.True(invoked); // due to unsubscribe; see 1356
+        }
+
+        [Fact]
+        public void Finally_OnlyCalledOnce_Never()
+        {
+            int invokeCount = 0;
+            var someObservable = Observable.Never<int>().Finally(() => { invokeCount++; });
+            var d = someObservable.Subscribe();
+            d.Dispose();
+            d.Dispose();
+
+            Assert.Equal(1, invokeCount);
+        }
+
+        [Fact]
+        public void Finally_OnlyCalledOnce_Empty()
+        {
+            var invokeCount = 0;
+            var someObservable = Observable.Empty<int>().Finally(() => { invokeCount++; });
+            var d = someObservable.Subscribe();
+            d.Dispose();
+            d.Dispose();
+
+            Assert.Equal(1, invokeCount);
+        }
+
+        [Fact]
+        public void Finally_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var invoked = false;
+            var res = scheduler.Start(() =>
+                xs.Finally(() => { invoked = true; })
+            );
+
+            Assert.True(invoked);
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Finally_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var invoked = false;
+            var res = scheduler.Start(() =>
+                xs.Finally(() => { invoked = true; })
+            );
+
+            Assert.True(invoked);
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Finally_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var invoked = false;
+            var res = scheduler.Start(() =>
+                xs.Finally(() => { invoked = true; })
+            );
+
+            Assert.True(invoked);
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+    }
+}

+ 240 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstAsyncTest.cs

@@ -0,0 +1,240 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class FirstAsyncTest : ReactiveTest
+    {
+        [Fact]
+        public void FirstAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstAsync(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstAsync(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstAsync(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void FirstAsync_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_Many()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_Predicate()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 3),
+                OnCompleted<int>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_Predicate_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_Predicate_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void FirstAsync_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstAsync(x => { if (x < 4) return false; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 246 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstOrDefaultAsyncTest.cs

@@ -0,0 +1,246 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class FirstOrDefaultAsyncTest : ReactiveTest
+    {
+        [Fact]
+        public void FirstOrDefaultAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstOrDefaultAsync(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstOrDefaultAsync(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstOrDefaultAsync(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_Many()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_Predicate()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 3),
+                OnCompleted<int>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_Predicate_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_Predicate_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void FirstOrDefaultAsync_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.FirstOrDefaultAsync(x => { if (x < 4) return false; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 133 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstOrDefaultTest.cs

@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+
+namespace ReactiveTests.Tests
+{
+    public class FirstOrDefaultTest : ReactiveTest
+    {
+
+        [Fact]
+        public void FirstOrDefault_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstOrDefault(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstOrDefault(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FirstOrDefault(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void FirstOrDefault_Empty()
+        {
+            Assert.Equal(default(int), Observable.Empty<int>().FirstOrDefault());
+        }
+
+        [Fact]
+        public void FirstOrDefaultPredicate_Empty()
+        {
+            Assert.Equal(default(int), Observable.Empty<int>().FirstOrDefault(_ => true));
+        }
+
+        [Fact]
+        public void FirstOrDefault_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).FirstOrDefault());
+        }
+
+        [Fact]
+        public void FirstOrDefault_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.FirstOrDefault());
+        }
+
+        [Fact]
+        public void FirstOrDefault_Range()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Range(value, 10).FirstOrDefault());
+        }
+
+#if !NO_THREAD
+        [Fact]
+        public void FirstOrDefault_NoDoubleSet()
+        {
+            //
+            // Regression test for a possible race condition caused by Return style operators
+            // that could trigger two Set calls on a ManualResetEvent, causing it to get
+            // disposed in between those two calls (cf. FirstOrDefaultInternal). This led
+            // to an exception will the following stack trace:
+            //
+            //    System.ObjectDisposedException: Safe handle has been closed
+            //       at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
+            //       at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
+            //       at Microsoft.Win32.Win32Native.SetEvent(SafeWaitHandle handle)
+            //       at System.Threading.EventWaitHandle.Set()
+            //       at System.Reactive.Linq.QueryLanguage.<>c__DisplayClass458_1`1.<FirstOrDefaultInternal>b__2()
+            //
+
+            var o = new O();
+
+            Scheduler.Default.Schedule(() =>
+            {
+                var x = o.FirstOrDefault();
+            });
+
+            o.Wait();
+
+            o.Next();
+
+            Thread.Sleep(100); // enough time to let the ManualResetEvent dispose
+
+            o.Done();
+        }
+#endif
+
+        class O : IObservable<int>
+        {
+            private readonly ManualResetEvent _event = new ManualResetEvent(false);
+            private IObserver<int> _observer;
+
+            public void Wait()
+            {
+                _event.WaitOne();
+            }
+
+            public void Next()
+            {
+                _observer.OnNext(42);
+            }
+
+            public void Done()
+            {
+                _observer.OnCompleted();
+            }
+
+            public IDisposable Subscribe(IObserver<int> observer)
+            {
+                _observer = observer;
+                _event.Set();
+                return Disposable.Empty;
+            }
+        }
+
+    }
+}

+ 99 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FirstTest.cs

@@ -0,0 +1,99 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class FirstTest : ReactiveTest
+    {
+
+        [Fact]
+        public void First_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.First(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.First(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.First(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void First_Empty()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Empty<int>().First());
+        }
+
+        [Fact]
+        public void FirstPredicate_Empty()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Empty<int>().First(_ => true));
+        }
+
+        [Fact]
+        public void First_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).First());
+        }
+
+        [Fact]
+        public void FirstPredicate_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).First(i => i % 2 == 0));
+        }
+
+        [Fact]
+        public void FirstPredicate_Return_NoMatch()
+        {
+            var value = 42;
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Return<int>(value).First(i => i % 2 != 0));
+        }
+
+        [Fact]
+        public void First_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.First());
+        }
+
+        [Fact]
+        public void FirstPredicate_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.First(_ => true));
+        }
+
+        [Fact]
+        public void First_Range()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Range(value, 10).First());
+        }
+
+        [Fact]
+        public void FirstPredicate_Range()
+        {
+            var value = 42;
+            Assert.Equal(46, Observable.Range(value, 10).First(i => i > 45));
+        }
+
+    }
+}

+ 576 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForEachAsyncTest.cs

@@ -0,0 +1,576 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ForEachAsyncTest : ReactiveTest
+    {
+
+        [Fact]
+        public void ForEachAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(default(IObservable<int>), x => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(Observable.Never<int>(), default(Action<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(default(IObservable<int>), x => { }, CancellationToken.None));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(Observable.Never<int>(), default(Action<int>), CancellationToken.None));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(default(IObservable<int>), (x, i) => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(Observable.Never<int>(), default(Action<int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(default(IObservable<int>), (x, i) => { }, CancellationToken.None));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEachAsync(Observable.Never<int>(), default(Action<int, int>), CancellationToken.None));
+        }
+
+        [Fact]
+        public void ForEachAsync_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x => list.Add(new Recorded<int>(scheduler.Clock, x)), cts.Token));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(150)
+            );
+
+            list.AssertEqual(
+                new Recorded<int>(200, 2),
+                new Recorded<int>(300, 3),
+                new Recorded<int>(400, 4),
+                new Recorded<int>(500, 5)
+            );
+
+            Assert.Equal(TaskStatus.WaitingForActivation, task.Status);
+        }
+
+        [Fact]
+        public void ForEachAsync_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnCompleted<int>(600)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x => list.Add(new Recorded<int>(scheduler.Clock, x)), cts.Token));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(150, 600)
+            );
+
+            list.AssertEqual(
+                new Recorded<int>(200, 2),
+                new Recorded<int>(300, 3),
+                new Recorded<int>(400, 4),
+                new Recorded<int>(500, 5)
+            );
+
+            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
+        }
+
+        [Fact]
+        public void ForEachAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var exception = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnError<int>(600, exception)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x => list.Add(new Recorded<int>(scheduler.Clock, x)), cts.Token));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(150, 600)
+            );
+
+            list.AssertEqual(
+                new Recorded<int>(200, 2),
+                new Recorded<int>(300, 3),
+                new Recorded<int>(400, 4),
+                new Recorded<int>(500, 5)
+            );
+
+            Assert.Equal(TaskStatus.Faulted, task.Status);
+            Assert.Same(exception, task.Exception.InnerException);
+        }
+
+        [Fact]
+        public void ForEachAsync_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var exception = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnCompleted<int>(600)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x =>
+            {
+                if (scheduler.Clock > 400)
+                    throw exception;
+                list.Add(new Recorded<int>(scheduler.Clock, x));
+            }, cts.Token));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(150, 500)
+            );
+
+            list.AssertEqual(
+                new Recorded<int>(200, 2),
+                new Recorded<int>(300, 3),
+                new Recorded<int>(400, 4)
+            );
+
+            Assert.Equal(TaskStatus.Faulted, task.Status);
+            Assert.Same(exception, task.Exception.InnerException);
+        }
+
+        [Fact]
+        public void ForEachAsync_CancelDuring()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnCompleted<int>(600)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x => list.Add(new Recorded<int>(scheduler.Clock, x)), cts.Token));
+            scheduler.ScheduleAbsolute(350, () => cts.Cancel());
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(150, 350)
+            );
+
+            list.AssertEqual(
+                new Recorded<int>(200, 2),
+                new Recorded<int>(300, 3)
+            );
+
+            Assert.Equal(TaskStatus.Canceled, task.Status);
+        }
+
+        [Fact]
+        public void ForEachAsync_CancelBefore()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnCompleted<int>(600)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            cts.Cancel();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x => list.Add(new Recorded<int>(scheduler.Clock, x)), cts.Token));
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            list.AssertEqual(
+            );
+
+            Assert.Equal(TaskStatus.Canceled, task.Status);
+        }
+
+        [Fact]
+        public void ForEachAsync_CancelAfter()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 4),
+                OnNext(500, 5),
+                OnCompleted<int>(600)
+            );
+
+            var task = default(Task);
+            var cts = new CancellationTokenSource();
+            var list = new List<Recorded<int>>();
+
+            scheduler.ScheduleAbsolute(150, () => task = xs.ForEachAsync(x => list.Add(new Recorded<int>(scheduler.Clock, x)), cts.Token));
+            scheduler.ScheduleAbsolute(700, () => cts.Cancel());
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(150, 600)
+            );
+
+            list.AssertEqual(
+                new Recorded<int>(200, 2),
+                new Recorded<int>(300, 3),
+                new Recorded<int>(400, 4),
+                new Recorded<int>(500, 5)
+            );
+
+            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
+        }
+
+        [Fact]
+        public void ForEachAsync_Default()
+        {
+            var list = new List<int>();
+            Observable.Range(1, 10).ForEachAsync(list.Add).Wait();
+            list.AssertEqual(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+        }
+
+        [Fact]
+        public void ForEachAsync_Index()
+        {
+            var list = new List<int>();
+            Observable.Range(3, 5).ForEachAsync((x, i) => list.Add(x * i)).Wait();
+            list.AssertEqual(3 * 0, 4 * 1, 5 * 2, 6 * 3, 7 * 4);
+        }
+
+        [Fact]
+        public void ForEachAsync_Default_Cancel()
+        {
+            var N = 10;
+
+            for (int n = 0; n < N; n++)
+            {
+                var cts = new CancellationTokenSource();
+                var done = false;
+
+                var xs = Observable.Create<int>(observer =>
+                {
+                    return new CompositeDisposable(
+                        Observable.Repeat(42, Scheduler.Default).Subscribe(observer),
+                        Disposable.Create(() => done = true)
+                    );
+                });
+
+                var lst = new List<int>();
+
+                var t = xs.ForEachAsync(
+                    x =>
+                    {
+                        lock (lst)
+                            lst.Add(x);
+                    },
+                    cts.Token
+                );
+
+                while (true)
+                {
+                    lock (lst)
+                        if (lst.Count >= 10)
+                            break;
+                }
+
+                cts.Cancel();
+
+                while (!t.IsCompleted)
+                    ;
+
+                for (int i = 0; i < 10; i++)
+                    Assert.Equal(42, lst[i]);
+
+                Assert.True(done);
+                Assert.True(t.IsCanceled);
+            }
+        }
+
+        [Fact]
+        public void ForEachAsync_Index_Cancel()
+        {
+            var N = 10;
+
+            for (int n = 0; n < N; n++)
+            {
+                var cts = new CancellationTokenSource();
+                var done = false;
+
+                var xs = Observable.Create<int>(observer =>
+                {
+                    return new CompositeDisposable(
+                        Observable.Repeat(42, Scheduler.Default).Subscribe(observer),
+                        Disposable.Create(() => done = true)
+                    );
+                });
+
+                var lst = new List<int>();
+
+                var t = xs.ForEachAsync(
+                    (x, i) =>
+                    {
+                        lock (lst)
+                            lst.Add(x * i);
+                    },
+                    cts.Token
+                );
+
+                while (true)
+                {
+                    lock (lst)
+                        if (lst.Count >= 10)
+                            break;
+                }
+
+                cts.Cancel();
+
+                while (!t.IsCompleted)
+                    ;
+
+                for (int i = 0; i < 10; i++)
+                    Assert.Equal(i * 42, lst[i]);
+
+                Assert.True(done);
+                Assert.True(t.IsCanceled);
+            }
+        }
+
+        [Fact]
+        public void ForEachAsync_DisposeThrows1()
+        {
+            var cts = new CancellationTokenSource();
+            var ex = new Exception();
+
+            var xs = Observable.Create<int>(observer =>
+            {
+                return new CompositeDisposable(
+                    Observable.Range(0, 10, Scheduler.CurrentThread).Subscribe(observer),
+                    Disposable.Create(() => { throw ex; })
+                );
+            });
+
+            var lst = new List<int>();
+            var t = xs.ForEachAsync(lst.Add, cts.Token);
+
+            //
+            // Unfortunately, this doesn't throw for CurrentThread scheduling. The
+            // subscription completes prior to assignment of the disposable, so we
+            // succeed calling the TrySetResult method for the OnCompleted handler
+            // prior to observing the exception of the Dispose operation, which is
+            // surfacing upon assignment to the SingleAssignmentDisposable. As a
+            // result, the exception evaporates.
+            //
+            // It'd be a breaking change at this point to rethrow the exception in
+            // that case, so we're merely asserting regressions here.
+            //
+            try
+            {
+                t.Wait();
+            }
+            catch
+            {
+                Assert.True(false);
+            }
+        }
+
+        [Fact]
+        public void ForEachAsync_DisposeThrows2()
+        {
+            var cts = new CancellationTokenSource();
+            var ex = new Exception();
+
+            var xs = Observable.Create<int>(observer =>
+            {
+                return new CompositeDisposable(
+                    Observable.Range(0, 10, Scheduler.CurrentThread).Subscribe(observer),
+                    Disposable.Create(() => { throw ex; })
+                );
+            });
+
+            var lst = new List<int>();
+
+            var t = default(Task);
+
+            Scheduler.CurrentThread.Schedule(() =>
+            {
+                t = xs.ForEachAsync(lst.Add, cts.Token);
+            });
+
+            //
+            // If the trampoline of the CurrentThread has been installed higher
+            // up the stack, the assignment of the subscription's disposable to
+            // the SingleAssignmentDisposable can complete prior to the Dispose
+            // method being called from the OnCompleted handler. In this case,
+            // the OnCompleted handler's invocation of Dispose will cause the
+            // exception to occur, and it bubbles out through TrySetException.
+            //
+            try
+            {
+                t.Wait();
+            }
+            catch (AggregateException err)
+            {
+                Assert.Equal(1, err.InnerExceptions.Count);
+                Assert.Same(ex, err.InnerExceptions[0]);
+            }
+        }
+
+#if !NO_THREAD
+        [Fact]
+        [Trait("SkipCI", "true")]
+        public void ForEachAsync_DisposeThrows()
+        {
+            //
+            // Unfortunately, this test is non-deterministic due to the race
+            // conditions described above in the tests using the CurrentThread
+            // scheduler. The exception can come out through the OnCompleted
+            // handler but can equally well get swallowed if the main thread
+            // hasn't reached the assignment of the disposable yet, causing
+            // the OnCompleted handler to win the race. The user can deal with
+            // this by hooking an exception handler to the scheduler, so we
+            // assert this behavior here.
+            //
+            // It'd be a breaking change at this point to change rethrowing
+            // behavior, so we're merely asserting regressions here.
+            //
+
+            var hasCaughtEscapingException = 0;
+
+            var cts = new CancellationTokenSource();
+            var ex = new Exception();
+
+            var s = Scheduler.Default.Catch<Exception>(err =>
+            {
+                Volatile.Write(ref hasCaughtEscapingException, 1);
+                return ex == err;
+            });
+
+            while (Volatile.Read(ref hasCaughtEscapingException) == 0)
+            {
+                var xs = Observable.Create<int>(observer =>
+                {
+                    return new CompositeDisposable(
+                        Observable.Range(0, 10, s).Subscribe(observer),
+                        Disposable.Create(() => { throw ex; })
+                    );
+                });
+
+                var lst = new List<int>();
+                var t = xs.ForEachAsync(lst.Add, cts.Token);
+
+                try
+                {
+                    t.Wait();
+                }
+                catch (AggregateException err)
+                {
+                    Assert.Equal(1, err.InnerExceptions.Count);
+                    Assert.Same(ex, err.InnerExceptions[0]);
+                }
+            }
+        }
+
+        [Fact]
+        public void ForEachAsync_SubscribeThrows()
+        {
+            var ex = new Exception();
+
+            var x = 42;
+            var xs = Observable.Create<int>(observer =>
+            {
+                if (x == 42)
+                    throw ex;
+                return Disposable.Empty;
+            });
+
+            var t = xs.ForEachAsync(_ => { });
+
+            try
+            {
+                t.Wait();
+                Assert.True(false);
+            }
+            catch (AggregateException err)
+            {
+                Assert.Equal(1, err.InnerExceptions.Count);
+                Assert.Same(ex, err.InnerExceptions[0]);
+            }
+        }
+#endif
+
+    }
+}

+ 128 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForEachTest.cs

@@ -0,0 +1,128 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class ForEachTest : ReactiveTest
+    {
+
+        [Fact]
+        public void ForEach_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEach(default(IObservable<int>), x => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEach(someObservable, default(Action<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEach(default(IObservable<int>), (x, i) => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ForEach(someObservable, default(Action<int, int>)));
+        }
+
+        [Fact]
+        public void ForEach_Empty()
+        {
+            var lst = new List<int>();
+            Observable.Empty<int>().ForEach(x => lst.Add(x));
+            Assert.True(lst.SequenceEqual(Enumerable.Empty<int>()));
+        }
+
+        [Fact]
+        public void ForEach_Index_Empty()
+        {
+            var lstX = new List<int>();
+            Observable.Empty<int>().ForEach((x, i) => lstX.Add(x));
+            Assert.True(lstX.SequenceEqual(Enumerable.Empty<int>()));
+        }
+
+        [Fact]
+        public void ForEach_Return()
+        {
+            var lst = new List<int>();
+            Observable.Return(42).ForEach(x => lst.Add(x));
+            Assert.True(lst.SequenceEqual(new[] { 42 }));
+        }
+
+        [Fact]
+        public void ForEach_Index_Return()
+        {
+            var lstX = new List<int>();
+            var lstI = new List<int>();
+            Observable.Return(42).ForEach((x, i) => { lstX.Add(x); lstI.Add(i); });
+            Assert.True(lstX.SequenceEqual(new[] { 42 }));
+            Assert.True(lstI.SequenceEqual(new[] { 0 }));
+        }
+
+        [Fact]
+        public void ForEach_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.ForEach(x => { Assert.True(false); }));
+        }
+
+        [Fact]
+        public void ForEach_Index_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.ForEach((x, i) => { Assert.True(false); }));
+        }
+
+        [Fact]
+        public void ForEach_SomeData()
+        {
+            var lstX = new List<int>();
+            Observable.Range(10, 10).ForEach(x => lstX.Add(x));
+            Assert.True(lstX.SequenceEqual(Enumerable.Range(10, 10)));
+        }
+
+        [Fact]
+        public void ForEach_Index_SomeData()
+        {
+            var lstX = new List<int>();
+            var lstI = new List<int>();
+            Observable.Range(10, 10).ForEach((x, i) => { lstX.Add(x); lstI.Add(i); });
+            Assert.True(lstX.SequenceEqual(Enumerable.Range(10, 10)));
+            Assert.True(lstI.SequenceEqual(Enumerable.Range(0, 10)));
+        }
+
+        [Fact]
+        public void ForEach_OnNextThrows()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Range(0, 10);
+
+            ReactiveAssert.Throws(ex, () => xs.ForEach(x => { throw ex; }));
+        }
+
+        [Fact]
+        public void ForEach_Index_OnNextThrows()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Range(0, 10);
+
+            ReactiveAssert.Throws(ex, () => xs.ForEach((x, i) => { throw ex; }));
+        }
+
+    }
+}

+ 126 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForTest.cs

@@ -0,0 +1,126 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ForTest : ReactiveTest
+    {
+
+        [Fact]
+        public void For_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.For(DummyEnumerable<int>.Instance, default(Func<int, IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.For(null, DummyFunc<int, IObservable<int>>.Instance));
+        }
+
+        [Fact]
+        public void For_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var results = scheduler.Start(() => Observable.For(new[] { 1, 2, 3 }, x => scheduler.CreateColdObservable(
+                OnNext<int>((ushort)(x * 100 + 10), x * 10 + 1),
+                OnNext<int>((ushort)(x * 100 + 20), x * 10 + 2),
+                OnNext<int>((ushort)(x * 100 + 30), x * 10 + 3),
+                OnCompleted<int>((ushort)(x * 100 + 40)))));
+
+            results.Messages.AssertEqual(
+                OnNext(310, 11),
+                OnNext(320, 12),
+                OnNext(330, 13),
+                OnNext(550, 21),
+                OnNext(560, 22),
+                OnNext(570, 23),
+                OnNext(890, 31),
+                OnNext(900, 32),
+                OnNext(910, 33),
+                OnCompleted<int>(920)
+            );
+        }
+
+        IEnumerable<int> For_Error_Core(Exception ex)
+        {
+            yield return 1;
+            yield return 2;
+            yield return 3;
+            throw ex;
+        }
+
+        [Fact]
+        public void For_Error_Iterator()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.For(For_Error_Core(ex), x => scheduler.CreateColdObservable(
+                OnNext<int>((ushort)(x * 100 + 10), x * 10 + 1),
+                OnNext<int>((ushort)(x * 100 + 20), x * 10 + 2),
+                OnNext<int>((ushort)(x * 100 + 30), x * 10 + 3),
+                OnCompleted<int>((ushort)(x * 100 + 40)))));
+
+            results.Messages.AssertEqual(
+                OnNext(310, 11),
+                OnNext(320, 12),
+                OnNext(330, 13),
+                OnNext(550, 21),
+                OnNext(560, 22),
+                OnNext(570, 23),
+                OnNext(890, 31),
+                OnNext(900, 32),
+                OnNext(910, 33),
+                OnError<int>(920, ex)
+            );
+        }
+
+        [Fact]
+        public void For_Error_Source()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.For(new[] { 1, 2, 3 }, x => Observable.Throw<int>(ex)));
+
+            results.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void For_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.For(new[] { 1, 2, 3 }, x => Throw<IObservable<int>>(ex)));
+
+            results.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        static T Throw<T>(Exception ex)
+        {
+            throw ex;
+        }
+    }
+}

+ 638 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ForkJoinTest.cs

@@ -0,0 +1,638 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ForkJoinTest : ReactiveTest
+    {
+
+        [Fact]
+        public void ForkJoin_ArgumentChecking()
+        {
+            var someObservable = DummyObservable<int>.Instance;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ForkJoin(someObservable, someObservable, (Func<int, int, int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ForkJoin(someObservable, (IObservable<int>)null, (_, __) => _ + __));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ForkJoin((IObservable<int>)null, someObservable, (_, __) => _ + __));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ForkJoin((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ForkJoin((IEnumerable<IObservable<int>>)null));
+        }
+
+        [Fact]
+        public void ForkJoin_EmptyEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin<int>());
+            res.Messages.AssertEqual(
+                OnCompleted<int[]>(200)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_EmptyReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_ReturnEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_ReturnReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnNext(250, 2 + 3),
+                OnCompleted<int>(250)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_EmptyThrow()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnError<int>(210, ex),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_ThrowEmpty()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnError<int>(210, ex),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_ReturnThrow()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnError<int>(220, ex),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_ThrowReturn()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnError<int>(220, ex),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_Binary()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            };
+
+            var o = scheduler.CreateHotObservable(msgs1);
+            var e = scheduler.CreateHotObservable(msgs2);
+
+            var res = scheduler.Start(() => e.ForkJoin(o, (x, y) => x + y));
+            res.Messages.AssertEqual(
+                OnNext(250, 4 + 7),   // TODO: fix ForkJoin behavior
+                OnCompleted<int>(250)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_NaryParams()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            };
+
+            var msgs3 = new[] {
+                OnNext(150, 1),
+                OnNext(230, 3),
+                OnNext(245, 5),
+                OnCompleted<int>(270)
+            };
+
+            var o1 = scheduler.CreateHotObservable(msgs1);
+            var o2 = scheduler.CreateHotObservable(msgs2);
+            var o3 = scheduler.CreateHotObservable(msgs3);
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin(o1, o2, o3));
+
+            res.Messages.AssertEqual(
+                OnNext<int[]>(270, l => l.SequenceEqual(new[] { 4, 7, 5 })), // TODO: fix ForkJoin behavior
+                OnCompleted<int[]>(270)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_NaryParamsEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            };
+
+            var msgs3 = new[] {
+                OnCompleted<int>(270)
+            };
+
+            var o1 = scheduler.CreateHotObservable(msgs1);
+            var o2 = scheduler.CreateHotObservable(msgs2);
+            var o3 = scheduler.CreateHotObservable(msgs3);
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin(o1, o2, o3));
+
+            res.Messages.AssertEqual(
+                OnCompleted<int[]>(270)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_NaryParamsEmptyBeforeEnd()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnCompleted<int>(235)
+            };
+
+            var msgs3 = new[] {
+                OnNext(150, 1),
+                OnNext(230, 3),
+                OnNext(245, 5),
+                OnCompleted<int>(270)
+            };
+
+            var o1 = scheduler.CreateHotObservable(msgs1);
+            var o2 = scheduler.CreateHotObservable(msgs2);
+            var o3 = scheduler.CreateHotObservable(msgs3);
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin(o1, o2, o3));
+
+            res.Messages.AssertEqual(
+                OnCompleted<int[]>(235)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_Nary_Immediate()
+        {
+            ObservableEx.ForkJoin(Observable.Return(1), Observable.Return(2)).First().SequenceEqual(new[] { 1, 2 });
+        }
+
+        [Fact]
+        public void ForkJoin_Nary_Virtual_And_Immediate()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            };
+
+            var msgs3 = new[] {
+                OnNext(150, 1),
+                OnNext(230, 3),
+                OnNext(245, 5),
+                OnCompleted<int>(270)
+            };
+
+            var o1 = scheduler.CreateHotObservable(msgs1);
+            var o2 = scheduler.CreateHotObservable(msgs2);
+            var o3 = scheduler.CreateHotObservable(msgs3);
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin(new List<IObservable<int>> { o1, o2, o3, Observable.Return(20) }));
+
+            res.Messages.AssertEqual(
+                OnNext<int[]>(270, l => l.SequenceEqual(new[] { 4, 7, 5, 20 })),
+                OnCompleted<int[]>(270)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_Nary_Immediate_And_Virtual()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            };
+
+            var msgs3 = new[] {
+                OnNext(150, 1),
+                OnNext(230, 3),
+                OnNext(245, 5),
+                OnCompleted<int>(270)
+            };
+
+            var o1 = scheduler.CreateHotObservable(msgs1);
+            var o2 = scheduler.CreateHotObservable(msgs2);
+            var o3 = scheduler.CreateHotObservable(msgs3);
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin(new List<IObservable<int>> { Observable.Return(20), o1, o2, o3 }));
+
+            res.Messages.AssertEqual(
+                OnNext<int[]>(270, l => l.SequenceEqual(new[] { 20, 4, 7, 5 })),
+                OnCompleted<int[]>(270)
+            );
+        }
+
+        [Fact]
+        public void ForkJoin_Nary()
+        {
+            var scheduler = new TestScheduler();
+
+            var msgs1 = new[] {
+                OnNext(150, 1),
+                OnNext(215, 2),
+                OnNext(225, 4),
+                OnCompleted<int>(230)
+            };
+
+            var msgs2 = new[] {
+                OnNext(150, 1),
+                OnNext(235, 6),
+                OnNext(240, 7),
+                OnCompleted<int>(250)
+            };
+
+            var msgs3 = new[] {
+                OnNext(150, 1),
+                OnNext(230, 3),
+                OnNext(245, 5),
+                OnCompleted<int>(270)
+            };
+
+            var o1 = scheduler.CreateHotObservable(msgs1);
+            var o2 = scheduler.CreateHotObservable(msgs2);
+            var o3 = scheduler.CreateHotObservable(msgs3);
+
+            var res = scheduler.Start(() => ObservableEx.ForkJoin(new List<IObservable<int>> { o1, o2, o3 }));
+
+            res.Messages.AssertEqual(
+                OnNext<int[]>(270, l => l.SequenceEqual(new[] { 4, 7, 5 })), // TODO: fix ForkJoin behavior
+                OnCompleted<int[]>(270)
+            );
+        }
+
+        [Fact]
+        public void Bug_1302_SelectorThrows_LeftLast()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnCompleted<int>(220)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, 2),
+                OnCompleted<int>(217)
+            );
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => xs.ForkJoin<int, int, int>(ys, (x, y) => { throw ex; }));
+
+            results.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 217)
+            );
+        }
+
+        [Fact]
+        public void Bug_1302_SelectorThrows_RightLast()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnCompleted<int>(217)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => xs.ForkJoin<int, int, int>(ys, (x, y) => { throw ex; }));
+
+            results.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 217)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Bug_1302_RightLast_NoLeft()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnCompleted<int>(217)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var results = scheduler.Start(() => xs.ForkJoin<int, int, int>(ys, (x, y) => x + y));
+
+            results.Messages.AssertEqual(
+                OnCompleted<int>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 217)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Bug_1302_RightLast_NoRight()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(215, 2),
+                OnCompleted<int>(217)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnCompleted<int>(220)
+            );
+
+            var results = scheduler.Start(() => xs.ForkJoin<int, int, int>(ys, (x, y) => x + y));
+
+            results.Messages.AssertEqual(
+                OnCompleted<int>(220)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 217)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+    }
+}

+ 1192 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromAsyncPatternTest.cs

@@ -0,0 +1,1192 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class FromAsyncPatternTest : ReactiveTest
+    {
+        private Task<int> doneTask;
+
+        public FromAsyncPatternTest()
+        {
+            var tcs = new TaskCompletionSource<int>();
+            tcs.SetResult(42);
+            doneTask = tcs.Task;
+        }
+
+        [Fact]
+        public void FromAsyncPattern_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(null, iar => 0));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern((cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int>((cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int>((a, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int>((a, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int>((a, b, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int>((a, b, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int>((a, b, c, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int>((a, b, c, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int>((a, b, c, d, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int>((a, b, c, d, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int>((a, b, c, d, e, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int>((a, b, c, d, e, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int>((a, b, c, d, e, f, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int>((a, b, c, d, e, f, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int>((a, b, c, d, e, f, g, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, l, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, l, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, l, m, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, l, m, cb, o) => null, default(Func<IAsyncResult, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, l, m, n, cb, o) => null, default(Action<IAsyncResult>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsyncPattern<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>((a, b, c, d, e, f, g, h, i, j, k, l, m, n, cb, o) => null, default(Func<IAsyncResult, int>)));
+        }
+
+        [Fact]
+        public void FromAsyncPattern0()
+        {
+            var x = new Result();
+
+            Func<AsyncCallback, object, IAsyncResult> begin = (cb, _) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)().Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction0()
+        {
+            var x = new Result();
+
+            Func<AsyncCallback, object, IAsyncResult> begin = (cb, _) => { cb(x); return x; };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern0_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<AsyncCallback, object, IAsyncResult> begin = (cb, _) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)().Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern0_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<AsyncCallback, object, IAsyncResult> begin = (cb, _) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)().Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern1()
+        {
+            var x = new Result();
+
+            Func<int, AsyncCallback, object, IAsyncResult> begin = (a, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction1()
+        {
+            var x = new Result();
+
+            Func<int, AsyncCallback, object, IAsyncResult> begin = (a, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern1_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, AsyncCallback, object, IAsyncResult> begin = (a, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern1_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, AsyncCallback, object, IAsyncResult> begin = (a, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern2()
+        {
+            var x = new Result();
+
+            Func<int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction2()
+        {
+            var x = new Result();
+
+            Func<int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern2_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern2_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern3()
+        {
+            var x = new Result();
+
+            Func<int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+        [Fact]
+        public void FromAsyncPatternAction3()
+        {
+            var x = new Result();
+
+            Func<int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern3_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern3_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern4()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction4()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern4_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern4_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern5()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction5()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern5_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern5_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern6()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction6()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern6_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern6_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern7()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction7()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern7_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern7_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern8()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction8()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern8_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern8_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern9()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction9()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern9_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern9_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern10()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction10()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern10_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern10_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern11()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction11()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern11_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern11_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern12()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                Assert.Equal(l, 13);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction12()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                Assert.Equal(l, 13);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern12_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern12_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern13()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                Assert.Equal(l, 13);
+                Assert.Equal(m, 14);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction13()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                Assert.Equal(l, 13);
+                Assert.Equal(m, 14);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern13_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern13_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern14()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                Assert.Equal(l, 13);
+                Assert.Equal(m, 14);
+                Assert.Equal(n, 15);
+                cb(x);
+                return x;
+            };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 1; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnNext<int>(1), Notification.CreateOnCompleted<int>() }));
+        }
+
+        [Fact]
+        public void FromAsyncPatternAction14()
+        {
+            var x = new Result();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, cb, _) =>
+            {
+                Assert.Equal(a, 2);
+                Assert.Equal(b, 3);
+                Assert.Equal(c, 4);
+                Assert.Equal(d, 5);
+                Assert.Equal(e, 6);
+                Assert.Equal(f, 7);
+                Assert.Equal(g, 8);
+                Assert.Equal(h, 9);
+                Assert.Equal(i, 10);
+                Assert.Equal(j, 11);
+                Assert.Equal(k, 12);
+                Assert.Equal(l, 13);
+                Assert.Equal(m, 14);
+                Assert.Equal(n, 15);
+                cb(x);
+                return x;
+            };
+            Action<IAsyncResult> end = iar => { Assert.Same(x, iar); };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new[] { new Unit() }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern14_Error()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, cb, o) => { cb(x); return x; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); throw ex; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        [Fact]
+        public void FromAsyncPattern14_ErrorBegin()
+        {
+            var x = new Result();
+            var ex = new Exception();
+
+            Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult> begin = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, cb, o) => { cb(x); throw ex; };
+            Func<IAsyncResult, int> end = iar => { Assert.Same(x, iar); return 0; };
+
+            var res = Observable.FromAsyncPattern(begin, end)(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).Materialize().ToEnumerable().ToArray();
+            Assert.True(res.SequenceEqual(new Notification<int>[] { Notification.CreateOnError<int>(ex) }));
+        }
+
+        class Result : IAsyncResult
+        {
+            public object AsyncState
+            {
+                get { throw new NotImplementedException(); }
+            }
+
+            public System.Threading.WaitHandle AsyncWaitHandle
+            {
+                get { throw new NotImplementedException(); }
+            }
+
+            public bool CompletedSynchronously
+            {
+                get { throw new NotImplementedException(); }
+            }
+
+            public bool IsCompleted
+            {
+                get { throw new NotImplementedException(); }
+            }
+        }
+
+    }
+}

+ 402 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromAsyncTest.cs

@@ -0,0 +1,402 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Threading;
+
+namespace ReactiveTests.Tests
+{
+    public class FromAsyncTest : ReactiveTest
+    {
+        private Task<int> doneTask;
+
+        public FromAsyncTest()
+        {
+            var tcs = new TaskCompletionSource<int>();
+            tcs.SetResult(42);
+            doneTask = tcs.Task;
+        }
+
+        #region Func
+
+        [Fact]
+        public void FromAsync_Func_ArgumentChecking()
+        {
+            var s = Scheduler.Immediate;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync<int>(default(Func<Task<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync<int>(default(Func<CancellationToken, Task<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync<int>(default(Func<Task<int>>), s));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync<int>(() => doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync<int>(default(Func<CancellationToken, Task<int>>), s));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync<int>(ct => doneTask, default(IScheduler)));
+        }
+
+        [Fact]
+        public void FromAsync_Func_Success()
+        {
+            var n = 42;
+
+            var i = 0;
+            var xs = Observable.FromAsync(() =>
+            {
+                i++;
+                return Task.Factory.StartNew(() => n);
+            });
+
+            Assert.Equal(n, xs.Single());
+            Assert.Equal(1, i);
+
+            Assert.Equal(n, xs.Single());
+            Assert.Equal(2, i);
+        }
+
+        [Fact]
+        public void FromAsync_Func_Throw_Synchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync<int>(() =>
+            {
+                throw ex;
+            });
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_Func_Throw_Asynchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync<int>(() =>
+                Task.Factory.StartNew<int>(() => { throw ex; })
+            );
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_FuncWithCancel_Success()
+        {
+            var n = 42;
+
+            var i = 0;
+            var xs = Observable.FromAsync(ct =>
+            {
+                i++;
+                return Task.Factory.StartNew(() => n);
+            });
+
+            Assert.Equal(n, xs.Single());
+            Assert.Equal(1, i);
+
+            Assert.Equal(n, xs.Single());
+            Assert.Equal(2, i);
+        }
+
+        [Fact]
+        public void FromAsync_FuncWithCancel_Throw_Synchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync<int>(ct =>
+            {
+                throw ex;
+            });
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_FuncWithCancel_Throw_Asynchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync<int>(ct =>
+                Task.Factory.StartNew<int>(() => { throw ex; })
+            );
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_FuncWithCancel_Cancel()
+        {
+            var e = new ManualResetEvent(false);
+            var f = new ManualResetEvent(false);
+
+            var t = default(Task<int>);
+            var xs = Observable.FromAsync(ct =>
+                t = Task.Factory.StartNew<int>(() =>
+                {
+                    try
+                    {
+                        e.Set();
+                        while (true)
+                            ct.ThrowIfCancellationRequested();
+                    }
+                    finally
+                    {
+                        f.Set();
+                    }
+                })
+            );
+
+            var d = xs.Subscribe(_ => { });
+            e.WaitOne();
+            d.Dispose();
+
+            f.WaitOne();
+            while (!t.IsCompleted)
+                ;
+        }
+
+#if DESKTOPCLR
+        [Fact]
+        public void FromAsync_Func_Scheduler1()
+        {
+            var e = new ManualResetEvent(false);
+            var x = default(int);
+            var t = default(int);
+
+            var tcs = new TaskCompletionSource<int>();
+
+            var xs = Observable.FromAsync(() => tcs.Task, Scheduler.Immediate);
+            xs.Subscribe(res =>
+            {
+                x = res;
+                t = Thread.CurrentThread.ManagedThreadId;
+                e.Set();
+            });
+
+            tcs.SetResult(42);
+
+            e.WaitOne();
+
+            Assert.Equal(42, x);
+            Assert.Equal(Thread.CurrentThread.ManagedThreadId, t);
+        }
+
+        [Fact]
+        public void FromAsync_Func_Scheduler2()
+        {
+            var e = new ManualResetEvent(false);
+            var x = default(int);
+            var t = default(int);
+
+            var tcs = new TaskCompletionSource<int>();
+
+            var xs = Observable.FromAsync(ct => tcs.Task, Scheduler.Immediate);
+            xs.Subscribe(res =>
+            {
+                x = res;
+                t = Thread.CurrentThread.ManagedThreadId;
+                e.Set();
+            });
+
+            tcs.SetResult(42);
+
+            e.WaitOne();
+
+            Assert.Equal(42, x);
+            Assert.Equal(Thread.CurrentThread.ManagedThreadId, t);
+        }
+#endif
+
+        #endregion
+
+        #region Action
+
+        [Fact]
+        public void FromAsync_Action_ArgumentChecking()
+        {
+            var s = Scheduler.Immediate;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task>), s));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(() => (Task)doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task>), s));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => (Task)doneTask, default(IScheduler)));
+        }
+
+        [Fact]
+        public void FromAsync_Action_Success()
+        {
+            var i = 0;
+            var xs = Observable.FromAsync(() =>
+            {
+                i++;
+                return Task.Factory.StartNew(() => { });
+            });
+
+            Assert.Equal(Unit.Default, xs.Single());
+            Assert.Equal(1, i);
+
+            Assert.Equal(Unit.Default, xs.Single());
+            Assert.Equal(2, i);
+        }
+
+        [Fact]
+        public void FromAsync_Action_Throw_Synchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync(() =>
+            {
+                throw ex;
+            });
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_Action_Throw_Asynchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync(() =>
+                Task.Factory.StartNew(() => { throw ex; })
+            );
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_ActionWithCancel_Success()
+        {
+            var i = 0;
+            var xs = Observable.FromAsync(ct =>
+            {
+                i++;
+                return Task.Factory.StartNew(() => { });
+            });
+
+            Assert.Equal(Unit.Default, xs.Single());
+            Assert.Equal(1, i);
+
+            Assert.Equal(Unit.Default, xs.Single());
+            Assert.Equal(2, i);
+        }
+
+        [Fact]
+        public void FromAsync_ActionWithCancel_Throw_Synchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync(ct =>
+            {
+                throw ex;
+            });
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_ActionWithCancel_Throw_Asynchronous()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.FromAsync(ct =>
+                Task.Factory.StartNew(() => { throw ex; })
+            );
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void FromAsync_ActionWithCancel_Cancel()
+        {
+            var e = new ManualResetEvent(false);
+            var f = new ManualResetEvent(false);
+
+            var t = default(Task);
+            var xs = Observable.FromAsync(ct =>
+                t = Task.Factory.StartNew(() =>
+                {
+                    try
+                    {
+                        e.Set();
+                        while (true)
+                            ct.ThrowIfCancellationRequested();
+                    }
+                    finally
+                    {
+                        f.Set();
+                    }
+                })
+            );
+
+            var d = xs.Subscribe(_ => { });
+            e.WaitOne();
+            d.Dispose();
+
+            f.WaitOne();
+            while (!t.IsCompleted)
+                ;
+        }
+
+#if DESKTOPCLR
+        [Fact]
+        public void FromAsync_Action_Scheduler1()
+        {
+            var e = new ManualResetEvent(false);
+            var t = default(int);
+
+            var tcs = new TaskCompletionSource<int>();
+
+            var xs = Observable.FromAsync(() => (Task)tcs.Task, Scheduler.Immediate);
+            xs.Subscribe(res =>
+            {
+                t = Thread.CurrentThread.ManagedThreadId;
+                e.Set();
+            });
+
+            tcs.SetResult(42);
+
+            e.WaitOne();
+
+            Assert.Equal(Thread.CurrentThread.ManagedThreadId, t);
+        }
+
+        [Fact]
+        public void FromAsync_Action_Scheduler2()
+        {
+            var e = new ManualResetEvent(false);
+            var t = default(int);
+
+            var tcs = new TaskCompletionSource<int>();
+
+            var xs = Observable.FromAsync(ct => (Task)tcs.Task, Scheduler.Immediate);
+            xs.Subscribe(res =>
+            {
+                t = Thread.CurrentThread.ManagedThreadId;
+                e.Set();
+            });
+
+            tcs.SetResult(42);
+
+            e.WaitOne();
+
+            Assert.Equal(Thread.CurrentThread.ManagedThreadId, t);
+        }
+#endif
+
+        #endregion
+
+    }
+}

+ 204 - 551
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/ObservableEventsTest.cs → Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromEventPatternTest.cs

@@ -4,23 +4,26 @@
 
 using System;
 using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
 using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
 using System.Reactive;
 using System.Reactive.Concurrency;
 using System.Reactive.Linq;
-using System.Reflection;
-using System.Threading;
 using Microsoft.Reactive.Testing;
 using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+using System.ComponentModel;
+using System.Diagnostics;
 
 namespace ReactiveTests.Tests
 {
-    
-    public partial class ObservableEventsTest : ReactiveTest
+    public class FromEventPatternTest : ReactiveTest
     {
-        #region + FromEventPattern +
 
         #region Strongly typed
 
@@ -611,356 +614,9 @@ namespace ReactiveTests.Tests
 
         #endregion
 
-        #endregion
-
-        #region + FromEvent +
-
-        [Fact]
-        public void FromEvent_ArgumentChecking()
-        {
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Func<Action<int>, Action<int>>), h => { }, h => { }));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, default(Action<Action<int>>), h => { }));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, h => { }, default(Action<Action<int>>)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Action<Action<int>>), h => { }));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => { }, default(Action<Action<int>>)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(default(Action<Action<int>>), h => { }));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(h => { }, default(Action<Action<int>>)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(default(Action<Action>), h => { }));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(h => { }, default(Action<Action>)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Func<Action<int>, Action<int>>), h => { }, h => { }, Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, default(Action<Action<int>>), h => { }, Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, h => { }, default(Action<Action<int>>), Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, h => { }, h => { }, default(IScheduler)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Action<Action<int>>), h => { }, Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => { }, default(Action<Action<int>>), Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => { }, h => { }, default(IScheduler)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(default(Action<Action<int>>), h => { }, Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(h => { }, default(Action<Action<int>>), Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(h => { }, h => { }, default(IScheduler)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(default(Action<Action>), h => { }, Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(h => { }, default(Action<Action>), Scheduler.Default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(h => { }, h => { }, default(IScheduler)));
-        }
-
-        [Fact]
-        public void FromEvent_Action()
-        {
-            var fe = new FromEvent();
-
-            var xs = Observable.FromEvent(h => fe.A += h, h => fe.A -= h);
-
-            fe.OnA();
-
-            var n = 0;
-            var d = xs.Subscribe(_ => n++);
-
-            fe.OnA();
-            fe.OnA();
-
-            d.Dispose();
-
-            fe.OnA();
-
-            Assert.Equal(2, n);
-        }
-
-        [Fact]
-        public void FromEvent_ActionOfInt()
-        {
-            var fe = new FromEvent();
-
-            var xs = Observable.FromEvent<int>(h => fe.B += h, h => fe.B -= h);
-
-            fe.OnB(1);
-
-            var n = 0;
-            var d = xs.Subscribe(x => n += x);
-
-            fe.OnB(2);
-            fe.OnB(3);
-
-            d.Dispose();
-
-            fe.OnB(4);
-
-            Assert.Equal(2 + 3, n);
-        }
-
-        [Fact]
-        public void FromEvent_ActionOfInt_SpecifiedExplicitly()
-        {
-            var fe = new FromEvent();
-
-            var xs = Observable.FromEvent<Action<int>, int>(h => fe.B += h, h => fe.B -= h);
-
-            fe.OnB(1);
-
-            var n = 0;
-            var d = xs.Subscribe(x => n += x);
-
-            fe.OnB(2);
-            fe.OnB(3);
-
-            d.Dispose();
-
-            fe.OnB(4);
-
-            Assert.Equal(2 + 3, n);
-        }
-
-        [Fact]
-        public void FromEvent_ActionOfInt_SpecifiedExplicitly_TrivialConversion()
-        {
-            var fe = new FromEvent();
-
-            var xs = Observable.FromEvent<Action<int>, int>(h => h, h => fe.B += h, h => fe.B -= h);
-
-            fe.OnB(1);
-
-            var n = 0;
-            var d = xs.Subscribe(x => n += x);
-
-            fe.OnB(2);
-            fe.OnB(3);
-
-            d.Dispose();
-
-            fe.OnB(4);
-
-            Assert.Equal(2 + 3, n);
-        }
-
-        [Fact]
-        public void FromEvent_MyAction()
-        {
-            var fe = new FromEvent();
-
-            var xs = Observable.FromEvent<MyAction, int>(h => new MyAction(h), h => fe.C += h, h => fe.C -= h);
-
-            fe.OnC(1);
-
-            var n = 0;
-            var d = xs.Subscribe(x => n += x);
-
-            fe.OnC(2);
-            fe.OnC(3);
-
-            d.Dispose();
-
-            fe.OnC(4);
-
-            Assert.Equal(2 + 3, n);
-        }
-
-        #endregion
 
         #region Rx v2.0 behavior
 
-        [Fact]
-        public void FromEvent_ImplicitPublish()
-        {
-            var src = new MyEventSource();
-
-            var addCount = 0;
-            var remCount = 0;
-
-            var xs = Observable.FromEventPattern<MyEventArgs>(h => { addCount++; src.Bar += h; }, h => { src.Bar -= h; remCount++; }, Scheduler.Immediate);
-
-            Assert.Equal(0, addCount);
-            Assert.Equal(0, remCount);
-
-            src.OnBar(41);
-
-            var fst = new List<int>();
-            var d1 = xs.Subscribe(e => fst.Add(e.EventArgs.Value));
-
-            Assert.Equal(1, addCount);
-            Assert.Equal(0, remCount);
-
-            src.OnBar(42);
-
-            Assert.True(fst.SequenceEqual(new[] { 42 }));
-
-            d1.Dispose();
-
-            Assert.Equal(1, addCount);
-            Assert.Equal(1, remCount);
-
-            var snd = new List<int>();
-            var d2 = xs.Subscribe(e => snd.Add(e.EventArgs.Value));
-
-            Assert.Equal(2, addCount);
-            Assert.Equal(1, remCount);
-
-            src.OnBar(43);
-
-            Assert.True(fst.SequenceEqual(new[] { 42 }));
-            Assert.True(snd.SequenceEqual(new[] { 43 }));
-
-            var thd = new List<int>();
-            var d3 = xs.Subscribe(e => thd.Add(e.EventArgs.Value));
-
-            Assert.Equal(2, addCount);
-            Assert.Equal(1, remCount);
-
-            src.OnBar(44);
-
-            Assert.True(fst.SequenceEqual(new[] { 42 }));
-            Assert.True(snd.SequenceEqual(new[] { 43, 44 }));
-            Assert.True(thd.SequenceEqual(new[] { 44 }));
-
-            d2.Dispose();
-
-            Assert.Equal(2, addCount);
-            Assert.Equal(1, remCount);
-
-            src.OnBar(45);
-
-            Assert.True(fst.SequenceEqual(new[] { 42 }));
-            Assert.True(snd.SequenceEqual(new[] { 43, 44 }));
-            Assert.True(thd.SequenceEqual(new[] { 44, 45 }));
-
-            d3.Dispose();
-
-            Assert.Equal(2, addCount);
-            Assert.Equal(2, remCount);
-
-            src.OnBar(46);
-
-            Assert.True(fst.SequenceEqual(new[] { 42 }));
-            Assert.True(snd.SequenceEqual(new[] { 43, 44 }));
-            Assert.True(thd.SequenceEqual(new[] { 44, 45 }));
-        }
-#if !NO_THREAD
-        [Fact]
-        public void FromEvent_SynchronizationContext()
-        {
-            var beforeSubscribeNull = false;
-            var afterSubscribeNull = false;
-            var subscribeOnCtx = false;
-
-            var fstNext = false;
-            var sndNext = false;
-            var thdNext = false;
-
-            var beforeDisposeNull = false;
-            var afterDisposeNull = false;
-            var disposeOnCtx = false;
-
-            RunWithContext(new MyEventSyncCtx(), ctx =>
-            {
-                var src = new MyEventSource();
-
-                var addCtx = default(SynchronizationContext);
-                var remCtx = default(SynchronizationContext);
-
-                var addEvt = new ManualResetEvent(false);
-                var remEvt = new ManualResetEvent(false);
-
-                var xs = Observable.FromEventPattern<MyEventArgs>(h => { addCtx = SynchronizationContext.Current; src.Bar += h; addEvt.Set(); }, h => { remCtx = SynchronizationContext.Current; src.Bar -= h; remEvt.Set(); });
-
-                Assert.Null(addCtx);
-                Assert.Null(remCtx);
-
-                var d = default(IDisposable);
-                var res = new List<int>();
-
-                var s = new Thread(() =>
-                {
-                    beforeSubscribeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
-                    d = xs.Subscribe(e => res.Add(e.EventArgs.Value));
-                    afterSubscribeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
-                });
-
-                s.Start();
-                s.Join();
-
-                addEvt.WaitOne();
-
-                subscribeOnCtx = object.ReferenceEquals(addCtx, ctx);
-
-                src.OnBar(42);
-                fstNext = res.SequenceEqual(new[] { 42 });
-
-                src.OnBar(43);
-                sndNext = res.SequenceEqual(new[] { 42, 43 });
-
-                var u = new Thread(() =>
-                {
-                    beforeDisposeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
-                    d.Dispose();
-                    afterDisposeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
-                });
-
-                u.Start();
-                u.Join();
-
-                remEvt.WaitOne();
-
-                disposeOnCtx = object.ReferenceEquals(remCtx, ctx);
-
-                src.OnBar(44);
-                thdNext = res.SequenceEqual(new[] { 42, 43 });
-            });
-
-            Assert.True(beforeSubscribeNull);
-            Assert.True(subscribeOnCtx);
-            Assert.True(afterSubscribeNull);
-
-            Assert.True(fstNext);
-            Assert.True(sndNext);
-            Assert.True(thdNext);
-
-            Assert.True(beforeDisposeNull);
-            Assert.True(disposeOnCtx);
-            Assert.True(afterDisposeNull);
-        }
-
-        private void RunWithContext<T>(T ctx, Action<T> run)
-            where T : SynchronizationContext
-        {
-            var t = new Thread(() =>
-            {
-                SynchronizationContext.SetSynchronizationContext(ctx);
-                run(ctx);
-            });
-
-            t.Start();
-            t.Join();
-        }
-#endif
-
-        [Fact]
-        public void FromEvent_Scheduler1()
-        {
-            RunWithScheduler((s, add, remove) => Observable.FromEvent<MyEventArgs>(h => { add(); }, h => { remove(); }, s));
-        }
-
-        [Fact]
-        public void FromEvent_Scheduler2()
-        {
-            RunWithScheduler((s, add, remove) => Observable.FromEvent(h => { add(); }, h => { remove(); }, s));
-        }
-
-        [Fact]
-        public void FromEvent_Scheduler3()
-        {
-            RunWithScheduler((s, add, remove) => Observable.FromEvent<Action<MyEventArgs>, MyEventArgs>(h => { add(); }, h => { remove(); }, s));
-        }
-
-        [Fact]
-        public void FromEvent_Scheduler4()
-        {
-            RunWithScheduler((s, add, remove) => Observable.FromEvent<Action, MyEventArgs>(h => () => { }, h => { add(); }, h => { remove(); }, s));
-        }
-
         [Fact]
         public void FromEventPattern_Scheduler1()
         {
@@ -1057,24 +713,6 @@ namespace ReactiveTests.Tests
             RunWithScheduler((s, add, remove) => Observable.FromEventPattern<EventHandler<MyEventArgs>, object, MyEventArgs>(h => { add(); }, h => { remove(); }, s));
         }
 
-        class MyEventObject
-        {
-            public static Action Add;
-            public static Action Remove;
-
-            public event EventHandler<MyEventArgs> I
-            {
-                add { Add(); }
-                remove { Remove(); }
-            }
-
-            public static event EventHandler<MyEventArgs> S
-            {
-                add { Add(); }
-                remove { Remove(); }
-            }
-        }
-
         private void RunWithScheduler<T>(Func<IScheduler, Action, Action, IObservable<T>> run)
         {
             var n = 0;
@@ -1113,242 +751,257 @@ namespace ReactiveTests.Tests
             Assert.Equal(1, r);
         }
 
-        class MyEventSource
+        #endregion
+    }
+
+    public class FromEventPattern
+    {
+        [DebuggerDisplay("{Id}")]
+        public class TestEventArgs : EventArgs, IEquatable<TestEventArgs>
         {
-            public event EventHandler<MyEventArgs> Bar;
+            public int Id { get; set; }
 
-            public void OnBar(int value)
+            public override string ToString()
             {
-                var bar = Bar;
-                if (bar != null)
-                    bar(this, new MyEventArgs(value));
+                return Id.ToString();
             }
-        }
 
-        class MyEventArgs : EventArgs
-        {
-            public MyEventArgs(int value)
+            public bool Equals(TestEventArgs other)
             {
-                Value = value;
+                if (other == this)
+                    return true;
+                if (other == null)
+                    return false;
+                return other.Id == Id;
             }
 
-            public int Value { get; private set; }
-        }
-
-        class MyEventSyncCtx : SynchronizationContext
-        {
-            public int PostCount { get; private set; }
+            public override bool Equals(object obj)
+            {
+                return Equals(obj as TestEventArgs);
+            }
 
-            public override void Post(SendOrPostCallback d, object state)
+            public override int GetHashCode()
             {
-                var old = SynchronizationContext.Current;
-                SynchronizationContext.SetSynchronizationContext(this);
-                try
-                {
-                    PostCount++;
-                    d(state);
-                }
-                finally
-                {
-                    SynchronizationContext.SetSynchronizationContext(old);
-                }
+                return Id;
             }
         }
 
-        class MyEventScheduler : LocalScheduler
-        {
-            private Action _schedule;
+        public delegate void TestEventHandler(object sender, TestEventArgs eventArgs);
 
-            public MyEventScheduler(Action schedule)
-            {
-                _schedule = schedule;
-            }
+        public event TestEventHandler E1;
 
-            public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
-            {
-                _schedule();
+        public void M1(int i)
+        {
+            var e = E1;
+            if (e != null)
+                e(this, new TestEventArgs { Id = i });
+        }
 
-                return action(this, state);
-            }
+        public event EventHandler<TestEventArgs> E2;
 
-            public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
-            {
-                throw new NotImplementedException();
-            }
+        public void M2(int i)
+        {
+            var e = E2;
+            if (e != null)
+                e(this, new TestEventArgs { Id = i });
         }
 
+        public event Action<object, TestEventArgs> E3;
 
-#endregion
+        public void M3(int i)
+        {
+            var e = E3;
+            if (e != null)
+                e(this, new TestEventArgs { Id = i });
+        }
 
-#region <| Helpers |>
+        public event Action<int> E4;
 
-        class FromEventPattern_ArgCheck
+        public void M4(int i)
         {
-#pragma warning disable 67
-            public event Action E1;
-            public event Action<int, int> E2;
-            public event Action<object, object> E3;
-            public event Action<object, int> E4;
-            public event Func<object, EventArgs, int> E5;
-            public event Action<EventArgs> E6;
-#pragma warning restore 67
+            var e = E4;
+            if (e != null)
+                e(i);
         }
 
-        public class FromEventPattern_VarianceCheck
+        public event TestEventHandler AddThrows
         {
-#pragma warning disable 67
-            public event EventHandler<EventArgs> E1;
-            public event EventHandler<CancelEventArgs> E2;
-            public event Action<string, EventArgs> E3;
-#pragma warning restore 67
+            add { throw new InvalidOperationException(); }
+            remove { }
+        }
 
-            public void OnE2(CancelEventArgs args)
-            {
-                var e = E2;
-                if (e != null)
-                    e(this, args);
-            }
+        public event TestEventHandler RemoveThrows
+        {
+            add { }
+            remove { throw new InvalidOperationException(); }
+        }
 
-            public void OnE3(string sender)
-            {
-                var e = E3;
-                if (e != null)
-                    e(sender, EventArgs.Empty);
-            }
+        public event EventHandler E5;
+        public void M5(int i)
+        {
+            var e = E5;
+            if (e != null)
+                e(this, new FromEventPattern.TestEventArgs { Id = i });
         }
 
-        public class FromEventPattern
+        public static event EventHandler<TestEventArgs> E6;
+
+        public static void M6(int i)
         {
-            [DebuggerDisplay("{Id}")]
-            public class TestEventArgs : EventArgs, IEquatable<TestEventArgs>
-            {
-                public int Id { get; set; }
-
-                public override string ToString()
-                {
-                    return Id.ToString();
-                }
-
-                public bool Equals(TestEventArgs other)
-                {
-                    if (other == this)
-                        return true;
-                    if (other == null)
-                        return false;
-                    return other.Id == Id;
-                }
-
-                public override bool Equals(object obj)
-                {
-                    return Equals(obj as TestEventArgs);
-                }
-
-                public override int GetHashCode()
-                {
-                    return Id;
-                }
-            }
+            var e = E6;
+            if (e != null)
+                e(null, new TestEventArgs { Id = i });
+        }
+    }
 
-            public delegate void TestEventHandler(object sender, TestEventArgs eventArgs);
+    public delegate void MyAction(int x);
 
-            public event TestEventHandler E1;
+    public class FromEvent
+    {
+        public event Action A;
 
-            public void M1(int i)
-            {
-                var e = E1;
-                if (e != null)
-                    e(this, new TestEventArgs { Id = i });
-            }
+        public void OnA()
+        {
+            var a = A;
+            if (a != null)
+                a();
+        }
 
-            public event EventHandler<TestEventArgs> E2;
+        public event Action<int> B;
 
-            public void M2(int i)
-            {
-                var e = E2;
-                if (e != null)
-                    e(this, new TestEventArgs { Id = i });
-            }
+        public void OnB(int x)
+        {
+            var b = B;
+            if (b != null)
+                b(x);
+        }
 
-            public event Action<object, TestEventArgs> E3;
+        public event MyAction C;
 
-            public void M3(int i)
-            {
-                var e = E3;
-                if (e != null)
-                    e(this, new TestEventArgs { Id = i });
-            }
+        public void OnC(int x)
+        {
+            var c = C;
+            if (c != null)
+                c(x);
+        }
+    }
 
-            public event Action<int> E4;
 
-            public void M4(int i)
-            {
-                var e = E4;
-                if (e != null)
-                    e(i);
-            }
 
-            public event TestEventHandler AddThrows
-            {
-                add { throw new InvalidOperationException(); }
-                remove { }
-            }
+    class FromEventPattern_ArgCheck
+    {
+#pragma warning disable 67
+        public event Action E1;
+        public event Action<int, int> E2;
+        public event Action<object, object> E3;
+        public event Action<object, int> E4;
+        public event Func<object, EventArgs, int> E5;
+        public event Action<EventArgs> E6;
+#pragma warning restore 67
+    }
 
-            public event TestEventHandler RemoveThrows
-            {
-                add { }
-                remove { throw new InvalidOperationException(); }
-            }
+    public class FromEventPattern_VarianceCheck
+    {
+#pragma warning disable 67
+        public event EventHandler<EventArgs> E1;
+        public event EventHandler<CancelEventArgs> E2;
+        public event Action<string, EventArgs> E3;
+#pragma warning restore 67
 
-            public event EventHandler E5;
-            public void M5(int i)
-            {
-                var e = E5;
-                if (e != null)
-                    e(this, new FromEventPattern.TestEventArgs { Id = i });
-            }
+        public void OnE2(CancelEventArgs args)
+        {
+            var e = E2;
+            if (e != null)
+                e(this, args);
+        }
 
-            public static event EventHandler<TestEventArgs> E6;
+        public void OnE3(string sender)
+        {
+            var e = E3;
+            if (e != null)
+                e(sender, EventArgs.Empty);
+        }
+    }
 
-            public static void M6(int i)
-            {
-                var e = E6;
-                if (e != null)
-                    e(null, new TestEventArgs { Id = i });
-            }
+    class MyEventObject
+    {
+        public static Action Add;
+        public static Action Remove;
+
+        public event EventHandler<MyEventArgs> I
+        {
+            add { Add(); }
+            remove { Remove(); }
         }
 
-        public delegate void MyAction(int x);
+        public static event EventHandler<MyEventArgs> S
+        {
+            add { Add(); }
+            remove { Remove(); }
+        }
+    }
+
+    class MyEventSource
+    {
+        public event EventHandler<MyEventArgs> Bar;
 
-        public class FromEvent
+        public void OnBar(int value)
         {
-            public event Action A;
+            var bar = Bar;
+            if (bar != null)
+                bar(this, new MyEventArgs(value));
+        }
+    }
 
-            public void OnA()
-            {
-                var a = A;
-                if (a != null)
-                    a();
-            }
+    class MyEventArgs : EventArgs
+    {
+        public MyEventArgs(int value)
+        {
+            Value = value;
+        }
 
-            public event Action<int> B;
+        public int Value { get; private set; }
+    }
 
-            public void OnB(int x)
+    class MyEventSyncCtx : SynchronizationContext
+    {
+        public int PostCount { get; private set; }
+
+        public override void Post(SendOrPostCallback d, object state)
+        {
+            var old = SynchronizationContext.Current;
+            SynchronizationContext.SetSynchronizationContext(this);
+            try
             {
-                var b = B;
-                if (b != null)
-                    b(x);
+                PostCount++;
+                d(state);
             }
-
-            public event MyAction C;
-
-            public void OnC(int x)
+            finally
             {
-                var c = C;
-                if (c != null)
-                    c(x);
+                SynchronizationContext.SetSynchronizationContext(old);
             }
         }
+    }
+
+    class MyEventScheduler : LocalScheduler
+    {
+        private Action _schedule;
 
-#endregion
+        public MyEventScheduler(Action schedule)
+        {
+            _schedule = schedule;
+        }
+
+        public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
+        {
+            _schedule();
+
+            return action(this, state);
+        }
+
+        public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 410 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromEventTest.cs

@@ -0,0 +1,410 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class FromEventTest : ReactiveTest
+    {
+
+        [Fact]
+        public void FromEvent_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Func<Action<int>, Action<int>>), h => { }, h => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, default(Action<Action<int>>), h => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, h => { }, default(Action<Action<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Action<Action<int>>), h => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => { }, default(Action<Action<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(default(Action<Action<int>>), h => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(h => { }, default(Action<Action<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(default(Action<Action>), h => { }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(h => { }, default(Action<Action>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Func<Action<int>, Action<int>>), h => { }, h => { }, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, default(Action<Action<int>>), h => { }, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, h => { }, default(Action<Action<int>>), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => h, h => { }, h => { }, default(IScheduler)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(default(Action<Action<int>>), h => { }, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => { }, default(Action<Action<int>>), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<Action<int>, int>(h => { }, h => { }, default(IScheduler)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(default(Action<Action<int>>), h => { }, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(h => { }, default(Action<Action<int>>), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent<int>(h => { }, h => { }, default(IScheduler)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(default(Action<Action>), h => { }, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(h => { }, default(Action<Action>), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromEvent(h => { }, h => { }, default(IScheduler)));
+        }
+
+        [Fact]
+        public void FromEvent_Action()
+        {
+            var fe = new FromEvent();
+
+            var xs = Observable.FromEvent(h => fe.A += h, h => fe.A -= h);
+
+            fe.OnA();
+
+            var n = 0;
+            var d = xs.Subscribe(_ => n++);
+
+            fe.OnA();
+            fe.OnA();
+
+            d.Dispose();
+
+            fe.OnA();
+
+            Assert.Equal(2, n);
+        }
+
+        [Fact]
+        public void FromEvent_ActionOfInt()
+        {
+            var fe = new FromEvent();
+
+            var xs = Observable.FromEvent<int>(h => fe.B += h, h => fe.B -= h);
+
+            fe.OnB(1);
+
+            var n = 0;
+            var d = xs.Subscribe(x => n += x);
+
+            fe.OnB(2);
+            fe.OnB(3);
+
+            d.Dispose();
+
+            fe.OnB(4);
+
+            Assert.Equal(2 + 3, n);
+        }
+
+        [Fact]
+        public void FromEvent_ActionOfInt_SpecifiedExplicitly()
+        {
+            var fe = new FromEvent();
+
+            var xs = Observable.FromEvent<Action<int>, int>(h => fe.B += h, h => fe.B -= h);
+
+            fe.OnB(1);
+
+            var n = 0;
+            var d = xs.Subscribe(x => n += x);
+
+            fe.OnB(2);
+            fe.OnB(3);
+
+            d.Dispose();
+
+            fe.OnB(4);
+
+            Assert.Equal(2 + 3, n);
+        }
+
+        [Fact]
+        public void FromEvent_ActionOfInt_SpecifiedExplicitly_TrivialConversion()
+        {
+            var fe = new FromEvent();
+
+            var xs = Observable.FromEvent<Action<int>, int>(h => h, h => fe.B += h, h => fe.B -= h);
+
+            fe.OnB(1);
+
+            var n = 0;
+            var d = xs.Subscribe(x => n += x);
+
+            fe.OnB(2);
+            fe.OnB(3);
+
+            d.Dispose();
+
+            fe.OnB(4);
+
+            Assert.Equal(2 + 3, n);
+        }
+
+        [Fact]
+        public void FromEvent_MyAction()
+        {
+            var fe = new FromEvent();
+
+            var xs = Observable.FromEvent<MyAction, int>(h => new MyAction(h), h => fe.C += h, h => fe.C -= h);
+
+            fe.OnC(1);
+
+            var n = 0;
+            var d = xs.Subscribe(x => n += x);
+
+            fe.OnC(2);
+            fe.OnC(3);
+
+            d.Dispose();
+
+            fe.OnC(4);
+
+            Assert.Equal(2 + 3, n);
+        }
+
+        #region Rx v2.0 behavior
+
+        [Fact]
+        public void FromEvent_ImplicitPublish()
+        {
+            var src = new MyEventSource();
+
+            var addCount = 0;
+            var remCount = 0;
+
+            var xs = Observable.FromEventPattern<MyEventArgs>(h => { addCount++; src.Bar += h; }, h => { src.Bar -= h; remCount++; }, Scheduler.Immediate);
+
+            Assert.Equal(0, addCount);
+            Assert.Equal(0, remCount);
+
+            src.OnBar(41);
+
+            var fst = new List<int>();
+            var d1 = xs.Subscribe(e => fst.Add(e.EventArgs.Value));
+
+            Assert.Equal(1, addCount);
+            Assert.Equal(0, remCount);
+
+            src.OnBar(42);
+
+            Assert.True(fst.SequenceEqual(new[] { 42 }));
+
+            d1.Dispose();
+
+            Assert.Equal(1, addCount);
+            Assert.Equal(1, remCount);
+
+            var snd = new List<int>();
+            var d2 = xs.Subscribe(e => snd.Add(e.EventArgs.Value));
+
+            Assert.Equal(2, addCount);
+            Assert.Equal(1, remCount);
+
+            src.OnBar(43);
+
+            Assert.True(fst.SequenceEqual(new[] { 42 }));
+            Assert.True(snd.SequenceEqual(new[] { 43 }));
+
+            var thd = new List<int>();
+            var d3 = xs.Subscribe(e => thd.Add(e.EventArgs.Value));
+
+            Assert.Equal(2, addCount);
+            Assert.Equal(1, remCount);
+
+            src.OnBar(44);
+
+            Assert.True(fst.SequenceEqual(new[] { 42 }));
+            Assert.True(snd.SequenceEqual(new[] { 43, 44 }));
+            Assert.True(thd.SequenceEqual(new[] { 44 }));
+
+            d2.Dispose();
+
+            Assert.Equal(2, addCount);
+            Assert.Equal(1, remCount);
+
+            src.OnBar(45);
+
+            Assert.True(fst.SequenceEqual(new[] { 42 }));
+            Assert.True(snd.SequenceEqual(new[] { 43, 44 }));
+            Assert.True(thd.SequenceEqual(new[] { 44, 45 }));
+
+            d3.Dispose();
+
+            Assert.Equal(2, addCount);
+            Assert.Equal(2, remCount);
+
+            src.OnBar(46);
+
+            Assert.True(fst.SequenceEqual(new[] { 42 }));
+            Assert.True(snd.SequenceEqual(new[] { 43, 44 }));
+            Assert.True(thd.SequenceEqual(new[] { 44, 45 }));
+        }
+#if !NO_THREAD
+        [Fact]
+        public void FromEvent_SynchronizationContext()
+        {
+            var beforeSubscribeNull = false;
+            var afterSubscribeNull = false;
+            var subscribeOnCtx = false;
+
+            var fstNext = false;
+            var sndNext = false;
+            var thdNext = false;
+
+            var beforeDisposeNull = false;
+            var afterDisposeNull = false;
+            var disposeOnCtx = false;
+
+            RunWithContext(new MyEventSyncCtx(), ctx =>
+            {
+                var src = new MyEventSource();
+
+                var addCtx = default(SynchronizationContext);
+                var remCtx = default(SynchronizationContext);
+
+                var addEvt = new ManualResetEvent(false);
+                var remEvt = new ManualResetEvent(false);
+
+                var xs = Observable.FromEventPattern<MyEventArgs>(h => { addCtx = SynchronizationContext.Current; src.Bar += h; addEvt.Set(); }, h => { remCtx = SynchronizationContext.Current; src.Bar -= h; remEvt.Set(); });
+
+                Assert.Null(addCtx);
+                Assert.Null(remCtx);
+
+                var d = default(IDisposable);
+                var res = new List<int>();
+
+                var s = new Thread(() =>
+                {
+                    beforeSubscribeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
+                    d = xs.Subscribe(e => res.Add(e.EventArgs.Value));
+                    afterSubscribeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
+                });
+
+                s.Start();
+                s.Join();
+
+                addEvt.WaitOne();
+
+                subscribeOnCtx = object.ReferenceEquals(addCtx, ctx);
+
+                src.OnBar(42);
+                fstNext = res.SequenceEqual(new[] { 42 });
+
+                src.OnBar(43);
+                sndNext = res.SequenceEqual(new[] { 42, 43 });
+
+                var u = new Thread(() =>
+                {
+                    beforeDisposeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
+                    d.Dispose();
+                    afterDisposeNull = object.ReferenceEquals(SynchronizationContext.Current, null);
+                });
+
+                u.Start();
+                u.Join();
+
+                remEvt.WaitOne();
+
+                disposeOnCtx = object.ReferenceEquals(remCtx, ctx);
+
+                src.OnBar(44);
+                thdNext = res.SequenceEqual(new[] { 42, 43 });
+            });
+
+            Assert.True(beforeSubscribeNull);
+            Assert.True(subscribeOnCtx);
+            Assert.True(afterSubscribeNull);
+
+            Assert.True(fstNext);
+            Assert.True(sndNext);
+            Assert.True(thdNext);
+
+            Assert.True(beforeDisposeNull);
+            Assert.True(disposeOnCtx);
+            Assert.True(afterDisposeNull);
+        }
+
+        private void RunWithContext<T>(T ctx, Action<T> run)
+            where T : SynchronizationContext
+        {
+            var t = new Thread(() =>
+            {
+                SynchronizationContext.SetSynchronizationContext(ctx);
+                run(ctx);
+            });
+
+            t.Start();
+            t.Join();
+        }
+#endif
+
+        [Fact]
+        public void FromEvent_Scheduler1()
+        {
+            RunWithScheduler((s, add, remove) => Observable.FromEvent<MyEventArgs>(h => { add(); }, h => { remove(); }, s));
+        }
+
+        [Fact]
+        public void FromEvent_Scheduler2()
+        {
+            RunWithScheduler((s, add, remove) => Observable.FromEvent(h => { add(); }, h => { remove(); }, s));
+        }
+
+        [Fact]
+        public void FromEvent_Scheduler3()
+        {
+            RunWithScheduler((s, add, remove) => Observable.FromEvent<Action<MyEventArgs>, MyEventArgs>(h => { add(); }, h => { remove(); }, s));
+        }
+
+        [Fact]
+        public void FromEvent_Scheduler4()
+        {
+            RunWithScheduler((s, add, remove) => Observable.FromEvent<Action, MyEventArgs>(h => () => { }, h => { add(); }, h => { remove(); }, s));
+        }
+
+        private void RunWithScheduler<T>(Func<IScheduler, Action, Action, IObservable<T>> run)
+        {
+            var n = 0;
+            var a = 0;
+            var r = 0;
+
+            var s = new MyEventScheduler(() => n++);
+
+            var add = new Action(() => a++);
+            var rem = new Action(() => r++);
+
+            var xs = run(s, add, rem);
+
+            Assert.Equal(0, n);
+            Assert.Equal(0, a);
+            Assert.Equal(0, r);
+
+            var d1 = xs.Subscribe();
+            Assert.Equal(1, n);
+            Assert.Equal(1, a);
+            Assert.Equal(0, r);
+
+            var d2 = xs.Subscribe();
+            Assert.Equal(1, n);
+            Assert.Equal(1, a);
+            Assert.Equal(0, r);
+
+            d1.Dispose();
+            Assert.Equal(1, n);
+            Assert.Equal(1, a);
+            Assert.Equal(0, r);
+
+            d2.Dispose();
+            Assert.Equal(2, n);
+            Assert.Equal(1, a);
+            Assert.Equal(1, r);
+        }
+
+        #endregion
+    }
+}

+ 463 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GenerateTest.cs

@@ -0,0 +1,463 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+using System.Runtime.CompilerServices;
+
+namespace ReactiveTests.Tests
+{
+    public class GenerateTest : ReactiveTest
+    {
+        #region + Non-timed +
+
+        [Fact]
+        public void Generate_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, (IScheduler)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, (Func<int, bool>)null, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, (Func<int, int>)null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, (Func<int, int>)null, DummyFunc<int, int>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyScheduler.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Generate_Finite()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => x <= 3, x => x + 1, x => x, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 0),
+                OnNext(202, 1),
+                OnNext(203, 2),
+                OnNext(204, 3),
+                OnCompleted<int>(205)
+            );
+        }
+
+        [Fact]
+        public void Generate_Throw_Condition()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, new Func<int, bool>(x => { throw ex; }), x => x + 1, x => x, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_Throw_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, new Func<int, int>(x => { throw ex; }), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_Throw_Iterate()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, new Func<int, int>(x => { throw ex; }), x => x, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 0),
+                OnError<int>(202, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, x => x, scheduler),
+                203
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 0),
+                OnNext(202, 1)
+            );
+        }
+
+        [Fact]
+        public void Generate_DefaultScheduler_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, (Func<int, bool>)null, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, (Func<int, int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, (Func<int, int>)null, DummyFunc<int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Generate_DefaultScheduler()
+        {
+            Observable.Generate(0, x => x < 10, x => x + 1, x => x).AssertEqual(Observable.Generate(0, x => x < 10, x => x + 1, x => x, DefaultScheduler.Instance));
+        }
+
+#if !NO_PERF
+        [Fact]
+        public void Generate_LongRunning1()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Generate(0, x => x < 100, x => x + 1, x => x, s);
+
+            var lst = new List<int>();
+            var done = false;
+            xs.Subscribe(x => { lst.Add(x); }, () => done = true);
+
+            end.WaitOne();
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(0, 100)));
+            Assert.True(done);
+        }
+
+        [Fact]
+        [MethodImpl(MethodImplOptions.NoOptimization)]
+        public void Generate_LongRunning2()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Generate(0, _ => true, x => x + 1, x => x, s);
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(x => { lst.Add(x); });
+
+            start.WaitOne();
+
+            while (lst.Count < 100)
+                ;
+
+            d.Dispose();
+            end.WaitOne();
+
+            Assert.True(lst.Take(100).SequenceEqual(Enumerable.Range(0, 100)));
+        }
+
+        [Fact]
+        public void Generate_LongRunning_Throw()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var ex = new Exception();
+            var xs = Observable.Generate(0, x => { if (x < 100) return true; throw ex; }, x => x + 1, x => x, s);
+
+            var lst = new List<int>();
+            var e = default(Exception);
+            var done = false;
+            xs.Subscribe(x => { lst.Add(x); }, e_ => e = e_, () => done = true);
+
+            end.WaitOne();
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(0, 100)));
+            Assert.Same(ex, e);
+            Assert.False(done);
+        }
+#endif
+
+        #endregion
+
+        #region + Timed +
+
+        [Fact]
+        public void Generate_TimeSpan_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance, (IScheduler)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, (Func<int, bool>)null, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, (Func<int, int>)null, DummyFunc<int, TimeSpan>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, (Func<int, int>)null, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, (Func<int, TimeSpan>)null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance, DummyScheduler.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_Finite()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => x <= 3, x => x + 1, x => x, x => TimeSpan.FromTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(202, 0),
+                OnNext(204, 1),
+                OnNext(207, 2),
+                OnNext(211, 3),
+                OnCompleted<int>(211)
+            );
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_Throw_Condition()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, new Func<int, bool>(x => { throw ex; }), x => x + 1, x => x, x => TimeSpan.FromTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_Throw_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, new Func<int, int>(x => { throw ex; }), x => TimeSpan.FromTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_Throw_Iterate()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, new Func<int, int>(x => { throw ex; }), x => x, x => TimeSpan.FromTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(202, 0),
+                OnError<int>(202, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_Throw_TimeSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, x => x, new Func<int, TimeSpan>(x => { throw ex; }), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, x => x, x => TimeSpan.FromTicks(x + 1), scheduler),
+                210
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(202, 0),
+                OnNext(204, 1),
+                OnNext(207, 2)
+            );
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_DefaultScheduler_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, (Func<int, bool>)null, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, (Func<int, int>)null, DummyFunc<int, TimeSpan>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, (Func<int, int>)null, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, (Func<int, TimeSpan>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, TimeSpan>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Generate_TimeSpan_DefaultScheduler()
+        {
+            Observable.Generate(0, x => x < 10, x => x + 1, x => x, x => TimeSpan.FromMilliseconds(x)).AssertEqual(Observable.Generate(0, x => x < 10, x => x + 1, x => x, x => TimeSpan.FromMilliseconds(x), DefaultScheduler.Instance));
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance, (IScheduler)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, (Func<int, bool>)null, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, (Func<int, int>)null, DummyFunc<int, DateTimeOffset>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, (Func<int, int>)null, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, (Func<int, DateTimeOffset>)null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance, DummyScheduler.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_Finite()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => x <= 3, x => x + 1, x => x, x => scheduler.Now.AddTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(202, 0),
+                OnNext(204, 1),
+                OnNext(207, 2),
+                OnNext(211, 3),
+                OnCompleted<int>(211)
+            );
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_Throw_Condition()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, new Func<int, bool>(x => { throw ex; }), x => x + 1, x => x, x => scheduler.Now.AddTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_Throw_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, new Func<int, int>(x => { throw ex; }), x => scheduler.Now.AddTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_Throw_Iterate()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, new Func<int, int>(x => { throw ex; }), x => x, x => scheduler.Now.AddTicks(x + 1), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(202, 0),
+                OnError<int>(202, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_Throw_TimeSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, x => x, new Func<int, DateTimeOffset>(x => { throw ex; }), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(201, ex)
+            );
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Generate(0, x => true, x => x + 1, x => x, x => scheduler.Now.AddTicks(x + 1), scheduler),
+                210
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(202, 0),
+                OnNext(204, 1),
+                OnNext(207, 2)
+            );
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_DefaultScheduler_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, (Func<int, bool>)null, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, (Func<int, int>)null, DummyFunc<int, DateTimeOffset>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, (Func<int, int>)null, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, (Func<int, DateTimeOffset>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Generate(0, DummyFunc<int, bool>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, DateTimeOffset>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Generate_DateTimeOffset_DefaultScheduler()
+        {
+            Observable.Generate(0, x => x < 10, x => x + 1, x => x, x => DateTimeOffset.Now.AddMilliseconds(x)).AssertEqual(Observable.Generate(0, x => x < 10, x => x + 1, x => x, x => DateTimeOffset.Now.AddMilliseconds(x), DefaultScheduler.Instance));
+        }
+
+        #endregion
+    }
+}

+ 172 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GetEnumeratorTest.cs

@@ -0,0 +1,172 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Reactive.Disposables;
+
+namespace ReactiveTests.Tests
+{
+    public class GetEnumeratorTest : ReactiveTest
+    {
+
+        [Fact]
+        public void GetEnumerator_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetEnumerator(default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void GetEnumerator_Regular1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable<int>(
+                OnNext(10, 2),
+                OnNext(20, 3),
+                OnNext(30, 5),
+                OnNext(40, 7),
+                OnCompleted<int>(50)
+            );
+
+            var res = default(IEnumerator<int>);
+
+            scheduler.ScheduleAbsolute(default(object), 100, (self, _) => { res = xs.GetEnumerator(); return Disposable.Empty; });
+
+            var hasNext = new List<bool>();
+            var vals = new List<Tuple<long, int>>();
+            for (long i = 200; i <= 250; i += 10)
+            {
+                var t = i;
+                scheduler.ScheduleAbsolute(default(object), t, (self, _) =>
+                {
+                    var b = res.MoveNext();
+                    hasNext.Add(b);
+                    if (b)
+                        vals.Add(new Tuple<long, int>(scheduler.Clock, res.Current));
+                    return Disposable.Empty;
+                });
+            }
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 150)
+            );
+
+            Assert.Equal(6, hasNext.Count);
+            Assert.True(hasNext.Take(4).All(_ => _));
+            Assert.True(hasNext.Skip(4).All(_ => !_));
+
+            Assert.Equal(4, vals.Count);
+            Assert.True(vals[0].Item1 == 200 && vals[0].Item2 == 2);
+            Assert.True(vals[1].Item1 == 210 && vals[1].Item2 == 3);
+            Assert.True(vals[2].Item1 == 220 && vals[2].Item2 == 5);
+            Assert.True(vals[3].Item1 == 230 && vals[3].Item2 == 7);
+        }
+
+        [Fact]
+        public void GetEnumerator_Regular2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable<int>(
+                OnNext(10, 2),
+                OnNext(30, 3),
+                OnNext(50, 5),
+                OnNext(70, 7),
+                OnCompleted<int>(90)
+            );
+
+            var res = default(IEnumerator<int>);
+
+            scheduler.ScheduleAbsolute(default(object), 100, (self, _) => { res = xs.GetEnumerator(); return Disposable.Empty; });
+
+            var hasNext = new List<bool>();
+            var vals = new List<Tuple<long, int>>();
+            for (long i = 120; i <= 220; i += 20)
+            {
+                var t = i;
+                scheduler.ScheduleAbsolute(default(object), t, (self, _) =>
+                {
+                    var b = res.MoveNext();
+                    hasNext.Add(b);
+                    if (b)
+                        vals.Add(new Tuple<long, int>(scheduler.Clock, res.Current));
+                    return Disposable.Empty;
+                });
+            }
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 190)
+            );
+
+            Assert.Equal(6, hasNext.Count);
+            Assert.True(hasNext.Take(4).All(_ => _));
+            Assert.True(hasNext.Skip(4).All(_ => !_));
+
+            Assert.Equal(4, vals.Count);
+            Assert.True(vals[0].Item1 == 120 && vals[0].Item2 == 2);
+            Assert.True(vals[1].Item1 == 140 && vals[1].Item2 == 3);
+            Assert.True(vals[2].Item1 == 160 && vals[2].Item2 == 5);
+            Assert.True(vals[3].Item1 == 180 && vals[3].Item2 == 7);
+        }
+
+        [Fact]
+        public void GetEnumerator_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable<int>(
+                OnNext(10, 2),
+                OnNext(30, 3),
+                OnNext(50, 5),
+                OnNext(70, 7),
+                OnCompleted<int>(90)
+            );
+
+            var res = default(IEnumerator<int>);
+
+            scheduler.ScheduleAbsolute(default(object), 100, (self, _) => { res = xs.GetEnumerator(); return Disposable.Empty; });
+
+            scheduler.ScheduleAbsolute(default(object), 140, (self, _) =>
+            {
+                Assert.True(res.MoveNext());
+                Assert.Equal(2, res.Current);
+
+                Assert.True(res.MoveNext());
+                Assert.Equal(3, res.Current);
+
+                res.Dispose();
+
+                return Disposable.Empty;
+            });
+
+            scheduler.ScheduleAbsolute(default(object), 160, (self, _) =>
+            {
+                ReactiveAssert.Throws<ObjectDisposedException>(() => res.MoveNext());
+                return Disposable.Empty;
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 140)
+            );
+        }
+
+    }
+}

+ 3454 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GroupByTest.cs

@@ -0,0 +1,3454 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class GroupByTest : ReactiveTest
+    {
+        #region + GroupBy +
+
+        [Fact]
+        public void GroupBy_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, DummyFunc<int, int>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, (Func<int, int>)null, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, EqualityComparer<int>.Default).Subscribe(null));
+        }
+
+        [Fact]
+        public void GroupBy_KeyEle_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, DummyFunc<int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, (Func<int, int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void GroupBy_KeyComparer_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, (IEqualityComparer<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, EqualityComparer<int>.Default).Subscribe(null));
+        }
+
+        [Fact]
+        public void GroupBy_Key_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void GroupBy_WithKeyComparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(x =>
+                {
+                    keyInvoked++;
+                    return x.Trim();
+                }, comparer).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(570, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    }, x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    }, comparer
+                ).Select(g => g.Key),
+                355
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 355)
+            );
+
+            Assert.Equal(5, keyInvoked);
+            Assert.Equal(5, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        if (keyInvoked == 10)
+                            throw ex;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(9, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        if (eleInvoked == 10)
+                            throw ex;
+                        return Reverse(x);
+                    },
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(10, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_ComparerEqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 250, ushort.MaxValue);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnError<string>(310, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            Assert.Equal(4, keyInvoked);
+            Assert.Equal(3, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Outer_ComparerGetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 410);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(8, keyInvoked);
+            Assert.Equal(7, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(570)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Complete_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(570)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex1),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }, ex => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnError<string>(570, ex1)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnError<string>(570, ex1)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnError<string>(570, ex1)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(570, ex1)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(400, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof")
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   ")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var keyInvoked = 0;
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x =>
+            {
+                keyInvoked++;
+                if (keyInvoked == 6)
+                    throw ex;
+                return x.Trim();
+            }, x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(3, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(360, ex)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var eleInvoked = 0;
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x =>
+            {
+                eleInvoked++;
+                if (eleInvoked == 6)
+                    throw ex;
+                return Reverse(x);
+            }, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(360, ex)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Comparer_EqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 400, ushort.MaxValue);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Comparer_GetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 400);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Outer_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => outerSubscription.Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(2, inners.Count);
+
+            outerResults.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR")
+            );
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof")
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(570)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Multiple_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+            scheduler.ScheduleAbsolute(280, () => innerSubscriptions["baR"].Dispose());
+            scheduler.ScheduleAbsolute(355, () => innerSubscriptions["Baz"].Dispose());
+            scheduler.ScheduleAbsolute(400, () => innerSubscriptions["qux"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof")
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Escape_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim()));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<string>(600)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Escape_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim()));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(600, ex)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Inner_Escape_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, new Exception())
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim()));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(400, () => outerSubscription.Dispose());
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void GroupBy_NullKeys_Simple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() => xs.GroupBy(x => x[0] == 'b' ? null : x.ToUpper()).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_NullKeys_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            var nullGroup = scheduler.CreateObserver<string>();
+            var err = default(Exception);
+
+            scheduler.ScheduleAbsolute(200, () => xs.GroupBy(x => x[0] == 'b' ? null : x.ToUpper()).Where(g => g.Key == null).Subscribe(g => g.Subscribe(nullGroup), ex_ => err = ex_));
+            scheduler.Start();
+
+            Assert.Same(ex, err);
+
+            nullGroup.Messages.AssertEqual(
+                OnNext(220, "bar"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        static string Reverse(string s)
+        {
+            var sb = new StringBuilder();
+
+            for (var i = s.Length - 1; i >= 0; i--)
+                sb.Append(s[i]);
+
+            return sb.ToString();
+        }
+
+        #endregion
+
+        #region + GroupBy w/capacity +
+
+        private const int _groupByCapacity = 1024;
+
+        [Fact]
+        public void GroupBy_Capacity_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, _groupByCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, DummyFunc<int, int>.Instance, _groupByCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, (Func<int, int>)null, _groupByCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, _groupByCapacity, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, _groupByCapacity, EqualityComparer<int>.Default).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, -1, EqualityComparer<int>.Default));
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_KeyEle_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, _groupByCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, DummyFunc<int, int>.Instance, _groupByCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, (Func<int, int>)null, _groupByCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, _groupByCapacity).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, -1));
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_KeyComparer_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, _groupByCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, _groupByCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, _groupByCapacity, (IEqualityComparer<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, _groupByCapacity, EqualityComparer<int>.Default).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, -1, EqualityComparer<int>.Default));
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Key_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).GroupBy(DummyFunc<int, int>.Instance, _groupByCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy((Func<int, int>)null, _groupByCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, _groupByCapacity).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.GroupBy(DummyFunc<int, int>.Instance, -1));
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_WithKeyComparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(x =>
+                {
+                    keyInvoked++;
+                    return x.Trim();
+                }, _groupByCapacity, comparer).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    _groupByCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    _groupByCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(570, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    }, x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    }, _groupByCapacity, comparer
+                ).Select(g => g.Key),
+                355
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 355)
+            );
+
+            Assert.Equal(5, keyInvoked);
+            Assert.Equal(5, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        if (keyInvoked == 10)
+                            throw ex;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    _groupByCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(9, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        if (eleInvoked == 10)
+                            throw ex;
+                        return Reverse(x);
+                    },
+                    _groupByCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(10, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_ComparerEqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 250, ushort.MaxValue);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    _groupByCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnError<string>(310, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            Assert.Equal(4, keyInvoked);
+            Assert.Equal(3, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_ComparerGetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 410);
+
+            var res = scheduler.Start(() =>
+                xs.GroupBy(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    _groupByCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(8, keyInvoked);
+            Assert.Equal(7, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(570)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Complete_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(570)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex1),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }, ex => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnError<string>(570, ex1)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnError<string>(570, ex1)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnError<string>(570, ex1)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(570, ex1)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(400, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof")
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   ")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var keyInvoked = 0;
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x =>
+            {
+                keyInvoked++;
+                if (keyInvoked == 6)
+                    throw ex;
+                return x.Trim();
+            }, x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(3, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(360, ex)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var eleInvoked = 0;
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x =>
+            {
+                eleInvoked++;
+                if (eleInvoked == 6)
+                    throw ex;
+                return Reverse(x);
+            }, _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(360, ex)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Comparer_EqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 400, ushort.MaxValue);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Comparer_GetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 400);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Outer_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => outerSubscription.Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(2, inners.Count);
+
+            outerResults.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR")
+            );
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof")
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(570)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(570)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Multiple_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), x => Reverse(x), _groupByCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+            scheduler.ScheduleAbsolute(280, () => innerSubscriptions["baR"].Dispose());
+            scheduler.ScheduleAbsolute(355, () => innerSubscriptions["Baz"].Dispose());
+            scheduler.ScheduleAbsolute(400, () => innerSubscriptions["qux"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof")
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Escape_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), _groupByCapacity));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<string>(600)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Escape_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), _groupByCapacity));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(600, ex)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_Inner_Escape_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, new Exception())
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupBy(x => x.Trim(), _groupByCapacity));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(400, () => outerSubscription.Dispose());
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_NullKeys_Simple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() => xs.GroupBy(x => x[0] == 'b' ? null : x.ToUpper(), _groupByCapacity).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupBy_Capacity_NullKeys_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            var nullGroup = scheduler.CreateObserver<string>();
+            var err = default(Exception);
+
+            scheduler.ScheduleAbsolute(200, () => xs.GroupBy(x => x[0] == 'b' ? null : x.ToUpper(), _groupByCapacity).Where(g => g.Key == null).Subscribe(g => g.Subscribe(nullGroup), ex_ => err = ex_));
+            scheduler.Start();
+
+            Assert.Same(ex, err);
+
+            nullGroup.Messages.AssertEqual(
+                OnNext(220, "bar"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        #endregion
+
+    }
+
+    class GroupByComparer : IEqualityComparer<string>
+    {
+        TestScheduler scheduler;
+        int equalsThrowsAfter;
+        ushort getHashCodeThrowsAfter;
+
+        public Exception HashCodeException = new Exception();
+        public Exception EqualsException = new Exception();
+
+        public GroupByComparer(TestScheduler scheduler, ushort equalsThrowsAfter, ushort getHashCodeThrowsAfter)
+        {
+            this.scheduler = scheduler;
+            this.equalsThrowsAfter = equalsThrowsAfter;
+            this.getHashCodeThrowsAfter = getHashCodeThrowsAfter;
+        }
+
+        public GroupByComparer(TestScheduler scheduler)
+            : this(scheduler, ushort.MaxValue, ushort.MaxValue)
+        {
+        }
+
+        public bool Equals(string x, string y)
+        {
+            if (scheduler.Clock > equalsThrowsAfter)
+                throw EqualsException;
+
+            return x.Equals(y, StringComparison.OrdinalIgnoreCase);
+        }
+
+        public int GetHashCode(string obj)
+        {
+            if (scheduler.Clock > getHashCodeThrowsAfter)
+                throw HashCodeException;
+
+            return StringComparer.OrdinalIgnoreCase.GetHashCode(obj);
+        }
+    }
+}

+ 3763 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GroupByUntilTest.cs

@@ -0,0 +1,3763 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class GroupByUntilTest : ReactiveTest
+    {
+        #region + GroupByUntil +
+
+        [Fact]
+        public void GroupByUntil_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>), EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, default(IEqualityComparer<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>), EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, default(IEqualityComparer<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>)));
+        }
+
+        [Fact]
+        public void GroupByUntil_WithKeyComparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnError<string>(570, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key),
+                355
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 355)
+            );
+
+            Assert.Equal(5, keyInvoked);
+            Assert.Equal(5, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        if (keyInvoked == 10)
+                            throw ex;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(9, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        if (eleInvoked == 10)
+                            throw ex;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(10, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_ComparerEqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 250, ushort.MaxValue);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnError<string>(310, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            Assert.Equal(4, keyInvoked);
+            Assert.Equal(3, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_ComparerGetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 410);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(8, keyInvoked);
+            Assert.Equal(7, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnCompleted<string>(320)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnCompleted<string>(570)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Complete_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex1),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }, ex => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnCompleted<string>(320)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(570, ex1)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnError<string>(570, ex1)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(400, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   ")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var keyInvoked = 0;
+            var ex = new Exception();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x =>
+            {
+                keyInvoked++;
+                if (keyInvoked == 6)
+                    throw ex;
+                return x.Trim();
+            }, x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(3, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x =>
+            {
+                eleInvoked++;
+                if (eleInvoked == 6)
+                    throw ex;
+                return Reverse(x);
+            }, g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Comparer_EqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 400, ushort.MaxValue);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Comparer_GetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 400);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Outer_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => outerSubscription.Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(2, inners.Count);
+
+            outerResults.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR")
+            );
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Multiple_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+            scheduler.ScheduleAbsolute(280, () => innerSubscriptions["baR"].Dispose());
+            scheduler.ScheduleAbsolute(355, () => innerSubscriptions["Baz"].Dispose());
+            scheduler.ScheduleAbsolute(400, () => innerSubscriptions["qux"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Escape_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), g => g.Skip(2)));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<string>(600)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Escape_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), g => g.Skip(2)));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(600, ex)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Inner_Escape_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, new Exception())
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), g => g.Skip(2)));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(290, () => outerSubscription.Dispose());
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Default()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim().ToLower();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2)
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "bar"),
+                OnNext(350, "baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "foo"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_DurationSelector_Throws()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, "foo")
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil<string, string, string>(x => x, g => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IGroupedObservable<string, string>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_NullKeys_Simple_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => Observable.Never<Unit>()).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_NullKeys_Simple_Expire1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var n = 0;
+            var res = scheduler.Start(() => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => { if (g.Key == null) n++; return Observable.Timer(TimeSpan.FromTicks(50), scheduler); }).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            Assert.Equal(2, n);
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_NullKeys_Simple_Expire2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var n = 0;
+            var res = scheduler.Start(() => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => { if (g.Key == null) n++; return Observable.Timer(TimeSpan.FromTicks(50), scheduler).IgnoreElements(); }).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            Assert.Equal(2, n);
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_NullKeys_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            var nullGroup = scheduler.CreateObserver<string>();
+            var err = default(Exception);
+
+            scheduler.ScheduleAbsolute(200, () => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => Observable.Never<Unit>()).Where(g => g.Key == null).Subscribe(g => g.Subscribe(nullGroup), ex_ => err = ex_));
+            scheduler.Start();
+
+            Assert.Same(ex, err);
+
+            nullGroup.Messages.AssertEqual(
+                OnNext(220, "bar"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        #endregion
+
+        #region + GroupByUntil w/capacity +
+
+        private const int _groupByUntilCapacity = 1024;
+
+        [Fact]
+        public void GroupByUntil_Capacity_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>), _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, default(IEqualityComparer<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>), _groupByUntilCapacity));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>), _groupByUntilCapacity, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity, default(IEqualityComparer<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(default(IObservable<int>), DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, default(Func<int, int>), DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, _groupByUntilCapacity));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, default(Func<IGroupedObservable<int, int>, IObservable<int>>), _groupByUntilCapacity));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, -1, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, -1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, -1, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.GroupByUntil(DummyObservable<int>.Instance, DummyFunc<int, int>.Instance, DummyFunc<IGroupedObservable<int, int>, IObservable<int>>.Instance, -1));
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_WithKeyComparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnError<string>(570, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key),
+                355
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 355)
+            );
+
+            Assert.Equal(5, keyInvoked);
+            Assert.Equal(5, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        if (keyInvoked == 10)
+                            throw ex;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(9, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        if (eleInvoked == 10)
+                            throw ex;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "FOO"),
+                OnError<string>(480, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 480)
+            );
+
+            Assert.Equal(10, keyInvoked);
+            Assert.Equal(10, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_ComparerEqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 250, ushort.MaxValue);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnError<string>(310, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            Assert.Equal(4, keyInvoked);
+            Assert.Equal(3, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_ComparerGetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 410);
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity,
+                    comparer
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR"),
+                OnNext(350, "Baz"),
+                OnNext(360, "qux"),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(8, keyInvoked);
+            Assert.Equal(7, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnCompleted<string>(320)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnCompleted<string>(570)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Complete_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex1 = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex1),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                scheduler.ScheduleRelative(100, () => innerSubscriptions[group.Key] = group.Subscribe(result));
+            }, ex => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnCompleted<string>(320)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "), // Breaking change > v2.2 - prior to resolving a deadlock, the group would get closed prior to letting this message through
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(570, ex1)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnError<string>(570, ex1)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }));
+
+            scheduler.ScheduleAbsolute(400, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   ")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_KeyThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var keyInvoked = 0;
+            var ex = new Exception();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x =>
+            {
+                keyInvoked++;
+                if (keyInvoked == 6)
+                    throw ex;
+                return x.Trim();
+            }, x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(3, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_EleThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            var eleInvoked = 0;
+            var ex = new Exception();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x =>
+            {
+                eleInvoked++;
+                if (eleInvoked == 6)
+                    throw ex;
+                return Reverse(x);
+            }, g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnError<string>(360, ex)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(360, ex)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnError<string>(360, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 360)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Comparer_EqualsThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, 400, ushort.MaxValue);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.EqualsException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Comparer_GetHashCodeThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler, ushort.MaxValue, 400);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.Start();
+
+            Assert.Equal(4, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnError<string>(420, comparer.HashCodeException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Outer_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => outerSubscription.Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(2, inners.Count);
+
+            outerResults.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "baR")
+            );
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab"),
+                OnNext(390, "rab   "),
+                OnNext(420, "  RAB "),
+                OnCompleted<string>(420)
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB "),
+                OnNext(480, "  zab"),
+                OnNext(510, " ZAb "),
+                OnCompleted<string>(510)
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  "),
+                OnCompleted<string>(570)
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Multiple_Independence()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var comparer = new GroupByComparer(scheduler);
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inners = new Dictionary<string, IObservable<string>>();
+            var innerSubscriptions = new Dictionary<string, IDisposable>();
+            var res = new Dictionary<string, ITestableObserver<string>>();
+            var outerResults = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), x => Reverse(x), g => g.Skip(2), _groupByUntilCapacity, comparer));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                outerResults.OnNext(group.Key);
+                var result = scheduler.CreateObserver<string>();
+                inners[group.Key] = group;
+                res[group.Key] = result;
+                innerSubscriptions[group.Key] = group.Subscribe(result);
+            }, outerResults.OnError, outerResults.OnCompleted));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                foreach (var d in innerSubscriptions.Values)
+                    d.Dispose();
+            });
+
+            scheduler.ScheduleAbsolute(320, () => innerSubscriptions["foo"].Dispose());
+            scheduler.ScheduleAbsolute(280, () => innerSubscriptions["baR"].Dispose());
+            scheduler.ScheduleAbsolute(355, () => innerSubscriptions["Baz"].Dispose());
+            scheduler.ScheduleAbsolute(400, () => innerSubscriptions["qux"].Dispose());
+
+            scheduler.Start();
+
+            Assert.Equal(5, inners.Count);
+
+            res["foo"].Messages.AssertEqual(
+                OnNext(220, "oof  "),
+                OnNext(240, " OoF "),
+                OnNext(310, " Oof"),
+                OnCompleted<string>(310)
+            );
+
+            res["baR"].Messages.AssertEqual(
+                OnNext(270, "  Rab")
+            );
+
+            res["Baz"].Messages.AssertEqual(
+                OnNext(350, "   zaB ")
+            );
+
+            res["qux"].Messages.AssertEqual(
+                OnNext(360, " xuq  ")
+            );
+
+            res["FOO"].Messages.AssertEqual(
+                OnNext(470, " OOF"),
+                OnNext(530, "    oOf    "),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Escape_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), g => g.Skip(2), _groupByUntilCapacity));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<string>(600)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Escape_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, ex)
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), g => g.Skip(2), _groupByUntilCapacity));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }, _ => { }));
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                outerSubscription.Dispose();
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(600, ex)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Inner_Escape_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(310, "foO "),
+                OnNext(470, "FOO "),
+                OnNext(530, "    fOo    "),
+                OnError<string>(570, new Exception())
+            );
+
+            var outer = default(IObservable<IGroupedObservable<string, string>>);
+            var outerSubscription = default(IDisposable);
+            var inner = default(IObservable<string>);
+            var innerSubscription = default(IDisposable);
+            var res = scheduler.CreateObserver<string>();
+
+            scheduler.ScheduleAbsolute(Created, () => outer = xs.GroupByUntil(x => x.Trim(), g => g.Skip(2), _groupByUntilCapacity));
+
+            scheduler.ScheduleAbsolute(Subscribed, () => outerSubscription = outer.Subscribe(group =>
+            {
+                inner = group;
+            }));
+
+            scheduler.ScheduleAbsolute(290, () => outerSubscription.Dispose());
+
+            scheduler.ScheduleAbsolute(600, () => innerSubscription = inner.Subscribe(res));
+
+            scheduler.ScheduleAbsolute(Disposed, () =>
+            {
+                innerSubscription.Dispose();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_Default()
+        {
+            var scheduler = new TestScheduler();
+
+            var keyInvoked = 0;
+            var eleInvoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, "error"),
+                OnNext(110, "error"),
+                OnNext(130, "error"),
+                OnNext(220, "  foo"),
+                OnNext(240, " FoO "),
+                OnNext(270, "baR  "),
+                OnNext(310, "foO "),
+                OnNext(350, " Baz   "),
+                OnNext(360, "  qux "),
+                OnNext(390, "   bar"),
+                OnNext(420, " BAR  "),
+                OnNext(470, "FOO "),
+                OnNext(480, "baz  "),
+                OnNext(510, " bAZ "),
+                OnNext(530, "    fOo    "),
+                OnCompleted<string>(570),
+                OnNext(580, "error"),
+                OnCompleted<string>(600),
+                OnError<string>(650, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil(
+                    x =>
+                    {
+                        keyInvoked++;
+                        return x.Trim().ToLower();
+                    },
+                    x =>
+                    {
+                        eleInvoked++;
+                        return Reverse(x);
+                    },
+                    g => g.Skip(2),
+                    _groupByUntilCapacity
+                ).Select(g => g.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "foo"),
+                OnNext(270, "bar"),
+                OnNext(350, "baz"),
+                OnNext(360, "qux"),
+                OnNext(470, "foo"),
+                OnCompleted<string>(570)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 570)
+            );
+
+            Assert.Equal(12, keyInvoked);
+            Assert.Equal(12, eleInvoked);
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_DurationSelector_Throws()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, "foo")
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.GroupByUntil<string, string, string>(x => x, g => { throw ex; }, _groupByUntilCapacity)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IGroupedObservable<string, string>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_NullKeys_Simple_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => Observable.Never<Unit>(), _groupByUntilCapacity).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_NullKeys_Simple_Expire1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var n = 0;
+            var res = scheduler.Start(() => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => { if (g.Key == null) n++; return Observable.Timer(TimeSpan.FromTicks(50), scheduler); }, _groupByUntilCapacity).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            Assert.Equal(2, n);
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_NullKeys_Simple_Expire2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var n = 0;
+            var res = scheduler.Start(() => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => { if (g.Key == null) n++; return Observable.Timer(TimeSpan.FromTicks(50), scheduler).IgnoreElements(); }, _groupByUntilCapacity).SelectMany(g => g, (g, x) => (g.Key ?? "(null)") + x));
+
+            Assert.Equal(2, n);
+
+            res.Messages.AssertEqual(
+                OnNext(220, "(null)bar"),
+                OnNext(240, "FOOfoo"),
+                OnNext(310, "QUXqux"),
+                OnNext(470, "(null)baz"),
+                OnCompleted<string>(500)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void GroupByUntil_Capacity_NullKeys_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, "bar"),
+                OnNext(240, "foo"),
+                OnNext(310, "qux"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            var nullGroup = scheduler.CreateObserver<string>();
+            var err = default(Exception);
+
+            scheduler.ScheduleAbsolute(200, () => xs.GroupByUntil(x => x[0] == 'b' ? null : x.ToUpper(), g => Observable.Never<Unit>(), _groupByUntilCapacity).Where(g => g.Key == null).Subscribe(g => g.Subscribe(nullGroup), ex_ => err = ex_));
+            scheduler.Start();
+
+            Assert.Same(ex, err);
+
+            nullGroup.Messages.AssertEqual(
+                OnNext(220, "bar"),
+                OnNext(470, "baz"),
+                OnError<string>(500, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        #endregion
+
+        static string Reverse(string s)
+        {
+            var sb = new StringBuilder();
+
+            for (var i = s.Length - 1; i >= 0; i--)
+                sb.Append(s[i]);
+
+            return sb.ToString();
+        }
+    }
+}

+ 1235 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/GroupJoinTest.cs

@@ -0,0 +1,1235 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class GroupJoinTest : ReactiveTest
+    {
+
+        [Fact]
+        public void GroupJoinOp_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupJoin(null, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupJoin(DummyObservable<int>.Instance, null, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupJoin(DummyObservable<int>.Instance, DummyObservable<int>.Instance, default(Func<int, IObservable<int>>), DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupJoin(DummyObservable<int>.Instance, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, default(Func<int, IObservable<int>>), DummyFunc<int, IObservable<int>, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupJoin(DummyObservable<int>.Instance, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, default(Func<int, IObservable<int>, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GroupJoin(DummyObservable<int>.Instance, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_I()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(280))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnNext(830, "9rat"),
+                OnCompleted<string>(990)
+            );
+
+            AssertDurations(xs, xsd, 990);
+            AssertDurations(ys, ysd, 990);
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+#endif
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 800)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+#endif
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_II()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(200))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<int>>(721)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(990)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(910)
+            );
+
+            AssertDurations(xs, xsd, 910);
+            AssertDurations(ys, ysd, 910);
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 721)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 910)
+            );
+#endif
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 910)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_III()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(280))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler).Where(_ => false), y => NewTimer(ysd, y.Interval, scheduler).Where(_ => false), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnNext(830, "9rat"),
+                OnCompleted<string>(990)
+            );
+
+            AssertDurations(xs, xsd, 990);
+            AssertDurations(ys, ysd, 990);
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+#endif
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 800)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+#endif
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_IV()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(200))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<int>>(990)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(980)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(990)
+            );
+
+            AssertDurations(xs, xsd, 990);
+            AssertDurations(ys, ysd, 990);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 980)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+#endif
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_V()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(200))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<int>>(990)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(900)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(990)
+            );
+
+            AssertDurations(xs, xsd, 990);
+            AssertDurations(ys, ysd, 990);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 990)
+            );
+#endif
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_VI()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(30))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(200))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(850)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(20))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(900)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(920)
+            );
+
+            AssertDurations(xs, xsd, 920);
+            AssertDurations(ys, ysd, 920);
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 850)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 920)
+            );
+#endif
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 920)
+            );
+#endif
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_VII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnCompleted<TimeInterval<int>>(210)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(20))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(900)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<string>(210)
+            );
+
+            AssertDurations(xs, xsd, 210);
+            AssertDurations(ys, ysd, 210);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_VIII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(200)))
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(220, new TimeInterval<string>("hat", TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<string>>(230)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, "0hat")
+            );
+
+            AssertDurations(xs, xsd, 1000);
+            AssertDurations(ys, ysd, 1000);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+#endif
+        }
+
+        [Fact]
+        public void GroupJoinOp_Normal_IX()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge(),
+                713
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man")
+            );
+
+            AssertDurations(xs, xsd, 713);
+            AssertDurations(ys, ysd, 713);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 713)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 713)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_I()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnError<TimeInterval<int>>(310, ex)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnError<string>(310, ex)
+            );
+
+            AssertDurations(xs, xsd, 310);
+            AssertDurations(ys, ysd, 310);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_II()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnError<TimeInterval<string>>(722, ex)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnError<string>(722, ex)
+            );
+
+            AssertDurations(xs, xsd, 722);
+            AssertDurations(ys, ysd, 722);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 722)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 722)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_III()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler).SelectMany(x.Value == 6 ? Observable.Throw<long>(ex) : Observable.Empty<long>()), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnError<string>(725, ex)
+            );
+
+            AssertDurations(xs, xsd, 725);
+            AssertDurations(ys, ysd, 725);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 725)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 725)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_IV()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(19))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler).SelectMany(y.Value == "tin" ? Observable.Throw<long>(ex) : Observable.Empty<long>()), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnError<string>(721, ex)
+            );
+
+            AssertDurations(xs, xsd, 721);
+            AssertDurations(ys, ysd, 721);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 721)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 721)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_V()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => { if (x.Value >= 0) throw ex; return Observable.Empty<long>(); }, y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+            AssertDurations(ys, ysd, 210);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_VI()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => { if (y.Value.Length >= 0) throw ex; return Observable.Empty<long>(); }, (x, yy) => yy.Select(y => x.Value + y.Value)).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(215, ex)
+            );
+
+            AssertDurations(xs, xsd, 215);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_VII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => { if (x.Value >= 0) throw ex; return yy.Select(y => x.Value + y.Value); }).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(215, ex)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x - Duration selector is now invoked before the result selector
+            AssertDurations(xs, xsd, 215);
+#endif
+            AssertDurations(ys, ysd, 215);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+        }
+
+        [Fact]
+        public void GroupJoinOp_Error_VIII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.GroupJoin(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, yy) => { if (x.Value >= 0) throw ex; return yy.Select(y => x.Value + y.Value); }).Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x - Duration selector is now invoked before the result selector
+            AssertDurations(xs, xsd, 210);
+#endif
+            AssertDurations(ys, ysd, 210);
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        private ITestableObservable<long> NewTimer(List<ITestableObservable<long>> l, TimeSpan t, TestScheduler scheduler)
+        {
+            var timer = scheduler.CreateColdObservable(OnNext(t.Ticks, 0L), OnCompleted<long>(t.Ticks));
+            l.Add(timer);
+            return timer;
+        }
+
+        private void AssertDurations<T, U>(ITestableObservable<TimeInterval<T>> xs, List<ITestableObservable<U>> xsd, long lastEnd)
+        {
+            Assert.Equal(xs.Messages.Where(x => x.Value.Kind == NotificationKind.OnNext && x.Time <= lastEnd).Count(), xsd.Count);
+
+            foreach (var pair in xs.Messages.Zip(xsd, (x, y) => new { Item1 = x, Item2 = y }))
+            {
+                var start = pair.Item1.Time;
+                var end = Math.Min(start + pair.Item1.Value.Value.Interval.Ticks, lastEnd);
+                pair.Item2.Subscriptions.AssertEqual(
+                    Subscribe(start, end)
+                );
+            }
+        }
+    }
+}

+ 313 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IfTest.cs

@@ -0,0 +1,313 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class IfTest : ReactiveTest
+    {
+
+        [Fact]
+        public void If_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If(null, DummyObservable<int>.Instance, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If(DummyFunc<bool>.Instance, null, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If(DummyFunc<bool>.Instance, DummyObservable<int>.Instance, default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If(null, DummyObservable<int>.Instance, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If(DummyFunc<bool>.Instance, default(IObservable<int>), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If(DummyFunc<bool>.Instance, DummyObservable<int>.Instance, default(IScheduler)));
+        }
+
+        [Fact]
+        public void If_True()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(250, 2),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(310, 3),
+                OnNext(350, 4),
+                OnCompleted<int>(400)
+            );
+
+            var results = scheduler.Start(() => Observable.If(() => true, xs, ys));
+
+            results.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(250, 2),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void If_False()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(250, 2),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(310, 3),
+                OnNext(350, 4),
+                OnCompleted<int>(400)
+            );
+
+            var results = scheduler.Start(() => Observable.If(() => false, xs, ys));
+
+            results.Messages.AssertEqual(
+                OnNext(310, 3),
+                OnNext(350, 4),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void If_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(250, 2),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(310, 3),
+                OnNext(350, 4),
+                OnCompleted<int>(400)
+            );
+
+            var ex = new Exception();
+
+            var results = scheduler.Start(() => Observable.If(() => Throw<bool>(ex), xs, ys));
+
+            results.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void If_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(250, 2)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(310, 3),
+                OnNext(350, 4),
+                OnCompleted<int>(400)
+            );
+
+            var results = scheduler.Start(() => Observable.If(() => true, xs, ys));
+
+            results.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(250, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            ys.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void If_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If<int>(null, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.If<int>(DummyFunc<bool>.Instance, null));
+        }
+
+        [Fact]
+        public void If_Default_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(220, 2),
+                OnNext(330, 3),
+                OnCompleted<int>(440)
+            );
+
+            var b = false;
+
+            scheduler.ScheduleAbsolute(150, () => b = true);
+
+            var results = scheduler.Start(() => Observable.If(() => b, xs));
+
+            results.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnNext(330, 3),
+                OnCompleted<int>(440)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 440)
+            );
+        }
+
+        [Fact]
+        public void If_Default_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(220, 2),
+                OnNext(330, 3),
+                OnError<int>(440, ex)
+            );
+
+            var b = false;
+
+            scheduler.ScheduleAbsolute(150, () => b = true);
+
+            var results = scheduler.Start(() => Observable.If(() => b, xs));
+
+            results.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnNext(330, 3),
+                OnError<int>(440, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 440)
+            );
+        }
+
+        [Fact]
+        public void If_Default_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(220, 2),
+                OnNext(330, 3)
+            );
+
+            var b = false;
+
+            scheduler.ScheduleAbsolute(150, () => b = true);
+
+            var results = scheduler.Start(() => Observable.If(() => b, xs));
+
+            results.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnNext(330, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void If_Default_Other()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(220, 2),
+                OnNext(330, 3),
+                OnError<int>(440, new Exception())
+            );
+
+            var b = true;
+
+            scheduler.ScheduleAbsolute(150, () => b = false);
+
+            var results = scheduler.Start(() => Observable.If(() => b, xs));
+
+            results.Messages.AssertEqual(
+                OnCompleted<int>(200)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void If_Default_Scheduler()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(220, 2),
+                OnNext(330, 3),
+                OnError<int>(440, new Exception())
+            );
+
+            var results = scheduler.Start(() => Observable.If(() => false, xs, scheduler));
+
+            results.Messages.AssertEqual(
+                OnCompleted<int>(201)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+        }
+
+        static T Throw<T>(Exception ex)
+        {
+            throw ex;
+        }
+    }
+}

+ 152 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IgnoreElementsTest.cs

@@ -0,0 +1,152 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class IgnoreElementsTest : ReactiveTest
+    {
+
+        [Fact]
+        public void IgnoreElements_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.IgnoreElements<int>(null));
+        }
+
+        [Fact]
+        public void IgnoreElements_IgnoreElements()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IgnoreElements().IgnoreElements()
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void IgnoreElements_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IgnoreElements()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void IgnoreElements_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnCompleted<int>(610)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IgnoreElements()
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(610)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 610)
+            );
+        }
+
+        [Fact]
+        public void IgnoreElements_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnError<int>(610, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IgnoreElements()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(610, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 610)
+            );
+        }
+
+    }
+}

+ 125 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IntervalTest.cs

@@ -0,0 +1,125 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class IntervalTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Interval_TimeSpan_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Interval(TimeSpan.Zero, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Interval(TimeSpan.Zero, DummyScheduler.Instance).Subscribe(null));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Interval(TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Interval(TimeSpan.FromSeconds(-1), DummyScheduler.Instance));
+        }
+
+        [Fact]
+        public void Interval_TimeSpan_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Interval(TimeSpan.FromTicks(100), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 0L),
+                OnNext(400, 1L),
+                OnNext(500, 2L),
+                OnNext(600, 3L),
+                OnNext(700, 4L),
+                OnNext(800, 5L),
+                OnNext(900, 6L)
+            );
+        }
+
+        [Fact]
+        public void Interval_TimeSpan_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Interval(TimeSpan.FromTicks(0), scheduler),
+                210
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 0L),
+                OnNext(202, 1L),
+                OnNext(203, 2L),
+                OnNext(204, 3L),
+                OnNext(205, 4L),
+                OnNext(206, 5L),
+                OnNext(207, 6L),
+                OnNext(208, 7L),
+                OnNext(209, 8L)
+            );
+        }
+
+        [Fact]
+        public void Interval_TimeSpan_Zero_DefaultScheduler()
+        {
+            var scheduler = new TestScheduler();
+            var observer = scheduler.CreateObserver<long>();
+
+            var completed = new ManualResetEvent(false);
+
+            Observable.Interval(TimeSpan.Zero).TakeWhile(i => i < 10).Subscribe(observer.OnNext, () => completed.Set());
+
+            completed.WaitOne();
+
+            Assert.Equal(10, observer.Messages.Count);
+        }
+
+        [Fact]
+        public void Interval_TimeSpan_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(
+                () => Observable.Interval(TimeSpan.FromTicks(1000), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Interval_TimeSpan_ObserverThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Interval(TimeSpan.FromTicks(1), scheduler);
+
+            xs.Subscribe(x => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler.Start());
+        }
+
+        [Fact]
+        public void Interval_TimeSpan_DefaultScheduler()
+        {
+            Assert.True(Observable.Interval(TimeSpan.FromMilliseconds(1)).ToEnumerable().Take(3).SequenceEqual(new[] { 0L, 1L, 2L }));
+        }
+
+    }
+}

+ 127 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/IsEmptyTest.cs

@@ -0,0 +1,127 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class IsEmptyTest : ReactiveTest
+    {
+
+        [Fact]
+        public void IsEmpty_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.IsEmpty(default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void IsEmpty_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IsEmpty()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, true),
+                OnCompleted<bool>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void IsEmpty_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IsEmpty()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, false),
+                OnCompleted<bool>(210)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void IsEmpty_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IsEmpty()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void IsEmpty_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.IsEmpty()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+    }
+}

+ 1119 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/JoinTest.cs

@@ -0,0 +1,1119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class JoinTest : ReactiveTest
+    {
+
+        [Fact]
+        public void JoinOp_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Join(null, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Join(DummyObservable<int>.Instance, null, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Join(DummyObservable<int>.Instance, DummyObservable<int>.Instance, default(Func<int, IObservable<int>>), DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Join(DummyObservable<int>.Instance, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, default(Func<int, IObservable<int>>), DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Join(DummyObservable<int>.Instance, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, default(Func<int, int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Join(DummyObservable<int>.Instance, DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void JoinOp_Normal_I()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnNext(830, "9rat"),
+                OnCompleted<string>(900)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 800)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#endif
+
+            AssertDurations(xs, xsd, 900);
+            AssertDurations(ys, ysd, 900);
+        }
+
+        [Fact]
+        public void JoinOp_Normal_II()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(200))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<int>>(721)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(990)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(910)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 721)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 910)
+            );
+#endif
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 910)
+            );
+
+            AssertDurations(xs, xsd, 910);
+            AssertDurations(ys, ysd, 910);
+        }
+
+        [Fact]
+        public void JoinOp_Normal_III()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler).Where(_ => false), y => NewTimer(ysd, y.Interval, scheduler).Where(_ => false), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnNext(830, "9rat"),
+                OnCompleted<string>(900)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 800)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#endif
+
+            AssertDurations(xs, xsd, 900);
+            AssertDurations(ys, ysd, 900);
+        }
+
+        [Fact]
+        public void JoinOp_Normal_IV()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(200))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<int>>(990)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(980)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(980)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 980)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 980)
+            );
+
+            AssertDurations(xs, xsd, 980);
+            AssertDurations(ys, ysd, 980);
+        }
+
+        [Fact]
+        public void JoinOp_Normal_V()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(200))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnCompleted<TimeInterval<int>>(990)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(900)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(922)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 922)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#else
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 922)
+            );
+#endif
+
+            AssertDurations(xs, xsd, 922);
+            AssertDurations(ys, ysd, 922);
+        }
+
+        [Fact]
+        public void JoinOp_Normal_VI()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(30))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(200))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(850)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(20))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(900)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnNext(732, "7wig"),
+                OnNext(732, "8wig"),
+                OnCompleted<string>(900)
+            );
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 850)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+#endif
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+
+            AssertDurations(xs, xsd, 900);
+            AssertDurations(ys, ysd, 900);
+        }
+
+        [Fact]
+        public void JoinOp_Normal_VII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value),
+                713
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 713)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 713)
+            );
+
+            AssertDurations(xs, xsd, 713);
+            AssertDurations(ys, ysd, 713);
+        }
+
+        [Fact]
+        public void JoinOp_Error_I()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnError<TimeInterval<int>>(310, ex)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnError<string>(310, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            AssertDurations(xs, xsd, 310);
+            AssertDurations(ys, ysd, 310);
+        }
+
+        [Fact]
+        public void JoinOp_Error_II()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnError<TimeInterval<string>>(722, ex)
+            );
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnError<string>(722, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 722)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 722)
+            );
+
+            AssertDurations(xs, xsd, 722);
+            AssertDurations(ys, ysd, 722);
+        }
+
+        [Fact]
+        public void JoinOp_Error_III()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler).SelectMany(x.Value == 6 ? Observable.Throw<long>(ex) : Observable.Empty<long>()), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnNext(722, "6rat"),
+                OnNext(722, "7rat"),
+                OnNext(722, "8rat"),
+                OnError<string>(725, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 725)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 725)
+            );
+
+            AssertDurations(xs, xsd, 725);
+            AssertDurations(ys, ysd, 725);
+        }
+
+        [Fact]
+        public void JoinOp_Error_IV()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(19))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler).SelectMany(y.Value == "tin" ? Observable.Throw<long>(ex) : Observable.Empty<long>()), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, "0hat"),
+                OnNext(217, "0bat"),
+                OnNext(219, "1hat"),
+                OnNext(300, "3wag"),
+                OnNext(300, "3pig"),
+                OnNext(305, "3cup"),
+                OnNext(310, "4wag"),
+                OnNext(310, "4pig"),
+                OnNext(310, "4cup"),
+                OnNext(702, "6tin"),
+                OnNext(710, "7tin"),
+                OnNext(712, "6man"),
+                OnNext(712, "7man"),
+                OnNext(720, "8tin"),
+                OnNext(720, "8man"),
+                OnError<string>(721, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 721)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 721)
+            );
+
+            AssertDurations(xs, xsd, 721);
+            AssertDurations(ys, ysd, 721);
+        }
+
+        [Fact]
+        public void JoinOp_Error_V()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => { if (x.Value >= 0) throw ex; return Observable.Empty<long>(); }, y => NewTimer(ysd, y.Interval, scheduler), (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+
+            AssertDurations(ys, ysd, 210);
+        }
+
+        [Fact]
+        public void JoinOp_Error_VI()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => { if (y.Value.Length >= 0) throw ex; return Observable.Empty<long>(); }, (x, y) => x.Value + y.Value)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(215, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            AssertDurations(xs, xsd, 215);
+        }
+
+        [Fact]
+        public void JoinOp_Error_VII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => { if (x.Value >= 0) throw ex; return x.Value + y.Value; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(215, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            AssertDurations(xs, xsd, 215);
+            AssertDurations(ys, ysd, 215);
+        }
+
+        [Fact]
+        public void JoinOp_Error_VIII()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, new TimeInterval<int>(0, TimeSpan.FromTicks(10))),
+                OnNext(219, new TimeInterval<int>(1, TimeSpan.FromTicks(5))),
+                OnNext(240, new TimeInterval<int>(2, TimeSpan.FromTicks(10))),
+                OnNext(300, new TimeInterval<int>(3, TimeSpan.FromTicks(100))),
+                OnNext(310, new TimeInterval<int>(4, TimeSpan.FromTicks(80))),
+                OnNext(500, new TimeInterval<int>(5, TimeSpan.FromTicks(90))),
+                OnNext(700, new TimeInterval<int>(6, TimeSpan.FromTicks(25))),
+                OnNext(710, new TimeInterval<int>(7, TimeSpan.FromTicks(300))),
+                OnNext(720, new TimeInterval<int>(8, TimeSpan.FromTicks(100))),
+                OnNext(830, new TimeInterval<int>(9, TimeSpan.FromTicks(10))),
+                OnCompleted<TimeInterval<int>>(900)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(215, new TimeInterval<string>("hat", TimeSpan.FromTicks(20))),
+                OnNext(217, new TimeInterval<string>("bat", TimeSpan.FromTicks(1))),
+                OnNext(290, new TimeInterval<string>("wag", TimeSpan.FromTicks(200))),
+                OnNext(300, new TimeInterval<string>("pig", TimeSpan.FromTicks(10))),
+                OnNext(305, new TimeInterval<string>("cup", TimeSpan.FromTicks(50))),
+                OnNext(600, new TimeInterval<string>("yak", TimeSpan.FromTicks(90))),
+                OnNext(702, new TimeInterval<string>("tin", TimeSpan.FromTicks(20))),
+                OnNext(712, new TimeInterval<string>("man", TimeSpan.FromTicks(10))),
+                OnNext(722, new TimeInterval<string>("rat", TimeSpan.FromTicks(200))),
+                OnNext(732, new TimeInterval<string>("wig", TimeSpan.FromTicks(5))),
+                OnCompleted<TimeInterval<string>>(800)
+            );
+
+            var ex = new Exception();
+
+            var xsd = new List<ITestableObservable<long>>();
+            var ysd = new List<ITestableObservable<long>>();
+
+            var res = scheduler.Start(() =>
+                xs.Join(ys, x => NewTimer(xsd, x.Interval, scheduler), y => NewTimer(ysd, y.Interval, scheduler), (x, y) => { if (x.Value >= 0) throw ex; return x.Value + y.Value; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(215, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 215)
+            );
+
+            AssertDurations(xs, xsd, 215);
+            AssertDurations(ys, ysd, 215);
+        }
+
+        private ITestableObservable<long> NewTimer(List<ITestableObservable<long>> l, TimeSpan t, TestScheduler scheduler)
+        {
+            var timer = scheduler.CreateColdObservable(OnNext(t.Ticks, 0L), OnCompleted<long>(t.Ticks));
+            l.Add(timer);
+            return timer;
+        }
+
+        private void AssertDurations<T, U>(ITestableObservable<TimeInterval<T>> xs, List<ITestableObservable<U>> xsd, long lastEnd)
+        {
+            Assert.Equal(xs.Messages.Where(x => x.Value.Kind == NotificationKind.OnNext && x.Time <= lastEnd).Count(), xsd.Count);
+
+            foreach (var pair in xs.Messages.Zip(xsd, (x, y) => new { Item1 = x, Item2 = y }))
+            {
+                var start = pair.Item1.Time;
+                var end = Math.Min(start + pair.Item1.Value.Value.Interval.Ticks, lastEnd);
+                pair.Item2.Subscriptions.AssertEqual(
+                    Subscribe(start, end)
+                );
+            }
+        }
+
+    }
+}

+ 240 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastAsyncTest.cs

@@ -0,0 +1,240 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class LastAsyncTest : ReactiveTest
+    {
+
+        [Fact]
+        public void LastAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastAsync(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastAsync(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastAsync(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void LastAsync_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_Many()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_Predicate()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_Predicate_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_Predicate_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LastAsync_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastAsync(x => { if (x < 4) return x % 2 == 1; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 242 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastOrDefaultAsyncTest.cs

@@ -0,0 +1,242 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class LastOrDefaultAsyncTest : ReactiveTest
+    {
+
+        [Fact]
+        public void LastOrDefaultAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastOrDefaultAsync(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastOrDefaultAsync(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastOrDefaultAsync(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_Many()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_Predicate()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_Predicate_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_Predicate_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LastOrDefaultAsync_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LastOrDefaultAsync(x => { if (x < 4) return x % 2 == 1; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 75 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastOrDefaultTest.cs

@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class LastOrDefaultTest : ReactiveTest
+    {
+
+        [Fact]
+        public void LastOrDefault_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastOrDefault(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastOrDefault(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LastOrDefault(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void LastOrDefault_Empty()
+        {
+            Assert.Equal(default(int), Observable.Empty<int>().LastOrDefault());
+        }
+
+        [Fact]
+        public void LastOrDefaultPredicate_Empty()
+        {
+            Assert.Equal(default(int), Observable.Empty<int>().LastOrDefault(_ => true));
+        }
+
+        [Fact]
+        public void LastOrDefault_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).LastOrDefault());
+        }
+
+        [Fact]
+        public void LastOrDefault_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.LastOrDefault());
+        }
+
+        [Fact]
+        public void LastOrDefault_Range()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Range(value - 9, 10).LastOrDefault());
+        }
+
+        [Fact]
+        public void LastOrDefaultPredicate_Range()
+        {
+            var value = 42;
+            Assert.Equal(50, Observable.Range(value, 10).LastOrDefault(i => i % 2 == 0));
+        }
+
+    }
+}

+ 75 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LastTest.cs

@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class LastTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Last_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Last(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Last(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Last(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void Last_Empty()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Empty<int>().Last());
+        }
+
+        [Fact]
+        public void LastPredicate_Empty()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Empty<int>().Last(_ => true));
+        }
+
+        [Fact]
+        public void Last_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).Last());
+        }
+
+        [Fact]
+        public void Last_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.Last());
+        }
+
+        [Fact]
+        public void Last_Range()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Range(value - 9, 10).Last());
+        }
+
+        [Fact]
+        public void LastPredicate_Range()
+        {
+            var value = 42;
+            Assert.Equal(50, Observable.Range(value, 10).Last(i => i % 2 == 0));
+        }
+
+    }
+}

+ 183 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LatestTest.cs

@@ -0,0 +1,183 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace ReactiveTests.Tests
+{
+    public class LatestTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Latest_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Latest(default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void Latest1()
+        {
+            var disposed = false;
+            var evt = new AutoResetEvent(false);
+            var src = Observable.Create<int>(obs =>
+            {
+                Task.Run(() =>
+                {
+                    evt.WaitOne();
+                    obs.OnNext(1);
+                    evt.WaitOne();
+                    obs.OnNext(2);
+                    evt.WaitOne();
+                    obs.OnCompleted();
+                });
+
+                return () => { disposed = true; };
+            });
+
+            var res = src.Latest().GetEnumerator();
+
+            Task.Run(async () =>
+            {
+                await Task.Delay(250);
+                evt.Set();
+            });
+
+            Assert.True(res.MoveNext());
+            Assert.Equal(1, res.Current);
+
+            evt.Set();
+            Assert.True(((IEnumerator)res).MoveNext());
+            Assert.Equal(2, ((IEnumerator)res).Current);
+
+            evt.Set();
+            Assert.False(res.MoveNext());
+
+            ReactiveAssert.Throws<NotSupportedException>(() => res.Reset());
+
+            res.Dispose();
+            //ReactiveAssert.Throws<ObjectDisposedException>(() => res.MoveNext());
+            Assert.True(disposed);
+        }
+
+        [Fact]
+        public void Latest2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnNext(270, 7),
+                OnNext(280, 8),
+                OnNext(290, 9),
+                OnCompleted<int>(300)
+            );
+
+            var res = xs.Latest();
+
+            var e1 = default(IEnumerator<int>);
+            scheduler.ScheduleAbsolute(205, () =>
+            {
+                e1 = res.GetEnumerator();
+            });
+
+            var o1 = new List<int>();
+            scheduler.ScheduleAbsolute(235, () =>
+            {
+                Assert.True(e1.MoveNext());
+                o1.Add(e1.Current);
+            });
+            scheduler.ScheduleAbsolute(265, () =>
+            {
+                Assert.True(e1.MoveNext());
+                o1.Add(e1.Current);
+            });
+
+            scheduler.ScheduleAbsolute(285, () => e1.Dispose());
+
+            var e2 = default(IEnumerator);
+            scheduler.ScheduleAbsolute(255, () =>
+            {
+                e2 = ((IEnumerable)res).GetEnumerator();
+            });
+
+            var o2 = new List<int>();
+            scheduler.ScheduleAbsolute(265, () =>
+            {
+                Assert.True(e2.MoveNext());
+                o2.Add((int)e2.Current);
+            });
+            scheduler.ScheduleAbsolute(275, () =>
+            {
+                Assert.True(e2.MoveNext());
+                o2.Add((int)e2.Current);
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(205, 285),
+                Subscribe(255, 300)
+            );
+
+            o1.AssertEqual(3, 6);
+            o2.AssertEqual(6, 7);
+        }
+
+        [Fact]
+        public void Latest_Error()
+        {
+            SynchronizationContext.SetSynchronizationContext(null);
+
+            var ex = new Exception();
+
+            var evt = new AutoResetEvent(false);
+            var src = Observable.Create<int>(obs =>
+            {
+                Task.Run(() =>
+                {
+                    evt.WaitOne();
+                    obs.OnNext(1);
+                    evt.WaitOne();
+                    obs.OnError(ex);
+                });
+
+                return () => { };
+            });
+
+            var res = src.Latest().GetEnumerator();
+
+            Task.Run(async () =>
+            {
+                await Task.Delay(250);
+                evt.Set();
+            });
+
+            Assert.True(res.MoveNext());
+            Assert.Equal(1, res.Current);
+
+            evt.Set();
+
+            ReactiveAssert.Throws(ex, () => res.MoveNext());
+        }
+
+    }
+}

+ 47 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LetTest.cs

@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class LetTest : ReactiveTest
+    {
+        #region Let
+
+        [Fact]
+        public void Let_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Let(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.Let<int, int>(someObservable, null));
+        }
+
+        [Fact]
+        public void Let_CallsFunctionImmediately()
+        {
+            bool called = false;
+            Observable.Empty<int>().Let(x => { called = true; return x; });
+            Assert.True(called);
+        }
+
+        #endregion
+
+    }
+}

+ 461 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/LongCountTest.cs

@@ -0,0 +1,461 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class LongCountTest : ReactiveTest
+    {
+
+        [Fact]
+        public void LongCount_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LongCount(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LongCount(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.LongCount(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void LongCount_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 1L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+#if !NO_PERF && !NO_THREAD
+        [Fact]
+        public void LongCount_InjectOverflow()
+        {
+            var xs = Observable.Return(42, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<int>(xs, long.MaxValue).LongCount();
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+#endif
+
+        [Fact]
+        public void LongCount_Predicate_Empty_True()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Empty_False()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => false)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Return_True()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 1L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Return_False()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => false)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Some_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(x => x < 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Some_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Some_Even()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(x => x % 2 == 0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Throw_True()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Throw_False()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => false)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(_ => true)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void LongCount_Predicate_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnCompleted<int>(240)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.LongCount(x =>
+                {
+                    if (x == 3)
+                        throw ex;
+
+                    return true;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+#if !NO_PERF && !NO_THREAD && !CRIPPLED_REFLECTION
+        [Fact]
+        public void LongCount_Predicate_InjectOverflow()
+        {
+            var xs = Observable.Return(42, ThreadPoolScheduler.Instance);
+
+            var res = new OverflowInjection<int>(xs, long.MaxValue).LongCount(_ => true);
+
+            ReactiveAssert.Throws<OverflowException>(() => res.ForEach(_ => { }));
+        }
+#endif
+
+    }
+}

+ 133 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ManySelectTest.cs

@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ManySelectTest : ReactiveTest
+    {
+
+        [Fact]
+        public void ManySelect_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ManySelect<int, int>(null, DummyFunc<IObservable<int>, int>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ManySelect<int, int>(DummyObservable<int>.Instance, null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ManySelect<int, int>(DummyObservable<int>.Instance, DummyFunc<IObservable<int>, int>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ManySelect<int, int>(null, DummyFunc<IObservable<int>, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ObservableEx.ManySelect<int, int>(DummyObservable<int>.Instance, null));
+        }
+
+        [Fact]
+        public void ManySelect_Law_1()
+        {
+            var xs = Observable.Range(1, 0);
+
+            var left = xs.ManySelect(Observable.First);
+            var right = xs;
+
+            Assert.True(left.SequenceEqual(right).First());
+        }
+
+        [Fact]
+        public void ManySelect_Law_2()
+        {
+            var xs = Observable.Range(1, 10);
+            Func<IObservable<int>, int> f = ys => ys.Count().First();
+
+            var left = xs.ManySelect(f).First();
+            var right = f(xs);
+
+            Assert.Equal(left, right);
+        }
+
+        [Fact]
+        public void ManySelect_Law_3()
+        {
+            var xs = Observable.Range(1, 10);
+            Func<IObservable<int>, int> f = ys => ys.Count().First();
+            Func<IObservable<int>, int> g = ys => ys.Last();
+
+            var left = xs.ManySelect(f).ManySelect(g);
+            var right = xs.ManySelect(ys => g(ys.ManySelect(f)));
+
+            Assert.True(left.SequenceEqual(right).First());
+        }
+
+        [Fact]
+        public void ManySelect_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(220, 2),
+                OnNext(270, 3),
+                OnNext(410, 4),
+                OnCompleted<int>(500)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ManySelect(ys => ys.First(), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 2),
+                OnNext(271, 3),
+                OnNext(411, 4),
+                OnCompleted<int>(501)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void ManySelect_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(220, 2),
+                OnNext(270, 3),
+                OnNext(410, 4),
+                OnError<int>(500, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.ManySelect(ys => ys.First(), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 2),
+                OnNext(271, 3),
+                OnNext(411, 4),
+                OnError<int>(501, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+    }
+}

+ 121 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MaterializeTest.cs

@@ -0,0 +1,121 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class MaterializeTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Materialize_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Materialize<int>(null));
+        }
+
+        [Fact]
+        public void Materialize_Never()
+        {
+            var scheduler = new TestScheduler();
+            var res = scheduler.Start(() =>
+                Observable.Never<int>().Materialize()
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Materialize_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Materialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, Notification.CreateOnCompleted<int>()),
+                OnCompleted<Notification<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Materialize_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Materialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, Notification.CreateOnNext(2)),
+                OnNext(250, Notification.CreateOnCompleted<int>()),
+                OnCompleted<Notification<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Materialize_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Materialize()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, Notification.CreateOnError<int>(ex)),
+                OnCompleted<Notification<int>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+    }
+}

+ 381 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MaxByTest.cs

@@ -0,0 +1,381 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class MaxByTest : ReactiveTest
+    {
+        #region + MaxBy +
+
+        [Fact]
+        public void MaxBy_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MaxBy(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MaxBy(DummyObservable<int>.Instance, default(Func<int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MaxBy(default(IObservable<int>), x => x, Comparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MaxBy(DummyObservable<int>.Instance, default(Func<int, int>), Comparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MaxBy(DummyObservable<int>.Instance, x => x, null));
+        }
+
+        [Fact]
+        public void MaxBy_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.Count == 0),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(2, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "a"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(4, "c")),
+                OnNext(230, new KeyValuePair<int, string>(2, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(4, "c"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Multiple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(215, new KeyValuePair<int, string>(2, "d")),
+                OnNext(220, new KeyValuePair<int, string>(3, "c")),
+                OnNext(225, new KeyValuePair<int, string>(2, "y")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnNext(235, new KeyValuePair<int, string>(4, "r")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(4, "a"),
+                    new KeyValuePair<int, string>(4, "r"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+        [Fact]
+        public void MaxBy_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnError<KeyValuePair<int, string>>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z"))
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Comparer_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.Count == 0),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Comparer_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(2, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "a"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Comparer_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(4, "c")),
+                OnNext(230, new KeyValuePair<int, string>(2, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "a"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Comparer_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnError<KeyValuePair<int, string>>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_Comparer_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z"))
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_SelectorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(2, "c")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy<KeyValuePair<int, string>, int>(x => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxBy_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(2, "c")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MaxBy(x => x.Key, new ThrowingComparer<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        #endregion
+
+    }
+}

+ 2428 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MaxTest.cs

@@ -0,0 +1,2428 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class MaxTest : ReactiveTest
+    {
+        [Fact]
+        public void Max_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<double>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<float>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<decimal>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<long>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<int?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<double?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<float?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<decimal?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<long?>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(int)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(double)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(float)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(decimal)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(long)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(int?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(double?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(float?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(decimal?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => default(long?)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, double>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, float>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, decimal>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, long>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, int?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, double?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, float?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, decimal?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, long?>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), Comparer<DateTime>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(IComparer<DateTime>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => ""));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, string>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(default(IObservable<DateTime>), _ => "", Comparer<string>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), default(Func<DateTime, string>), Comparer<string>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Max(Observable.Empty<DateTime>(), _ => "", default(IComparer<string>)));
+        }
+
+        [Fact]
+        public void Max_Int32_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Int32_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Int32_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 3),
+                OnNext(220, 4),
+                OnNext(230, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Int32_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Int32_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Int64_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Int64_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnNext(210, 2L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Int64_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnNext(210, 3L),
+                OnNext(220, 4L),
+                OnNext(230, 2L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Int64_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnError<long>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Int64_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Float_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Float_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnNext(210, 2f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2f),
+                OnCompleted<float>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Float_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnNext(210, 3f),
+                OnNext(220, 4f),
+                OnNext(230, 2f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4f),
+                OnCompleted<float>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Float_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnError<float>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Float_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Double_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Double_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnNext(210, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Double_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnNext(210, 3.0),
+                OnNext(220, 4.0),
+                OnNext(230, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Double_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnError<double>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Double_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Decimal_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Decimal_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnNext(210, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Decimal_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnNext(210, 3m),
+                OnNext(220, 4m),
+                OnNext(230, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4m),
+                OnCompleted<decimal>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Decimal_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnError<decimal>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Decimal_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int32_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(int?)),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int32_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int32_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)null),
+                OnNext(220, (int?)4),
+                OnNext(230, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)4),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int32_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)null),
+                OnNext(220, (int?)2),
+                OnNext(230, (int?)4),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)4),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_GeneralNullableMaxTest_LhsNull()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)null),
+                OnNext(220, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_GeneralNullableMaxTest_RhsNull()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnNext(220, (int?)null),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_GeneralNullableMaxTest_Less()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnNext(220, (int?)3),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)3),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_GeneralNullableMaxTest_Greater()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)3),
+                OnNext(220, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)3),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int32_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnError<int?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int32_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int64_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(long?)),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int64_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int64_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)null),
+                OnNext(220, (long?)4L),
+                OnNext(230, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (long?)4L),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int64_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)null),
+                OnNext(220, (long?)2L),
+                OnNext(230, (long?)4L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (long?)4L),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int64_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnError<long?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Int64_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Float_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(float?)),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Float_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Float_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)null),
+                OnNext(220, (float?)4f),
+                OnNext(230, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)4f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Float_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)null),
+                OnNext(220, (float?)2f),
+                OnNext(230, (float?)4f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)4f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Float_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnError<float?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Float_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Double_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(double?)),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Double_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Double_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)null),
+                OnNext(220, (double?)4.0),
+                OnNext(230, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)4.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Double_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)null),
+                OnNext(220, (double?)2.0),
+                OnNext(230, (double?)4.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)4.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Double_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnError<double?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Double_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Decimal_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(decimal?)),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Decimal_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Decimal_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)null),
+                OnNext(220, (decimal?)4m),
+                OnNext(230, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)4m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Decimal_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)null),
+                OnNext(220, (decimal?)2m),
+                OnNext(230, (decimal?)4m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)4m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Decimal_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnError<decimal?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Max_Nullable_Decimal_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x - Behavior for reference types
+            res.Messages.AssertEqual(
+                OnNext(250, default(string)),
+                OnCompleted<string>(250)
+            );
+#else
+            res.Messages.AssertEqual(
+                OnError<string>(250, e => e is InvalidOperationException)
+            );
+#endif
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "a"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "b"),
+                OnNext(220, "c"),
+                OnNext(230, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "c"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1993, 2, 11)),
+                OnNext(220, new DateTime(2003, 2, 11)),
+                OnNext(230, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(2003, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnError<string>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnError<DateTime>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Comparer_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x - Behavior for reference types
+            res.Messages.AssertEqual(
+                OnNext(250, default(string)),
+                OnCompleted<string>(250)
+            );
+#else
+            res.Messages.AssertEqual(
+                OnError<string>(250, e => e is InvalidOperationException)
+            );
+#endif
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Comparer_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Comparer_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "a"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Comparer_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Comparer_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "b"),
+                OnNext(220, "c"),
+                OnNext(230, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "a"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Comparer_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1993, 2, 11)),
+                OnNext(220, new DateTime(2003, 2, 11)),
+                OnNext(230, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Comparer_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnError<string>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Comparer_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnError<DateTime>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_Comparer_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_Comparer_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Reference_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "b"),
+                OnNext(220, "c"),
+                OnNext(230, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ThrowingComparer<string>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Value_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1993, 2, 11)),
+                OnNext(220, new DateTime(2003, 2, 11)),
+                OnNext(230, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Max(new ThrowingComparer<DateTime>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Int32()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => (int)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 3),
+                OnCompleted<int>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Int64()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => (long)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 3L),
+                OnCompleted<long>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Single()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => (float)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 3.0f),
+                OnCompleted<float>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Double()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => (double)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 3.0),
+                OnCompleted<double>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Decimal()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => (decimal)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 3.0m),
+                OnCompleted<decimal>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Int32_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => x == "fo" ? default(int?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (int?)3),
+                OnCompleted<int?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Int64_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => x == "fo" ? default(long?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (long?)3.0),
+                OnCompleted<long?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Single_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => x == "fo" ? default(float?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (float?)3.0),
+                OnCompleted<float?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Double_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => x == "fo" ? default(double?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (double?)3.0),
+                OnCompleted<double?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Max_Selector_Regular_Decimal_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => x == "fo" ? default(decimal?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (decimal?)3.0),
+                OnCompleted<decimal?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Selector_Regular()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "bar"),
+                OnNext(220, "qux"),
+                OnNext(230, "foo"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => new string(x.ToCharArray().Reverse().ToArray())));
+
+            res.Messages.AssertEqual(
+                OnNext(240, "xuq"),
+                OnCompleted<string>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void MaxOfT_Selector_Regular_Comparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "bar"),
+                OnNext(220, "qux"),
+                OnNext(230, "foo"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Max(x => new string(x.ToCharArray().Reverse().ToArray()), new ReverseComparer<string>(Comparer<string>.Default)));
+
+            res.Messages.AssertEqual(
+                OnNext(240, "oof"),
+                OnCompleted<string>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+        
+    }
+
+    internal class ReverseComparer<T> : IComparer<T>
+    {
+        private IComparer<T> _comparer;
+
+        public ReverseComparer(IComparer<T> comparer)
+        {
+            _comparer = comparer;
+        }
+
+        public int Compare(T x, T y)
+        {
+            return -_comparer.Compare(x, y);
+        }
+    }
+
+    internal class ThrowingComparer<T> : IComparer<T>
+    {
+        private Exception _ex;
+
+        public ThrowingComparer(Exception ex)
+        {
+            _ex = ex;
+        }
+
+        public int Compare(T x, T y)
+        {
+            throw _ex;
+        }
+    }
+}

+ 1966 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MergeTest.cs

@@ -0,0 +1,1966 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class MergeTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Merge_ArgumentChecking()
+        {
+            var xs = DummyObservable<int>.Instance;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(default(IScheduler), xs, xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(xs, xs, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(xs, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(default(IObservable<int>), xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge((IEnumerable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).Merge(xs, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => xs.Merge(default(IObservable<int>), DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge((IEnumerable<IObservable<int>>)null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(new IObservable<int>[0], default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge((IObservable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(DummyScheduler.Instance, (IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge((IObservable<Task<int>>)null));
+        }
+
+        [Fact]
+        public void Merge_DefaultScheduler()
+        {
+            var xs = Observable.Merge<int>(Observable.Return(42), Observable.Return(43), Observable.Return(44));
+            var res = xs.ToList().Single();
+            Assert.True(new[] { 42, 43, 44 }.SequenceEqual(res));
+        }
+
+        [Fact]
+        public void Merge_Never2()
+        {
+            var scheduler = new TestScheduler();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, n1, n2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(201, 1000)
+            );
+
+            n2.Subscriptions.AssertEqual(
+                Subscribe(202, 1000)
+            );
+        }
+
+        [Fact]
+        public void Merge_Never3()
+        {
+            var scheduler = new TestScheduler();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var n3 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, n1, n2, n3)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(201, 1000)
+            );
+
+            n2.Subscriptions.AssertEqual(
+                Subscribe(202, 1000)
+            );
+
+            n3.Subscriptions.AssertEqual(
+                Subscribe(203, 1000)
+            );
+        }
+
+        [Fact]
+        public void Merge_Empty2()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(210)
+            );
+
+            var e2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, e2)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 210)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(202, 230)
+            );
+        }
+
+        [Fact]
+        public void Merge_Empty3()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(210)
+            );
+
+            var e2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(230)
+            );
+
+            var e3 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(240)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, e2, e3)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(240)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 210)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(202, 230)
+            );
+
+            e3.Subscriptions.AssertEqual(
+                Subscribe(203, 240)
+            );
+        }
+
+        [Fact]
+        public void Merge_EmptyDelayed2_RightLast()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(240)
+            );
+
+            var e2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, e2)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 240)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(202, 250)
+            );
+        }
+
+        [Fact]
+        public void Merge_EmptyDelayed2_LeftLast()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var e2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(240)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, e2)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 250)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(202, 240)
+            );
+        }
+
+        [Fact]
+        public void Merge_EmptyDelayed3_MiddleLast()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(245)
+            );
+
+            var e2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var e3 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(240)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, e2, e3)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            e2.Subscriptions.AssertEqual(
+                Subscribe(202, 250)
+            );
+
+            e3.Subscriptions.AssertEqual(
+                Subscribe(203, 240)
+            );
+        }
+
+        [Fact]
+        public void Merge_EmptyNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(245)
+            );
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, n1)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(202, 1000)
+            );
+        }
+
+        [Fact]
+        public void Merge_NeverEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(245)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, n1, e1)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(201, 1000)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(202, 245)
+            );
+        }
+
+        [Fact]
+        public void Merge_ReturnNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var r1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(245)
+            );
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, r1, n1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2)
+            );
+
+            r1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(202, 1000)
+            );
+        }
+
+        [Fact]
+        public void Merge_NeverReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var r1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(245)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, n1, r1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2)
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(201, 1000)
+            );
+
+            r1.Subscriptions.AssertEqual(
+                Subscribe(202, 245)
+            );
+        }
+
+        [Fact]
+        public void Merge_ErrorNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(245, ex)
+            );
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, n1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(245, ex)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(202, 245)
+            );
+        }
+
+        [Fact]
+        public void Merge_NeverError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var n1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(245, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, n1, e1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnError<int>(245, ex)
+            );
+
+            n1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(202, 245)
+            );
+        }
+
+        [Fact]
+        public void Merge_EmptyReturn()
+        {
+            var scheduler = new TestScheduler();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(245)
+            );
+
+            var r1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, e1, r1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            r1.Subscriptions.AssertEqual(
+                Subscribe(202, 250)
+            );
+        }
+
+        [Fact]
+        public void Merge_ReturnEmpty()
+        {
+            var scheduler = new TestScheduler();
+
+            var r1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(245)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, r1, e1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            r1.Subscriptions.AssertEqual(
+                Subscribe(201, 250)
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(202, 245)
+            );
+        }
+
+        [Fact]
+        public void Merge_Lots2()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 4),
+                OnNext(230, 6),
+                OnNext(240, 8),
+                OnCompleted<int>(245)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 3),
+                OnNext(225, 5),
+                OnNext(235, 7),
+                OnNext(245, 9),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(scheduler, o1, o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(220, 4),
+                OnNext(225, 5),
+                OnNext(230, 6),
+                OnNext(235, 7),
+                OnNext(240, 8),
+                OnNext(245, 9),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(202, 250)
+            );
+        }
+
+        [Fact]
+        public void Merge_Lots3()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(225, 5),
+                OnNext(240, 8),
+                OnCompleted<int>(245)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 3),
+                OnNext(230, 6),
+                OnNext(245, 9),
+                OnCompleted<int>(250)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 4),
+                OnNext(235, 7),
+                OnCompleted<int>(240)
+            );
+
+            var res = scheduler.Start(() =>
+                new[] { o1, o2, o3 }.Merge(scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(220, 4),
+                OnNext(225, 5),
+                OnNext(230, 6),
+                OnNext(235, 7),
+                OnNext(240, 8),
+                OnNext(245, 9),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(202, 250)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(203, 240)
+            );
+        }
+
+        [Fact]
+        public void Merge_LotsMore()
+        {
+            var inputs = new List<List<Recorded<Notification<int>>>>();
+
+            const int N = 10;
+            for (int i = 0; i < N; i++)
+            {
+                var lst = new List<Recorded<Notification<int>>> { OnNext(150, 1) };
+                inputs.Add(lst);
+
+                ushort start = (ushort)(301 + i);
+                for (int j = 0; j < i; j++)
+                {
+                    var onNext = OnNext(start += (ushort)(j * 5), j + i + 2);
+                    lst.Add(onNext);
+                }
+
+                lst.Add(OnCompleted<int>((ushort)(start + N - i)));
+            }
+
+            var inputsFlat = inputs.Aggregate((l, r) => l.Concat(r).ToList()).ToArray();
+
+            var resOnNext = (from n in inputsFlat
+                             where n.Time >= 200
+                             where n.Value.Kind == NotificationKind.OnNext
+                             orderby n.Time
+                             select n).ToList();
+
+            var lastCompleted = (from n in inputsFlat
+                                 where n.Time >= 200
+                                 where n.Value.Kind == NotificationKind.OnCompleted
+                                 orderby n.Time descending
+                                 select n).First();
+
+            var scheduler = new TestScheduler();
+
+            // Last ToArray: got to create the hot observables *now*
+            var xss = inputs.Select(lst => (IObservable<int>)scheduler.CreateHotObservable(lst.ToArray())).ToArray();
+
+            var res = scheduler.Start(() =>
+                xss.Merge(scheduler)
+            );
+
+            Assert.True(resOnNext.Count + 1 == res.Messages.Count, "length");
+            for (int i = 0; i < resOnNext.Count; i++)
+            {
+                var msg = res.Messages[i];
+                Assert.True(msg.Time == resOnNext[i].Time);
+                Assert.True(msg.Value.Kind == NotificationKind.OnNext);
+                Assert.True(msg.Value.Value == resOnNext[i].Value.Value);
+            }
+            Assert.True(res.Messages[resOnNext.Count].Value.Kind == NotificationKind.OnCompleted && res.Messages[resOnNext.Count].Time == lastCompleted.Time, "complete");
+        }
+
+        [Fact]
+        public void Merge_ErrorLeft()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(245, ex)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(215, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(o1, o2, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnError<int>(245, ex)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(201, 245)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(202, 245)
+            );
+        }
+
+        [Fact]
+        public void Merge_ErrorCausesDisposal()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var e1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex) //!
+            );
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 1), // should not come
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.Merge(e1, o1, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex) //!
+            );
+
+            e1.Subscriptions.AssertEqual(
+                Subscribe(201, 210)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(202, 210)
+            );
+        }
+
+        [Fact]
+        public void Merge_ObservableOfObservable_Data()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(10, 101),
+                OnNext(20, 102),
+                OnNext(110, 103),
+                OnNext(120, 104),
+                OnNext(210, 105),
+                OnNext(220, 106),
+                OnCompleted<int>(230)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(10, 201),
+                OnNext(20, 202),
+                OnNext(30, 203),
+                OnNext(40, 204),
+                OnCompleted<int>(50)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 301),
+                OnNext(20, 302),
+                OnNext(30, 303),
+                OnNext(40, 304),
+                OnNext(120, 305),
+                OnCompleted<int>(150)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(300, ys1),
+                OnNext<IObservable<int>>(400, ys2),
+                OnNext<IObservable<int>>(500, ys3),
+                OnCompleted<IObservable<int>>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 101),
+                OnNext(320, 102),
+                OnNext(410, 103),
+                OnNext(410, 201),
+                OnNext(420, 104),
+                OnNext(420, 202),
+                OnNext(430, 203),
+                OnNext(440, 204),
+                OnNext(510, 105),
+                OnNext(510, 301),
+                OnNext(520, 106),
+                OnNext(520, 302),
+                OnNext(530, 303),
+                OnNext(540, 304),
+                OnNext(620, 305),
+                OnCompleted<int>(650)
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+#endif
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(300, 530)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(400, 450)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(500, 650)
+            );
+        }
+
+        [Fact]
+        public void Merge_ObservableOfObservable_Data_NonOverlapped()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(10, 101),
+                OnNext(20, 102),
+                OnCompleted<int>(230)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(10, 201),
+                OnNext(20, 202),
+                OnNext(30, 203),
+                OnNext(40, 204),
+                OnCompleted<int>(50)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 301),
+                OnNext(20, 302),
+                OnNext(30, 303),
+                OnNext(40, 304),
+                OnCompleted<int>(50)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(300, ys1),
+                OnNext<IObservable<int>>(400, ys2),
+                OnNext<IObservable<int>>(500, ys3),
+                OnCompleted<IObservable<int>>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 101),
+                OnNext(320, 102),
+                OnNext(410, 201),
+                OnNext(420, 202),
+                OnNext(430, 203),
+                OnNext(440, 204),
+                OnNext(510, 301),
+                OnNext(520, 302),
+                OnNext(530, 303),
+                OnNext(540, 304),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(300, 530)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(400, 450)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(500, 550)
+            );
+        }
+
+        [Fact]
+        public void Merge_ObservableOfObservable_InnerThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(10, 101),
+                OnNext(20, 102),
+                OnNext(110, 103),
+                OnNext(120, 104),
+                OnNext(210, 105),
+                OnNext(220, 106),
+                OnCompleted<int>(230)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(10, 201),
+                OnNext(20, 202),
+                OnNext(30, 203),
+                OnNext(40, 204),
+                OnError<int>(50, ex)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 301),
+                OnNext(20, 302),
+                OnNext(30, 303),
+                OnNext(40, 304),
+                OnCompleted<int>(150)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(300, ys1),
+                OnNext<IObservable<int>>(400, ys2),
+                OnNext<IObservable<int>>(500, ys3),
+                OnCompleted<IObservable<int>>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 101),
+                OnNext(320, 102),
+                OnNext(410, 103),
+                OnNext(410, 201),
+                OnNext(420, 104),
+                OnNext(420, 202),
+                OnNext(430, 203),
+                OnNext(440, 204),
+                OnError<int>(450, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(300, 450)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(400, 450)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Merge_ObservableOfObservable_OuterThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(10, 101),
+                OnNext(20, 102),
+                OnNext(110, 103),
+                OnNext(120, 104),
+                OnNext(210, 105),
+                OnNext(220, 106),
+                OnCompleted<int>(230)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(10, 201),
+                OnNext(20, 202),
+                OnNext(30, 203),
+                OnNext(40, 204),
+                OnCompleted<int>(50)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(300, ys1),
+                OnNext<IObservable<int>>(400, ys2),
+                OnError<IObservable<int>>(500, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 101),
+                OnNext(320, 102),
+                OnNext(410, 103),
+                OnNext(410, 201),
+                OnNext(420, 104),
+                OnNext(420, 202),
+                OnNext(430, 203),
+                OnNext(440, 204),
+                OnError<int>(500, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(300, 500)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(400, 450)
+            );
+        }
+
+        [Fact]
+        public void Merge_Binary_DefaultScheduler()
+        {
+            Assert.True(Observable.Return(1).Merge(Observable.Return(2)).ToEnumerable().OrderBy(x => x).SequenceEqual(new[] { 1, 2 }));
+        }
+
+        [Fact]
+        public void Merge_Params_DefaultScheduler()
+        {
+            Assert.True(Observable.Merge(Observable.Return(1), Observable.Return(2)).ToEnumerable().OrderBy(x => x).SequenceEqual(new[] { 1, 2 }));
+        }
+
+        [Fact]
+        public void Merge_IEnumerableOfIObservable_DefaultScheduler()
+        {
+            Assert.True(Observable.Merge((IEnumerable<IObservable<int>>)new[] { Observable.Return(1), Observable.Return(2) }).ToEnumerable().OrderBy(x => x).SequenceEqual(new[] { 1, 2 }));
+        }
+
+        [Fact]
+        public void MergeConcat_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(default(IEnumerable<IObservable<int>>), 1, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Merge(DummyEnumerable<IObservable<int>>.Instance, 0, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(DummyEnumerable<IObservable<int>>.Instance, 1, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(default(IEnumerable<IObservable<int>>), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Merge(DummyEnumerable<IObservable<int>>.Instance, 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Merge(default(IObservable<IObservable<int>>), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Merge(DummyObservable<IObservable<int>>.Instance, 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat(default(IObservable<IObservable<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Concat(default(IObservable<Task<int>>)));
+        }
+
+        [Fact]
+        public void MergeConcat_Enumerable_Scheduler()
+        {
+            var b = Enumerable.Range(1, 3).Select(x => Observable.Range(x * 10, 3)).Merge(1)
+                    .SequenceEqual(new[] { 10, 11, 12, 20, 21, 22, 30, 31, 32 }.ToObservable())
+                    .First();
+            Assert.True(b);
+        }
+
+        [Fact]
+        public void MergeConcat_Enumerable()
+        {
+            var b = Enumerable.Range(1, 3).Select(x => Observable.Range(x * 10, 3)).Merge(1, DefaultScheduler.Instance)
+                    .SequenceEqual(new[] { 10, 11, 12, 20, 21, 22, 30, 31, 32 }.ToObservable())
+                    .First();
+            Assert.True(b);
+        }
+
+        [Fact]
+        public void MergeConcat_Default()
+        {
+            var b = Observable.Range(1, 3).Select(x => Observable.Range(x * 10, 3)).Concat()
+                    .SequenceEqual(new[] { 10, 11, 12, 20, 21, 22, 30, 31, 32 }.ToObservable())
+                    .First();
+            Assert.True(b);
+        }
+
+        [Fact]
+        public void MergeConcat_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(200)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnCompleted<int>(130)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable<IObservable<int>>(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(320, ys4),
+                OnCompleted<IObservable<int>>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 6),
+                OnNext(440, 7),
+                OnNext(460, 8),
+                OnNext(670, 9),
+                OnNext(700, 10),
+                OnCompleted<int>(760)
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 760)
+            );
+#endif
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 460)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(350, 480)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+                Subscribe(460, 760)
+            );
+        }
+
+        [Fact]
+        public void MergeConcat_Basic_Long()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(300)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnCompleted<int>(130)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(320, ys4),
+                OnCompleted<IObservable<int>>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 6),
+                OnNext(440, 7),
+                OnNext(460, 8),
+                OnNext(690, 9),
+                OnNext(720, 10),
+                OnCompleted<int>(780)
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 780)
+            );
+#endif
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 560)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(350, 480)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+                Subscribe(480, 780)
+            );
+        }
+
+        [Fact]
+        public void MergeConcat_Basic_Wide()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(300)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnCompleted<int>(130)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(420, ys4),
+                OnCompleted<IObservable<int>>(450)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(280, 6),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 7),
+                OnNext(380, 8),
+                OnNext(630, 9),
+                OnNext(660, 10),
+                OnCompleted<int>(720)
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 720)
+            );
+#endif
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 560)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(270, 400)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+                Subscribe(420, 720)
+            );
+        }
+
+        [Fact]
+        public void MergeConcat_Basic_Late()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(300)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnCompleted<int>(130)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(420, ys4),
+                OnCompleted<IObservable<int>>(750)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(280, 6),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 7),
+                OnNext(380, 8),
+                OnNext(630, 9),
+                OnNext(660, 10),
+                OnCompleted<int>(750)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 750)
+            );
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 560)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(270, 400)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+                Subscribe(420, 720)
+            );
+        }
+
+        [Fact]
+        public void MergeConcat_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(200)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnCompleted<int>(130)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(320, ys4),
+                OnCompleted<IObservable<int>>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(2),
+                450
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 6),
+                OnNext(440, 7)
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+#endif
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 450)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(350, 450)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void MergeConcat_OuterError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(200)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnCompleted<int>(130)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(320, ys4),
+                OnError<IObservable<int>>(400, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 6),
+                OnError<int>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 400)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(350, 400)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void MergeConcat_InnerError()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var ys1 = scheduler.CreateColdObservable(
+                OnNext(50, 1),
+                OnNext(100, 2),
+                OnNext(120, 3),
+                OnCompleted<int>(140)
+            );
+
+            var ys2 = scheduler.CreateColdObservable(
+                OnNext(20, 4),
+                OnNext(70, 5),
+                OnCompleted<int>(200)
+            );
+
+            var ys3 = scheduler.CreateColdObservable(
+                OnNext(10, 6),
+                OnNext(90, 7),
+                OnNext(110, 8),
+                OnError<int>(140, ex)
+            );
+
+            var ys4 = scheduler.CreateColdObservable(
+                OnNext(210, 9),
+                OnNext(240, 10),
+                OnCompleted<int>(300)
+            );
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext<IObservable<int>>(210, ys1),
+                OnNext<IObservable<int>>(260, ys2),
+                OnNext<IObservable<int>>(270, ys3),
+                OnNext<IObservable<int>>(320, ys4),
+                OnCompleted<IObservable<int>>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Merge(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(280, 4),
+                OnNext(310, 2),
+                OnNext(330, 3),
+                OnNext(330, 5),
+                OnNext(360, 6),
+                OnNext(440, 7),
+                OnNext(460, 8),
+                OnError<int>(490, ex)
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+#else
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 490)
+            );
+#endif
+
+            ys1.Subscriptions.AssertEqual(
+                Subscribe(210, 350)
+            );
+
+            ys2.Subscriptions.AssertEqual(
+                Subscribe(260, 460)
+            );
+
+            ys3.Subscriptions.AssertEqual(
+                Subscribe(350, 490)
+            );
+
+            ys4.Subscriptions.AssertEqual(
+                Subscribe(460, 490)
+            );
+        }
+
+        [Fact]
+        public void Merge_Task()
+        {
+            var tss = Observable.Merge(new[] { Task.Factory.StartNew(() => 1), Task.Factory.StartNew(() => 2), Task.Factory.StartNew(() => 3) }.ToObservable());
+
+            var res = tss.ToArray().Single();
+
+            Assert.True(res.OrderBy(x => x).SequenceEqual(new[] { 1, 2, 3 }));
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_RanToCompletion_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            var res = Observable.Merge(Observable.Range(0, 2).Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43 });
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_RanToCompletion_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            var res = Observable.Merge(Observable.Range(0, 2).Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43 });
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_Faulted_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.Merge(Observable.Range(0, 3).Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_Faulted_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            var res = Observable.Merge(Observable.Range(0, 3).Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_Canceled_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.Merge(Observable.Range(0, 3).Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            tcss[1].SetCanceled();
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_Canceled_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            tcss[1].SetCanceled();
+
+            var res = Observable.Merge(Observable.Range(0, 3).Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_InnerCompleteBeforeOuter()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.Merge(xs.Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            xs.OnCompleted();
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void Merge_TaskWithCompletionSource_OuterCompleteBeforeInner()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.Merge(xs.Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void Merge_Task_OnError()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.Merge(xs.Select(x => tcss[x].Task));
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            var ex = new Exception();
+            xs.OnError(ex);
+
+            done.WaitOne();
+
+            Assert.Same(ex, err);
+        }
+
+    }
+}

+ 409 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MinByTest.cs

@@ -0,0 +1,409 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class MinByTest : ReactiveTest
+    {
+
+        [Fact]
+        public void MinBy_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MinBy(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MinBy(DummyObservable<int>.Instance, default(Func<int, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MinBy(default(IObservable<int>), x => x, Comparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MinBy(DummyObservable<int>.Instance, default(Func<int, int>), Comparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MinBy(DummyObservable<int>.Instance, x => x, null));
+        }
+
+        [Fact]
+        public void MinBy_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.Count == 0),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(2, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "a"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(2, "c")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "c"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Multiple()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(215, new KeyValuePair<int, string>(2, "d")),
+                OnNext(220, new KeyValuePair<int, string>(3, "c")),
+                OnNext(225, new KeyValuePair<int, string>(2, "y")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnNext(235, new KeyValuePair<int, string>(4, "r")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "d"),
+                    new KeyValuePair<int, string>(2, "y"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnError<KeyValuePair<int, string>>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z"))
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Comparer_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.Count == 0),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Comparer_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(2, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(2, "a"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Comparer_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(20, "c")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<IList<KeyValuePair<int, string>>>(250, x => x.SequenceEqual(new[] {
+                    new KeyValuePair<int, string>(20, "c"),
+                })),
+                OnCompleted<IList<KeyValuePair<int, string>>>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Comparer_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnError<KeyValuePair<int, string>>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinBy_Comparer_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z"))
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key, new ReverseComparer<int>(Comparer<int>.Default))
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinBy_SelectorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(2, "c")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy<KeyValuePair<int, string>, int>(x => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinBy_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, new KeyValuePair<int, string>(1, "z")),
+                OnNext(210, new KeyValuePair<int, string>(3, "b")),
+                OnNext(220, new KeyValuePair<int, string>(2, "c")),
+                OnNext(230, new KeyValuePair<int, string>(4, "a")),
+                OnCompleted<KeyValuePair<int, string>>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.MinBy(x => x.Key, new ThrowingComparer<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<IList<KeyValuePair<int, string>>>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        class ReverseComparer<T> : IComparer<T>
+        {
+            private IComparer<T> _comparer;
+
+            public ReverseComparer(IComparer<T> comparer)
+            {
+                _comparer = comparer;
+            }
+
+            public int Compare(T x, T y)
+            {
+                return -_comparer.Compare(x, y);
+            }
+        }
+
+        class ThrowingComparer<T> : IComparer<T>
+        {
+            private Exception _ex;
+
+            public ThrowingComparer(Exception ex)
+            {
+                _ex = ex;
+            }
+
+            public int Compare(T x, T y)
+            {
+                throw _ex;
+            }
+        }
+
+    }
+}

+ 2399 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MinTest.cs

@@ -0,0 +1,2399 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class MinTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Min_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<double>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<float>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<decimal>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<long>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<int?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<double?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<float?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<decimal?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<long?>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(int)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(double)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(float)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(decimal)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(long)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(int?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(double?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(float?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(decimal?)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => default(long?)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, double>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, float>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, decimal>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, long>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, int?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, double?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, float?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, decimal?>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, long?>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), Comparer<DateTime>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(IComparer<DateTime>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => ""));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, string>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(default(IObservable<DateTime>), _ => "", Comparer<string>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), default(Func<DateTime, string>), Comparer<string>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Min(Observable.Empty<DateTime>(), _ => "", default(IComparer<string>)));
+        }
+
+        [Fact]
+        public void Min_Int32_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Int32_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Int32_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 3),
+                OnNext(220, 2),
+                OnNext(230, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Int32_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Int32_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Int64_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Int64_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnNext(210, 2L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Int64_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnNext(210, 3L),
+                OnNext(220, 2L),
+                OnNext(230, 4L),
+                OnCompleted<long>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2L),
+                OnCompleted<long>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Int64_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L),
+                OnError<long>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Int64_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1L)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Float_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Float_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnNext(210, 2f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2f),
+                OnCompleted<float>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Float_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnNext(210, 3f),
+                OnNext(220, 2f),
+                OnNext(230, 4f),
+                OnCompleted<float>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2f),
+                OnCompleted<float>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Float_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f),
+                OnError<float>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Float_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1f)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Double_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Double_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnNext(210, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Double_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnNext(210, 3.0),
+                OnNext(220, 2.0),
+                OnNext(230, 4.0),
+                OnCompleted<double>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2.0),
+                OnCompleted<double>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Double_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0),
+                OnError<double>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Double_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1.0)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Decimal_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Decimal_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnNext(210, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Decimal_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnNext(210, 3m),
+                OnNext(220, 2m),
+                OnNext(230, 4m),
+                OnCompleted<decimal>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2m),
+                OnCompleted<decimal>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Decimal_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m),
+                OnError<decimal>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Decimal_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1m)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int32_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(int?)),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int32_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int32_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)null),
+                OnNext(220, (int?)2),
+                OnNext(230, (int?)4),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int32_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)null),
+                OnNext(220, (int?)4),
+                OnNext(230, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_GeneralNullableMinTest_LhsNull()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)null),
+                OnNext(220, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_GeneralNullableMinTest_RhsNull()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnNext(220, (int?)null),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_GeneralNullableMinTest_Less()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)2),
+                OnNext(220, (int?)3),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_GeneralNullableMinTest_Greater()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnNext(210, (int?)3),
+                OnNext(220, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (int?)2),
+                OnCompleted<int?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int32_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1),
+                OnError<int?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int32_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (int?)1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int64_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(long?)),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int64_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int64_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)null),
+                OnNext(220, (long?)2L),
+                OnNext(230, (long?)4L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int64_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnNext(210, (long?)null),
+                OnNext(220, (long?)4L),
+                OnNext(230, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (long?)2L),
+                OnCompleted<long?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int64_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L),
+                OnError<long?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<long?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Int64_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (long?)1L)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Float_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(float?)),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Float_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Float_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)null),
+                OnNext(220, (float?)2f),
+                OnNext(230, (float?)4f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Float_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnNext(210, (float?)null),
+                OnNext(220, (float?)4f),
+                OnNext(230, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (float?)2f),
+                OnCompleted<float?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Float_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f),
+                OnError<float?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<float?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Float_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (float?)1f)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Double_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(double?)),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Double_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Double_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)null),
+                OnNext(220, (double?)2.0),
+                OnNext(230, (double?)4.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Double_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnNext(210, (double?)null),
+                OnNext(220, (double?)4.0),
+                OnNext(230, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (double?)2.0),
+                OnCompleted<double?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Double_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0),
+                OnError<double?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<double?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Double_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (double?)1.0)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Decimal_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, default(decimal?)),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Decimal_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Decimal_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)null),
+                OnNext(220, (decimal?)2m),
+                OnNext(230, (decimal?)4m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Decimal_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnNext(210, (decimal?)null),
+                OnNext(220, (decimal?)4m),
+                OnNext(230, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, (decimal?)2m),
+                OnCompleted<decimal?>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Decimal_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m),
+                OnError<decimal?>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<decimal?>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Min_Nullable_Decimal_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, (decimal?)1m)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x - Behavior for reference types
+            res.Messages.AssertEqual(
+                OnNext(250, default(string)),
+                OnCompleted<string>(250)
+            );
+#else
+            res.Messages.AssertEqual(
+                OnError<string>(250, e => e is InvalidOperationException)
+            );
+#endif
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "a"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "b"),
+                OnNext(220, "c"),
+                OnNext(230, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "a"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1993, 2, 11)),
+                OnNext(220, new DateTime(2003, 2, 11)),
+                OnNext(230, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnError<string>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnError<DateTime>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min()
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Comparer_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+#if !NO_PERF
+            // BREAKING CHANGE v2 > v1.x - Behavior for reference types
+            res.Messages.AssertEqual(
+                OnNext(250, default(string)),
+                OnCompleted<string>(250)
+            );
+#else
+            res.Messages.AssertEqual(
+                OnError<string>(250, e => e is InvalidOperationException)
+            );
+#endif
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Comparer_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Comparer_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "a"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Comparer_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Comparer_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "b"),
+                OnNext(220, "c"),
+                OnNext(230, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, "c"),
+                OnCompleted<string>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Comparer_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1993, 2, 11)),
+                OnNext(220, new DateTime(2003, 2, 11)),
+                OnNext(230, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, new DateTime(2003, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Comparer_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnError<string>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Comparer_Throw()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnError<DateTime>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_Comparer_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<string>(Comparer<string>.Default))
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_Comparer_Never()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ReverseComparer<DateTime>(Comparer<DateTime>.Default))
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Reference_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, "z"),
+                OnNext(210, "b"),
+                OnNext(220, "c"),
+                OnNext(230, "a"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ThrowingComparer<string>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<string>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Value_ComparerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, DateTime.Now),
+                OnNext(210, new DateTime(1993, 2, 11)),
+                OnNext(220, new DateTime(2003, 2, 11)),
+                OnNext(230, new DateTime(1983, 2, 11)),
+                OnCompleted<DateTime>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Min(new ThrowingComparer<DateTime>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<DateTime>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Int32()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => (int)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 1),
+                OnCompleted<int>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Int64()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => (long)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 1L),
+                OnCompleted<long>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Single()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => (float)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 1.0f),
+                OnCompleted<float>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Double()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => (double)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 1.0),
+                OnCompleted<double>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Decimal()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => (decimal)x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, 1.0m),
+                OnCompleted<decimal>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Int32_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => x == "fo" ? default(int?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (int?)1),
+                OnCompleted<int?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Int64_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => x == "fo" ? default(long?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (long?)1.0),
+                OnCompleted<long?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Single_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => x == "fo" ? default(float?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (float?)1.0),
+                OnCompleted<float?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Double_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => x == "fo" ? default(double?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (double?)1.0),
+                OnCompleted<double?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void Min_Selector_Regular_Decimal_Nullable()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "fo"),
+                OnNext(220, "b"),
+                OnNext(230, "qux"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => x == "fo" ? default(decimal?) : x.Length));
+
+            res.Messages.AssertEqual(
+                OnNext(240, (decimal?)1.0),
+                OnCompleted<decimal?>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Selector_Regular()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "qux"),
+                OnNext(220, "foo"),
+                OnNext(230, "bar"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => new string(x.ToCharArray().Reverse().ToArray())));
+
+            res.Messages.AssertEqual(
+                OnNext(240, "oof"),
+                OnCompleted<string>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void MinOfT_Selector_Regular_Comparer()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<string>(
+                OnNext(210, "qux"),
+                OnNext(220, "foo"),
+                OnNext(230, "bar"),
+                OnCompleted<string>(240)
+            );
+
+            var res = scheduler.Start(() => xs.Min(x => new string(x.ToCharArray().Reverse().ToArray()), new ReverseComparer<string>(Comparer<string>.Default)));
+
+            res.Messages.AssertEqual(
+                OnNext(240, "xuq"),
+                OnCompleted<string>(240)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+    }
+}

+ 208 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MostRecentTest.cs

@@ -0,0 +1,208 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Collections;
+
+namespace ReactiveTests.Tests
+{
+    public class MostRecentTest : ReactiveTest
+    {
+
+        [Fact]
+        public void MostRecent_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.MostRecent(default(IObservable<int>), 1));
+        }
+
+        [Fact]
+        public void MostRecent1()
+        {
+            var evt = new AutoResetEvent(false);
+            var nxt = new AutoResetEvent(false);
+            var src = Observable.Create<int>(obs =>
+            {
+                Task.Run(() =>
+                {
+                    evt.WaitOne();
+                    obs.OnNext(1);
+                    nxt.Set();
+                    evt.WaitOne();
+                    obs.OnNext(2);
+                    nxt.Set();
+                    evt.WaitOne();
+                    obs.OnCompleted();
+                    nxt.Set();
+                });
+
+                return () => { };
+            });
+
+            var res = src.MostRecent(42).GetEnumerator();
+
+            Assert.True(res.MoveNext());
+            Assert.Equal(42, res.Current);
+            Assert.True(res.MoveNext());
+            Assert.Equal(42, res.Current);
+
+            for (int i = 1; i <= 2; i++)
+            {
+                evt.Set();
+                nxt.WaitOne();
+                Assert.True(res.MoveNext());
+                Assert.Equal(i, res.Current);
+                Assert.True(res.MoveNext());
+                Assert.Equal(i, res.Current);
+            }
+
+            evt.Set();
+            nxt.WaitOne();
+            Assert.False(res.MoveNext());
+        }
+
+        [Fact]
+        public void MostRecent2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnNext(270, 7),
+                OnNext(280, 8),
+                OnNext(290, 9),
+                OnCompleted<int>(300)
+            );
+
+            var res = xs.MostRecent(0);
+
+            var e1 = default(IEnumerator<int>);
+            scheduler.ScheduleAbsolute(200, () =>
+            {
+                e1 = res.GetEnumerator();
+            });
+
+            var o1 = new List<int>();
+            scheduler.ScheduleAbsolute(205, () =>
+            {
+                Assert.True(e1.MoveNext());
+                o1.Add(e1.Current);
+            });
+            scheduler.ScheduleAbsolute(232, () =>
+            {
+                Assert.True(e1.MoveNext());
+                o1.Add(e1.Current);
+            });
+            scheduler.ScheduleAbsolute(234, () =>
+            {
+                Assert.True(e1.MoveNext());
+                o1.Add(e1.Current);
+            });
+            scheduler.ScheduleAbsolute(265, () =>
+            {
+                Assert.True(e1.MoveNext());
+                o1.Add(e1.Current);
+            });
+
+            scheduler.ScheduleAbsolute(285, () => e1.Dispose());
+
+            var e2 = default(IEnumerator);
+            scheduler.ScheduleAbsolute(255, () =>
+            {
+                e2 = ((IEnumerable)res).GetEnumerator();
+            });
+
+            var o2 = new List<int>();
+            scheduler.ScheduleAbsolute(258, () =>
+            {
+                Assert.True(e2.MoveNext());
+                o2.Add((int)e2.Current);
+            });
+            scheduler.ScheduleAbsolute(262, () =>
+            {
+                Assert.True(e2.MoveNext());
+                o2.Add((int)e2.Current);
+            });
+            scheduler.ScheduleAbsolute(264, () =>
+            {
+                Assert.True(e2.MoveNext());
+                o2.Add((int)e2.Current);
+            });
+            scheduler.ScheduleAbsolute(275, () =>
+            {
+                Assert.True(e2.MoveNext());
+                o2.Add((int)e2.Current);
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 285),
+                Subscribe(255, 300)
+            );
+
+            o1.AssertEqual(0, 3, 3, 6);
+            o2.AssertEqual(0, 6, 6, 7);
+        }
+
+        [Fact]
+        public void MostRecent_Error()
+        {
+            var ex = new Exception();
+
+            var evt = new AutoResetEvent(false);
+            var nxt = new AutoResetEvent(false);
+            var src = Observable.Create<int>(obs =>
+            {
+                Task.Run(() =>
+                {
+                    evt.WaitOne();
+                    obs.OnNext(1);
+                    nxt.Set();
+                    evt.WaitOne();
+                    obs.OnError(ex);
+                    nxt.Set();
+                });
+
+                return () => { };
+            });
+
+            var res = src.MostRecent(42).GetEnumerator();
+
+            Assert.True(res.MoveNext());
+            Assert.Equal(42, res.Current);
+            Assert.True(res.MoveNext());
+            Assert.Equal(42, res.Current);
+
+            evt.Set();
+            nxt.WaitOne();
+            Assert.True(res.MoveNext());
+            Assert.Equal(1, res.Current);
+            Assert.True(res.MoveNext());
+            Assert.Equal(1, res.Current);
+
+            evt.Set();
+            nxt.WaitOne();
+
+            ReactiveAssert.Throws(ex, () => res.MoveNext());
+        }
+
+    }
+}

+ 483 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/MulticastTest.cs

@@ -0,0 +1,483 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class MulticastTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Multicast_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Multicast<int, int>(null, new Subject<int>()));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Multicast<int, int>(DummyObservable<int>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Multicast<int, int, int>(null, () => new Subject<int>(), xs => xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Multicast<int, int, int>(DummyObservable<int>.Instance, null, xs => xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Multicast<int, int, int>(DummyObservable<int>.Instance, () => new Subject<int>(), null));
+        }
+
+        [Fact]
+        public void Multicast_Hot_1()
+        {
+            var scheduler = new TestScheduler();
+
+            var s = new Subject<int>();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var c = default(IConnectableObservable<int>);
+            var o = scheduler.CreateObserver<int>();
+            var d1 = default(IDisposable);
+            var d2 = default(IDisposable);
+
+            scheduler.ScheduleAbsolute(50, () => c = xs.Multicast(s));
+            scheduler.ScheduleAbsolute(100, () => d1 = c.Subscribe(o));
+            scheduler.ScheduleAbsolute(200, () => d2 = c.Connect());
+            scheduler.ScheduleAbsolute(300, () => d1.Dispose());
+
+            scheduler.Start();
+
+            o.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Hot_2()
+        {
+            var scheduler = new TestScheduler();
+
+            var s = new Subject<int>();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var c = default(IConnectableObservable<int>);
+            var o = scheduler.CreateObserver<int>();
+            var d1 = default(IDisposable);
+            var d2 = default(IDisposable);
+
+            scheduler.ScheduleAbsolute(50, () => c = xs.Multicast(s));
+            scheduler.ScheduleAbsolute(100, () => d2 = c.Connect());
+            scheduler.ScheduleAbsolute(200, () => d1 = c.Subscribe(o));
+            scheduler.ScheduleAbsolute(300, () => d1.Dispose());
+
+            scheduler.Start();
+
+            o.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Hot_3()
+        {
+            var scheduler = new TestScheduler();
+
+            var s = new Subject<int>();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var c = default(IConnectableObservable<int>);
+            var o = scheduler.CreateObserver<int>();
+            var d1 = default(IDisposable);
+            var d2 = default(IDisposable);
+
+            scheduler.ScheduleAbsolute(50, () => c = xs.Multicast(s));
+            scheduler.ScheduleAbsolute(100, () => d2 = c.Connect());
+            scheduler.ScheduleAbsolute(200, () => d1 = c.Subscribe(o));
+            scheduler.ScheduleAbsolute(300, () => d2.Dispose());
+            scheduler.ScheduleAbsolute(335, () => d2 = c.Connect());
+
+            scheduler.Start();
+
+            o.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 300),
+                Subscribe(335, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Hot_4()
+        {
+            var scheduler = new TestScheduler();
+
+            var s = new Subject<int>();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnError<int>(390, ex)
+            );
+
+            var c = default(IConnectableObservable<int>);
+            var o = scheduler.CreateObserver<int>();
+            var d1 = default(IDisposable);
+            var d2 = default(IDisposable);
+
+            scheduler.ScheduleAbsolute(50, () => c = xs.Multicast(s));
+            scheduler.ScheduleAbsolute(100, () => d2 = c.Connect());
+            scheduler.ScheduleAbsolute(200, () => d1 = c.Subscribe(o));
+            scheduler.ScheduleAbsolute(300, () => d2.Dispose());
+            scheduler.ScheduleAbsolute(335, () => d2 = c.Connect());
+
+            scheduler.Start();
+
+            o.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(340, 7),
+                OnError<int>(390, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 300),
+                Subscribe(335, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Hot_5()
+        {
+            var scheduler = new TestScheduler();
+
+            var s = new Subject<int>();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnError<int>(390, ex)
+            );
+
+            var c = default(IConnectableObservable<int>);
+            var o = scheduler.CreateObserver<int>();
+            var d1 = default(IDisposable);
+            var d2 = default(IDisposable);
+
+            scheduler.ScheduleAbsolute(50, () => c = xs.Multicast(s));
+            scheduler.ScheduleAbsolute(100, () => d2 = c.Connect());
+            scheduler.ScheduleAbsolute(400, () => d1 = c.Subscribe(o));
+
+            scheduler.Start();
+
+            o.Messages.AssertEqual(
+                OnError<int>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Hot_6()
+        {
+            var scheduler = new TestScheduler();
+
+            var s = new Subject<int>();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var c = default(IConnectableObservable<int>);
+            var o = scheduler.CreateObserver<int>();
+            var d1 = default(IDisposable);
+            var d2 = default(IDisposable);
+
+            scheduler.ScheduleAbsolute(50, () => c = xs.Multicast(s));
+            scheduler.ScheduleAbsolute(100, () => d2 = c.Connect());
+            scheduler.ScheduleAbsolute(400, () => d1 = c.Subscribe(o));
+
+            scheduler.Start();
+
+            o.Messages.AssertEqual(
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(100, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Cold_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Multicast(() => new Subject<int>(), ys => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Cold_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnError<int>(390, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Multicast(() => new Subject<int>(), ys => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnError<int>(390, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Cold_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Multicast(() => new Subject<int>(), ys => ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Multicast_Cold_Zip()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Multicast(() => new Subject<int>(), ys => ys.Zip(ys, (a, b) => a + b))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 6),
+                OnNext(240, 8),
+                OnNext(270, 10),
+                OnNext(330, 12),
+                OnNext(340, 14),
+                OnCompleted<int>(390)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+        }
+
+        [Fact]
+        public void Multicast_SubjectSelectorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Multicast<int, int, int>(() => { throw ex; }, _ => _)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Multicast_SelectorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 1),
+                OnNext(240, 2),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Multicast<int, int, int>(() => new Subject<int>(), _ => { throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+        }
+
+    }
+}

+ 68 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/NeverTest.cs

@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class NeverTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Never_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Never<int>().Subscribe(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Never<int>(42).Subscribe(null));
+        }
+
+        [Fact]
+        public void Never_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Never<int>();
+
+            var res = scheduler.CreateObserver<int>();
+
+            xs.Subscribe(res);
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Never_Basic_Witness()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Never<int>(42);
+
+            var res = scheduler.CreateObserver<int>();
+
+            xs.Subscribe(res);
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+    }
+}

+ 177 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/NextTest.cs

@@ -0,0 +1,177 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Collections;
+
+namespace ReactiveTests.Tests
+{
+    public class NextTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Next_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Next(default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void Next1()
+        {
+            var evt = new AutoResetEvent(false);
+            var src = Observable.Create<int>(obs =>
+            {
+                Task.Run(() =>
+                {
+                    evt.WaitOne();
+                    obs.OnNext(1);
+                    evt.WaitOne();
+                    obs.OnNext(2);
+                    evt.WaitOne();
+                    obs.OnCompleted();
+                });
+
+                return () => { };
+            });
+
+            var res = src.Next().GetEnumerator();
+
+            Action release = () => Task.Run(async () =>
+            {
+                await Task.Delay(250);
+                evt.Set();
+            });
+
+            release();
+            Assert.True(res.MoveNext());
+            Assert.Equal(1, res.Current);
+
+            release();
+            Assert.True(res.MoveNext());
+            Assert.Equal(2, res.Current);
+
+            release();
+            Assert.False(res.MoveNext());
+        }
+
+
+        [Fact]
+        public void Next2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnNext(270, 7),
+                OnNext(280, 8),
+                OnNext(290, 9),
+                OnCompleted<int>(300)
+            );
+
+            var res = xs.Next();
+
+            var e1 = default(IEnumerator<int>);
+            scheduler.ScheduleAbsolute(200, () =>
+            {
+                e1 = res.GetEnumerator();
+            });
+
+            scheduler.ScheduleAbsolute(285, () => e1.Dispose());
+
+            var e2 = default(IEnumerator);
+            scheduler.ScheduleAbsolute(255, () =>
+            {
+                e2 = ((IEnumerable)res).GetEnumerator();
+            });
+
+            scheduler.Start();
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 285),
+                Subscribe(255, 300)
+            );
+        }
+
+        [Fact]
+        public void Next_DoesNotBlock()
+        {
+            var evt = new ManualResetEvent(false);
+
+            var xs = Observable.Empty<int>().Do(_ => { }, () => evt.Set());
+
+            var e = xs.Next().GetEnumerator();
+
+            evt.WaitOne();
+
+            Assert.False(e.MoveNext());
+        }
+
+        [Fact]
+        public void Next_SomeResults()
+        {
+            var xs = Observable.Range(0, 100, Scheduler.Default);
+
+            var res = xs.Next().ToList();
+
+            Assert.True(res.All(x => x < 100));
+            Assert.True(res.Count == res.Distinct().Count());
+        }
+
+#if !NO_THREAD
+        [Fact]
+        public void Next_Error()
+        {
+            var ex = new Exception();
+
+            var evt = new AutoResetEvent(false);
+            var src = Observable.Create<int>(obs =>
+            {
+                new Thread(() =>
+                {
+                    evt.WaitOne();
+                    obs.OnNext(1);
+                    evt.WaitOne();
+                    obs.OnError(ex);
+                }).Start();
+
+                return () => { };
+            });
+
+            var res = src.Next().GetEnumerator();
+
+            Action release = () => new Thread(() =>
+            {
+                Thread.Sleep(250);
+                evt.Set();
+            }).Start();
+
+            release();
+            Assert.True(res.MoveNext());
+            Assert.Equal(1, res.Current);
+
+            release();
+
+            ReactiveAssert.Throws(ex, () => res.MoveNext());
+        }
+#endif
+
+    }
+}

+ 19 - 476
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/ObservableConcurrencyTest.cs → Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ObserveOnTest.cs

@@ -5,29 +5,25 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
 using System.Reactive;
 using System.Reactive.Concurrency;
-using System.Reactive.Disposables;
 using System.Reactive.Linq;
-using System.Reactive.Subjects;
-using System.Threading;
-#if NET46
-using System.Windows.Threading;
-#endif
 using Microsoft.Reactive.Testing;
 using Xunit;
 using ReactiveTests.Dummies;
-
-#if HAS_WINFORMS
-using System.Windows.Forms;
-#endif
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
 
 namespace ReactiveTests.Tests
 {
-
-    public partial class ObservableConcurrencyTest : TestBase
+    public class ObserveOnTest : TestBase
     {
-        #region + ObserveOn +
+
+        #region + TestBase +
 
         [Fact]
         public void ObserveOn_ArgumentChecking()
@@ -222,326 +218,12 @@ namespace ReactiveTests.Tests
             });
         }
 #endif
-        #endregion
-
-        #region SubscribeOn
-
-        [Fact]
-        public void SubscribeOn_ArgumentChecking()
-        {
-            var someObservable = Observable.Empty<int>();
-
-#if HAS_WINFORMS
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new ControlScheduler(new Label())));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(someObservable, default(ControlScheduler)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => ControlObservable.SubscribeOn<int>(default(IObservable<int>), new Label()));
-            ReactiveAssert.Throws<ArgumentNullException>(() => ControlObservable.SubscribeOn<int>(someObservable, default(Label)));
-#endif
-#if HAS_DISPATCHER
-#if USE_SL_DISPATCHER
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new DispatcherScheduler(System.Windows.Deployment.Current.Dispatcher)));
-#else
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new DispatcherScheduler(Dispatcher.CurrentDispatcher)));
-#endif
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(someObservable, default(DispatcherScheduler)));
-
-#if USE_SL_DISPATCHER
-            ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOn<int>(default(IObservable<int>), System.Windows.Deployment.Current.Dispatcher));
-#else
-            ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOn<int>(default(IObservable<int>), Dispatcher.CurrentDispatcher));
-#endif
-            ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOn<int>(someObservable, default(Dispatcher)));
-            ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOnDispatcher<int>(default(IObservable<int>)));
-#endif
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new SynchronizationContext()));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(someObservable, default(SynchronizationContext)));
-        }
-
-#if HAS_WINFORMS
-        [Fact]
-        public void SubscribeOn_Control()
-        {
-            var lbl = CreateLabel();
-
-            var evt2 = new ManualResetEvent(false);
-            var evt = new ManualResetEvent(false);
-            bool okay = true;
-            var d = Observable.Create<int>(obs =>
-            {
-                lbl.Text = "Subscribe";
-                okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
-                evt2.Set();
-
-                return () =>
-                {
-                    lbl.Text = "Unsubscribe";
-                    okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
-                    evt.Set();
-                };
-            })
-            .SubscribeOn(lbl)
-            .Subscribe(_ => {});
-
-            evt2.WaitOne();
-            d.Dispose();
-
-            evt.WaitOne();
-            Application.Exit();
-            Assert.True(okay);
-        }
-
-        [Fact]
-        public void SubscribeOn_ControlScheduler()
-        {
-            var lbl = CreateLabel();
-
-            var evt2 = new ManualResetEvent(false);
-            var evt = new ManualResetEvent(false);
-            bool okay = true;
-            var d = Observable.Create<int>(obs =>
-            {
-                lbl.Text = "Subscribe";
-                okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
-                evt2.Set();
-
-                return () =>
-                {
-                    lbl.Text = "Unsubscribe";
-                    okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
-                    evt.Set();
-                };
-            })
-            .SubscribeOn(new ControlScheduler(lbl))
-            .Subscribe(_ => { });
-
-            evt2.WaitOne();
-            d.Dispose();
-
-            evt.WaitOne();
-            Application.Exit();
-            Assert.True(okay);
-        }
-#endif
-
-#if HAS_DISPATCHER
-        [Fact]
-        [Asynchronous]
-        public void SubscribeOn_Dispatcher()
-        {
-            var dispatcher = DispatcherHelpers.EnsureDispatcher();
-
-            RunAsync(evt =>
-            {
-                var s = new AsyncSubject<Unit>();
-                bool okay = true;
-                var d = Observable.Create<int>(obs =>
-                {
-                    okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
-                    s.OnNext(Unit.Default);
-                    s.OnCompleted();
-
-                    return () =>
-                    {
-                        okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
-                        Assert.True(okay);
-                        dispatcher.InvokeShutdown();
-                        evt.Set();
-                    };
-                })
-                .SubscribeOn(dispatcher)
-                .Subscribe(_ => { });
-
-                s.Subscribe(_ => d.Dispose());
-            });
-        }
-
-        [Fact]
-        [Asynchronous]
-        public void SubscribeOn_DispatcherScheduler()
-        {
-            var dispatcher = DispatcherHelpers.EnsureDispatcher();
-
-            RunAsync(evt =>
-            {
-                var s = new AsyncSubject<Unit>();
-                bool okay = true;
-                var d = Observable.Create<int>(obs =>
-                {
-                    okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
-                    s.OnNext(Unit.Default);
-                    s.OnCompleted();
-
-                    return () =>
-                    {
-                        okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
-                        Assert.True(okay);
-                        dispatcher.InvokeShutdown();
-                        evt.Set();
-                    };
-                })
-                .SubscribeOn(new DispatcherScheduler(dispatcher))
-                .Subscribe(_ => { });
-
-                s.Subscribe(_ => d.Dispose());
-            });
-        }
-
-        [Fact]
-        [Asynchronous]
-        public void SubscribeOn_CurrentDispatcher()
-        {
-            var dispatcher = DispatcherHelpers.EnsureDispatcher();
-
-            RunAsync(evt =>
-            {
-                var s = new AsyncSubject<Unit>();
-                bool okay = true;
-
-                dispatcher.BeginInvoke(new Action(() =>
-                {
-                    var d = Observable.Create<int>(obs =>
-                    {
-                        okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
-                        s.OnNext(Unit.Default);
-                        s.OnCompleted();
-
-                        return () =>
-                        {
-                            okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
-                            Assert.True(okay);
-                            dispatcher.InvokeShutdown();
-                            evt.Set();
-                        };
-                    })
-                    .SubscribeOnDispatcher()
-                    .Subscribe(_ => { });
-
-                    s.Subscribe(_ => d.Dispose());
-                }));
-            });
-        }
-#endif
-        #endregion
-
-        #region + Synchronize +
-
-        [Fact]
-        public void Synchronize_ArgumentChecking()
-        {
-            var someObservable = Observable.Empty<int>();
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Synchronize<int>(default(IObservable<int>)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Synchronize<int>(default(IObservable<int>), new object()));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Synchronize<int>(someObservable, null));
-        }
-
-#if !NO_THREAD
-        [Fact]
-        public void Synchronize_Range()
-        {
-            int i = 0;
-            bool outsideLock = true;
-
-            var gate = new object();
-            lock (gate)
-            {
-                outsideLock = false;
-                Observable.Range(0, 100, NewThreadScheduler.Default).Synchronize(gate).Subscribe(x => i++, () => { Assert.True(outsideLock); });
-                Thread.Sleep(100);
-                Assert.Equal(0, i);
-                outsideLock = true;
-            }
-
-            while (i < 100)
-            {
-                Thread.Sleep(10);
-                lock (gate)
-                {
-                    int start = i;
-                    Thread.Sleep(100);
-                    Assert.Equal(start, i);
-                }
-            }
-        }
-
-        [Fact]
-        public void Synchronize_Throw()
-        {
-            var ex = new Exception();
-            var resLock = new object();
-            var e = default(Exception);
-            bool outsideLock = true;
-
-            var gate = new object();
-            lock (gate)
-            {
-                outsideLock = false;
-                Observable.Throw<int>(ex, NewThreadScheduler.Default).Synchronize(gate).Subscribe(x => { Assert.True(false); }, err => { lock (resLock) { e = err; } }, () => { Assert.True(outsideLock); });
-                Thread.Sleep(100);
-                Assert.Null(e);
-                outsideLock = true;
-            }
-
-            while (true)
-            {
-                lock (resLock)
-                {
-                    if (e != null)
-                        break;
-                }
-            }
-
-            Assert.Same(ex, e);
-        }
-
-        [Fact]
-        public void Synchronize_BadObservable()
-        {
-            var o = Observable.Create<int>(obs =>
-            {
-                var t1 = new Thread(() =>
-                {
-                    for (int i = 0; i < 100; i++)
-                    {
-                        obs.OnNext(i);
-                    }
-                });
-
-                new Thread(() =>
-                {
-                    t1.Start();
-
-                    for (int i = 100; i < 200; i++)
-                    {
-                        obs.OnNext(i);
-                    }
+        #endregion + TestBase +
 
-                    t1.Join();
-                    obs.OnCompleted();
-                }).Start();
-
-                return () => { };
-            });
-
-            var evt = new ManualResetEvent(false);
-
-            int sum = 0;
-            o.Synchronize().Subscribe(x => sum += x, () => { evt.Set(); });
-
-            evt.WaitOne();
-
-            Assert.Equal(Enumerable.Range(0, 200).Sum(), sum);
-        }
-#endif
-        #endregion
     }
 
-
-    public class ObservableConcurrencyReactiveTest : ReactiveTest
+    public class ObserveOnReactiveTest : ReactiveTest
     {
-        #region + ObserveOn +
 
         [Fact]
         public void ObserveOn_Scheduler_ArgumentChecking()
@@ -963,159 +645,20 @@ namespace ReactiveTests.Tests
             );
         }
 
-        #endregion
-
-        #region SubscribeOn
-
-        [Fact]
-        public void SubscribeOn_Scheduler_ArgumentChecking()
-        {
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn(default(IObservable<int>), DummyScheduler.Instance));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn(DummyObservable<int>.Instance, default(IScheduler)));
-        }
-
-        [Fact]
-        public void SubscribeOn_Scheduler_Sleep()
-        {
-            var scheduler = new TestScheduler();
-
-            var s = 0L;
-            var d = 0L;
-
-            var xs = Observable.Create<long>(observer =>
-                {
-                    s = scheduler.Clock;
-                    return () => d = scheduler.Clock;
-                });
-
-            var results = scheduler.Start(() =>
-                xs.SubscribeOn(scheduler)
-            );
-
-            results.Messages.AssertEqual(
-            );
-
-            Assert.Equal(201, s);
-            Assert.Equal(1001, d);
-        }
-
-        [Fact]
-        public void SubscribeOn_Scheduler_Completed()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnCompleted<long>(300)
-            );
-
-            var results = scheduler.Start(() =>
-                xs.SubscribeOn(scheduler)
-            );
-
-            results.Messages.AssertEqual(
-                OnCompleted<long>(300)
-            );
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(201, 301)
-            );
-        }
-
-        [Fact]
-        public void SubscribeOn_Scheduler_Error()
-        {
-            var scheduler = new TestScheduler();
-
-            var ex = new Exception();
-
-            var xs = scheduler.CreateHotObservable(
-                OnError<int>(300, ex)
-            );
-
-            var results = scheduler.Start(() =>
-                xs.SubscribeOn(scheduler)
-            );
-
-            results.Messages.AssertEqual(
-                OnError<int>(300, ex)
-            );
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(201, 301)
-            );
-        }
-
-        [Fact]
-        public void SubscribeOn_Scheduler_Dispose()
-        {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable<int>(
-            );
-
-            var results = scheduler.Start(() =>
-                xs.SubscribeOn(scheduler)
-            );
-
-            results.Messages.AssertEqual(
-            );
+    }
 
-            xs.Subscriptions.AssertEqual(
-                Subscribe(201, 1001)
-            );
-        }
+    internal class MyCtx : SynchronizationContext
+    {
+        private IScheduler scheduler;
 
-        [Fact]
-        public void SubscribeOn_SynchronizationContext_Simple()
+        public MyCtx(IScheduler scheduler)
         {
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(90, 1),
-                OnNext(120, 2),
-                OnNext(230, 3),
-                OnNext(240, 4),
-                OnNext(310, 5),
-                OnNext(470, 6),
-                OnCompleted<int>(530)
-            );
-
-            var results = scheduler.Start(() =>
-                xs.SubscribeOn(new MyCtx(scheduler))
-            );
-
-            results.Messages.AssertEqual(
-                OnNext(230, 3),
-                OnNext(240, 4),
-                OnNext(310, 5),
-                OnNext(470, 6),
-                OnCompleted<int>(530)
-            );
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(201, 531)
-            );
+            this.scheduler = scheduler;
         }
 
-        #endregion
-
-        #region |> Helpers <|
-
-        class MyCtx : SynchronizationContext
+        public override void Post(SendOrPostCallback d, object state)
         {
-            private IScheduler scheduler;
-
-            public MyCtx(IScheduler scheduler)
-            {
-                this.scheduler = scheduler;
-            }
-
-            public override void Post(SendOrPostCallback d, object state)
-            {
-                scheduler.Schedule(state, (self, s) => { d(s); return Disposable.Empty; });
-            }
+            scheduler.Schedule(state, (self, s) => { d(s); return Disposable.Empty; });
         }
-
-        #endregion
     }
 }

+ 160 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/OfTypeTest.cs

@@ -0,0 +1,160 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class OfTypeTest : ReactiveTest
+    {
+
+        [Fact]
+        public void OfType_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OfType<bool>(default(IObservable<object>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OfType<bool>(DummyObservable<object>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void OfType_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new A(1)),
+                OnNext<object>(230, new E(2)),
+                OnNext<object>(240, new D(3)),
+                OnNext<object>(250, new C(4)),
+                OnNext<object>(260, new B(5)),
+                OnNext<object>(270, new B(6)),
+                OnNext<object>(280, new D(7)),
+                OnNext<object>(290, new A(8)),
+                OnNext<object>(300, new E(9)),
+                OnNext<object>(310, 3),
+                OnNext<object>(320, "foo"),
+                OnNext<object>(330, true),
+                OnNext<object>(340, new B(10)),
+                OnCompleted<object>(350)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.OfType<B>()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(240, new D(3)),
+                OnNext<B>(260, new B(5)),
+                OnNext<B>(270, new B(6)),
+                OnNext<B>(280, new D(7)),
+                OnNext<B>(340, new B(10)),
+                OnCompleted<B>(350)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        [Fact]
+        public void OfType_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new A(1)),
+                OnNext<object>(230, new E(2)),
+                OnNext<object>(240, new D(3)),
+                OnNext<object>(250, new C(4)),
+                OnNext<object>(260, new B(5)),
+                OnNext<object>(270, new B(6)),
+                OnNext<object>(280, new D(7)),
+                OnNext<object>(290, new A(8)),
+                OnNext<object>(300, new E(9)),
+                OnNext<object>(310, 3),
+                OnNext<object>(320, "foo"),
+                OnNext<object>(330, true),
+                OnNext<object>(340, new B(10)),
+                OnError<object>(350, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.OfType<B>()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(240, new D(3)),
+                OnNext<B>(260, new B(5)),
+                OnNext<B>(270, new B(6)),
+                OnNext<B>(280, new D(7)),
+                OnNext<B>(340, new B(10)),
+                OnError<B>(350, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        [Fact]
+        public void OfType_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<object>(
+                OnNext<object>(210, new B(0)),
+                OnNext<object>(220, new A(1)),
+                OnNext<object>(230, new E(2)),
+                OnNext<object>(240, new D(3)),
+                OnNext<object>(250, new C(4)),
+                OnNext<object>(260, new B(5)),
+                OnNext<object>(270, new B(6)),
+                OnNext<object>(280, new D(7)),
+                OnNext<object>(290, new A(8)),
+                OnNext<object>(300, new E(9)),
+                OnNext<object>(310, 3),
+                OnNext<object>(320, "foo"),
+                OnNext<object>(330, true),
+                OnNext<object>(340, new B(10)),
+                OnError<object>(350, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.OfType<B>(),
+                275
+            );
+
+            res.Messages.AssertEqual(
+                OnNext<B>(210, new B(0)),
+                OnNext<B>(240, new D(3)),
+                OnNext<B>(260, new B(5)),
+                OnNext<B>(270, new B(6))
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 275)
+            );
+        }
+
+    }
+}

+ 735 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/OnErrorResumeNextTest.cs

@@ -0,0 +1,735 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class OnErrorResumeNextTest : ReactiveTest
+    {
+
+        [Fact]
+        public void OnErrorResumeNext_ArgumentChecking()
+        {
+            var xs = DummyObservable<int>.Instance;
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OnErrorResumeNext<int>((IObservable<int>[])null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OnErrorResumeNext<int>((IEnumerable<IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OnErrorResumeNext<int>((IObservable<int>)null, xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OnErrorResumeNext<int>(xs, (IObservable<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OnErrorResumeNext<int>(null, xs));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.OnErrorResumeNext<int>(xs, null));
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_IEofIO_GetEnumeratorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xss = new RogueEnumerable<IObservable<int>>(ex);
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext(xss)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_IEofIO()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs1 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnCompleted<int>(40)
+            );
+
+            var xs2 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 4),
+                OnNext(20, 5),
+                OnError<int>(30, new Exception())
+            );
+
+            var xs3 = scheduler.CreateColdObservable<int>(
+                OnNext(10, 6),
+                OnNext(20, 7),
+                OnNext(30, 8),
+                OnNext(40, 9),
+                OnCompleted<int>(50)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext(new[] { xs1, xs2, xs3 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(250, 4),
+                OnNext(260, 5),
+                OnNext(280, 6),
+                OnNext(290, 7),
+                OnNext(300, 8),
+                OnNext(310, 9),
+                OnCompleted<int>(320)
+            );
+
+            xs1.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+
+            xs2.Subscriptions.AssertEqual(
+                Subscribe(240, 270)
+            );
+
+            xs3.Subscriptions.AssertEqual(
+                Subscribe(270, 320)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_NoErrors()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.OnErrorResumeNext(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, new Exception())
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.OnErrorResumeNext(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 250)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_ErrorMultiple()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, new Exception())
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(230, 3),
+                OnError<int>(240, new Exception())
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext(o1, o2, o3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(220, 240)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(240, 250)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_EmptyReturnThrowAndMore()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(205)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(215, 2),
+                OnCompleted<int>(220)
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(225, 3),
+                OnNext(230, 4),
+                OnCompleted<int>(235)
+            );
+
+            var o4 = scheduler.CreateHotObservable(
+                OnError<int>(240, new Exception())
+            );
+
+            var o5 = scheduler.CreateHotObservable(
+                OnNext<int>(245, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                new[] { o1, o2, o3, o4, o5 }.OnErrorResumeNext()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(215, 2),
+                OnNext(225, 3),
+                OnNext(230, 4),
+                OnNext(245, 5),
+                OnCompleted<int>(250)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 205)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(205, 220)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(220, 235)
+            );
+
+            o4.Subscriptions.AssertEqual(
+                Subscribe(235, 240)
+            );
+
+            o5.Subscriptions.AssertEqual(
+                Subscribe(240, 250)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_LastIsntSpecial()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(220)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnError<int>(230, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                o1.OnErrorResumeNext(o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnCompleted<int>(230)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(220, 230)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_SingleSourceDoesntThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnError<int>(230, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext(o1)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_EndWithNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(220)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext(o1, o2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(220, 1000)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_StartWithNever()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(220)
+            );
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext(o1, o2)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            o2.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_DefaultScheduler_Binary()
+        {
+            var evt = new ManualResetEvent(false);
+
+            int sum = 0;
+            Observable.Return(1).OnErrorResumeNext(Observable.Return(2)).Subscribe(x =>
+            {
+                sum += x;
+            }, () => evt.Set());
+
+            evt.WaitOne();
+            Assert.Equal(3, sum);
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_DefaultScheduler_Nary()
+        {
+            var evt = new ManualResetEvent(false);
+
+            int sum = 0;
+            Observable.OnErrorResumeNext(Observable.Return(1), Observable.Return(2), Observable.Return(3)).Subscribe(x =>
+            {
+                sum += x;
+            }, () => evt.Set());
+
+            evt.WaitOne();
+            Assert.Equal(6, sum);
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_DefaultScheduler_NaryEnumerable()
+        {
+            var evt = new ManualResetEvent(false);
+
+            IEnumerable<IObservable<int>> sources = new[] { Observable.Return(1), Observable.Return(2), Observable.Return(3) };
+
+            int sum = 0;
+            Observable.OnErrorResumeNext(sources).Subscribe(x =>
+            {
+                sum += x;
+            }, () => evt.Set());
+
+            evt.WaitOne();
+            Assert.Equal(6, sum);
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_IteratorThrows()
+        {
+            var scheduler = new TestScheduler();
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                Observable.OnErrorResumeNext<int>(Catch_IteratorThrows_Source(ex, true))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(200, ex)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_EnumerableThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(225, new Exception())
+            );
+
+            var ex = new Exception();
+            var xss = new MockEnumerable<IObservable<int>>(scheduler, GetObservablesForOnErrorResumeNextThrow(o, ex));
+
+            var res = scheduler.Start(() =>
+                xss.OnErrorResumeNext()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(225, ex)
+            );
+
+            o.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        private IEnumerable<IObservable<int>> GetObservablesForOnErrorResumeNextThrow(IObservable<int> first, Exception ex)
+        {
+            yield return first;
+            throw ex;
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_EnumerableTiming()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2), // !
+                OnNext(220, 3), // !
+                OnCompleted<int>(230)
+            );
+
+            var o2 = scheduler.CreateColdObservable(
+                OnNext(50, 4),  // !
+                OnNext(60, 5),  // !
+                OnNext(70, 6),  // !
+                OnError<int>(80, new Exception())
+            );
+
+            var o3 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(200, 2),
+                OnNext(210, 3),
+                OnNext(220, 4),
+                OnNext(230, 5),
+                OnNext(270, 6),
+                OnNext(320, 7), // !
+                OnNext(330, 8), // !
+                OnCompleted<int>(340)
+            );
+
+            var xss = new MockEnumerable<ITestableObservable<int>>(scheduler, new[] { o1, o2, o3, o2 });
+
+            var res = scheduler.Start(() =>
+                xss.Select(xs => (IObservable<int>)xs).OnErrorResumeNext()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 5),
+                OnNext(300, 6),
+                OnNext(320, 7),
+                OnNext(330, 8),
+                OnNext(390, 4),
+                OnNext(400, 5),
+                OnNext(410, 6),
+                OnCompleted<int>(420)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 310),
+                Subscribe(340, 420)
+            );
+
+            o3.Subscriptions.AssertEqual(
+                Subscribe(310, 340)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void OnErrorResumeNext_Enumerable_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var o1 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnError<int>(230, new Exception())
+            );
+
+            var o2 = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(200, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(320, 6),
+                OnNext(330, 7),
+                OnCompleted<int>(340)
+            );
+
+            var xss = new MockEnumerable<ITestableObservable<int>>(scheduler, new[] { o1, o2 });
+
+            var res = scheduler.Start(() =>
+                xss.Select(xs => (IObservable<int>)xs).OnErrorResumeNext(),
+                300
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(240, 4),
+                OnNext(270, 5)
+            );
+
+            o1.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+
+            o2.Subscriptions.AssertEqual(
+                Subscribe(230, 300)
+            );
+
+            xss.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+#if !NO_PERF
+        [Fact]
+        public void OnErrorResumeNext_TailRecursive1()
+        {
+            var create = 0L;
+            var start = 200L;
+            var end = 1000L;
+
+            var scheduler = new TestScheduler();
+
+            var o = scheduler.CreateColdObservable<int>(
+                OnNext(10, 1),
+                OnNext(20, 2),
+                OnNext(30, 3),
+                OnError<int>(40, new Exception())
+            );
+
+            var f = default(Func<IObservable<int>>);
+            f = () => Observable.Defer(() => o.OnErrorResumeNext(f()));
+
+            var res = scheduler.Start(() => f(), create, start, end);
+
+            var expected = new List<Recorded<Notification<int>>>();
+
+            var t = start;
+            while (t <= end)
+            {
+                var n = (t - start) / 10;
+                if (n % 4 != 0)
+                {
+                    expected.Add(OnNext(t, (int)(n % 4)));
+                }
+
+                t += 10;
+            }
+
+            res.Messages.AssertEqual(expected);
+        }
+
+#if HAS_STACKTRACE && !NO_THREAD
+        [Fact]
+        public void OnErrorResumeNext_TailRecursive2()
+        {
+            var f = default(Func<int, IObservable<int>>);
+            f = x => Observable.Defer(() => Observable.Throw<int>(new Exception(), ThreadPoolScheduler.Instance).StartWith(x).OnErrorResumeNext(f(x + 1)));
+
+            var lst = new List<int>();
+            f(0).Select(x => new StackTrace().FrameCount).Take(10).ForEach(lst.Add);
+
+            Assert.True(lst.Last() - lst.First() < 10);
+        }
+#endif
+
+        [Fact]
+        public void OnErrorResumeNext_TailRecursive3()
+        {
+            var ex = new Exception();
+
+            var res =
+                Observable.OnErrorResumeNext(
+                    Observable.Return(1),
+                    Observable.Defer(() =>
+                    {
+                        if (ex != null)
+                        {
+                            throw ex;
+                        }
+
+                        return Observable.Return(-2);
+                    }),
+                    Observable.Defer(() =>
+                    {
+                        if (ex != null)
+                        {
+                            throw ex;
+                        }
+
+                        return Observable.Return(-1);
+                    }),
+                    Observable.Return(2)
+                )
+                .SequenceEqual(new[] { 1, 2 });
+
+            Assert.True(res.Wait());
+        }
+#endif
+
+        private IEnumerable<IObservable<int>> Catch_IteratorThrows_Source(Exception ex, bool b)
+        {
+            if (b)
+                throw ex;
+            else
+                yield break;
+        }
+
+    }
+}

+ 370 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/PublishLastTest.cs

@@ -0,0 +1,370 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class PublishLastTest : ReactiveTest
+    {
+
+        [Fact]
+        public void PublishLast_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+            var scheduler = new TestScheduler();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.PublishLast(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.PublishLast(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.PublishLast<int, int>(someObservable, null));
+        }
+
+        [Fact]
+        public void PublishLast_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.PublishLast());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void PublishLast_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.PublishLast());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishLast_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.PublishLast());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(600, 20),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishLast_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.PublishLast());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(350, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void PublishLast_MultipleConnections()
+        {
+            var xs = Observable.Never<int>();
+            var ys = xs.PublishLast();
+
+            var connection1 = ys.Connect();
+            var connection2 = ys.Connect();
+
+            Assert.Same(connection1, connection2);
+
+            connection1.Dispose();
+            connection2.Dispose();
+
+            var connection3 = ys.Connect();
+
+            Assert.NotSame(connection1, connection3);
+
+            connection3.Dispose();
+        }
+
+        [Fact]
+        public void PublishLastLambda_Zip_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.PublishLast(_xs => _xs.Zip(_xs, (x, y) => x + y))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(600, 40),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishLastLambda_Zip_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.PublishLast(_xs => _xs.Zip(_xs, (x, y) => x + y))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishLastLambda_Zip_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.PublishLast(_xs => _xs.Zip(_xs, (x, y) => x + y)),
+                470
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+    }
+}

+ 856 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/PublishTest.cs

@@ -0,0 +1,856 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class PublishTest : ReactiveTest
+    {
+        [Fact]
+        public void Publish_Cold_Zip()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(40, 0),
+                OnNext(90, 1),
+                OnNext(150, 2),
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(270, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(390)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(ys => ys.Zip(ys, (a, b) => a + b))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 6),
+                OnNext(240, 8),
+                OnNext(270, 10),
+                OnNext(330, 12),
+                OnNext(340, 14),
+                OnCompleted<int>(390)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+        }
+
+        [Fact]
+        public void Publish_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Publish(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Publish(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Publish<int, int>(someObservable, null));
+        }
+
+        [Fact]
+        public void Publish_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(520, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void Publish_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void Publish_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void Publish_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish());
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(350, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(340, 8)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void Publish_MultipleConnections()
+        {
+            var xs = Observable.Never<int>();
+            var ys = xs.Publish();
+
+            var connection1 = ys.Connect();
+            var connection2 = ys.Connect();
+
+            Assert.Same(connection1, connection2);
+
+            connection1.Dispose();
+            connection2.Dispose();
+
+            var connection3 = ys.Connect();
+
+            Assert.NotSame(connection1, connection3);
+
+            connection3.Dispose();
+        }
+
+        [Fact]
+        public void PublishLambda_Zip_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(_xs => _xs.Zip(_xs.Skip(1), (prev, cur) => cur + prev))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 7),
+                OnNext(290, 5),
+                OnNext(340, 9),
+                OnNext(360, 13),
+                OnNext(370, 11),
+                OnNext(390, 13),
+                OnNext(410, 20),
+                OnNext(430, 15),
+                OnNext(450, 11),
+                OnNext(520, 20),
+                OnNext(560, 31),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishLambda_Zip_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(_xs => _xs.Zip(_xs.Skip(1), (prev, cur) => cur + prev))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 7),
+                OnNext(290, 5),
+                OnNext(340, 9),
+                OnNext(360, 13),
+                OnNext(370, 11),
+                OnNext(390, 13),
+                OnNext(410, 20),
+                OnNext(430, 15),
+                OnNext(450, 11),
+                OnNext(520, 20),
+                OnNext(560, 31),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishLambda_Zip_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(_xs => _xs.Zip(_xs.Skip(1), (prev, cur) => cur + prev)),
+                470
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 7),
+                OnNext(290, 5),
+                OnNext(340, 9),
+                OnNext(360, 13),
+                OnNext(370, 11),
+                OnNext(390, 13),
+                OnNext(410, 20),
+                OnNext(430, 15),
+                OnNext(450, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Publish(default(IObservable<int>), 1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Publish(default(IObservable<int>), x => x, 1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Publish(someObservable, default(Func<IObservable<int>, IObservable<int>>), 1));
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_SanityCheck()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            Observable.Publish(Observable.Range(1, 10), x => x, 0).AssertEqual(Observable.Range(0, 11));
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish(1979));
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1979),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(520, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish(1979));
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1979),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish(1979));
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1979),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Publish(1979));
+            scheduler.ScheduleAbsolute(Subscribed, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(350, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1979),
+                OnNext(340, 8)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValue_MultipleConnections()
+        {
+            var xs = Observable.Never<int>();
+            var ys = xs.Publish(1979);
+
+            var connection1 = ys.Connect();
+            var connection2 = ys.Connect();
+
+            Assert.Same(connection1, connection2);
+
+            connection1.Dispose();
+            connection2.Dispose();
+
+            var connection3 = ys.Connect();
+
+            Assert.NotSame(connection1, connection3);
+
+            connection3.Dispose();
+        }
+
+        [Fact]
+        public void PublishWithInitialValueLambda_Zip_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(_xs => _xs.Zip(_xs.Skip(1), (prev, cur) => cur + prev), 1979)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 1982),
+                OnNext(280, 7),
+                OnNext(290, 5),
+                OnNext(340, 9),
+                OnNext(360, 13),
+                OnNext(370, 11),
+                OnNext(390, 13),
+                OnNext(410, 20),
+                OnNext(430, 15),
+                OnNext(450, 11),
+                OnNext(520, 20),
+                OnNext(560, 31),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValueLambda_Zip_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(_xs => _xs.Zip(_xs.Skip(1), (prev, cur) => cur + prev), 1979)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 1982),
+                OnNext(280, 7),
+                OnNext(290, 5),
+                OnNext(340, 9),
+                OnNext(360, 13),
+                OnNext(370, 11),
+                OnNext(390, 13),
+                OnNext(410, 20),
+                OnNext(430, 15),
+                OnNext(450, 11),
+                OnNext(520, 20),
+                OnNext(560, 31),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void PublishWithInitialValueLambda_Zip_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Publish(_xs => _xs.Zip(_xs.Skip(1), (prev, cur) => cur + prev), 1979),
+                470
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 1982),
+                OnNext(280, 7),
+                OnNext(290, 5),
+                OnNext(340, 9),
+                OnNext(360, 13),
+                OnNext(370, 11),
+                OnNext(390, 13),
+                OnNext(410, 20),
+                OnNext(430, 15),
+                OnNext(450, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+    }
+}

+ 220 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RangeTest.cs

@@ -0,0 +1,220 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+using System.Runtime.CompilerServices;
+
+namespace ReactiveTests.Tests
+{
+    public class RangeTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Range_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Range(0, 0, null));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Range(0, -1, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Range(int.MaxValue, 2, DummyScheduler.Instance));
+        }
+
+        [Fact]
+        public void Range_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Range(0, 0, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(201)
+            );
+        }
+
+        [Fact]
+        public void Range_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Range(0, 1, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 0),
+                OnCompleted<int>(202)
+            );
+        }
+
+        [Fact]
+        public void Range_Five()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Range(10, 5, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 10),
+                OnNext(202, 11),
+                OnNext(203, 12),
+                OnNext(204, 13),
+                OnNext(205, 14),
+                OnCompleted<int>(206)
+            );
+        }
+
+        [Fact]
+        public void Range_Boundaries()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Range(int.MaxValue, 1, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, int.MaxValue),
+                OnCompleted<int>(202)
+            );
+        }
+
+        [Fact]
+        public void Range_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Range(-10, 5, scheduler),
+                204
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, -10),
+                OnNext(202, -9),
+                OnNext(203, -8)
+            );
+        }
+
+        [Fact]
+        public void Range_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Range(0, -1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Range(int.MaxValue, 2));
+        }
+
+        [Fact]
+        public void Range_Default()
+        {
+            for (int i = 0; i < 100; i++)
+                Observable.Range(100, 100).AssertEqual(Observable.Range(100, 100, DefaultScheduler.Instance));
+        }
+
+#if !NO_PERF
+        [Fact]
+        public void Range_LongRunning1()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Range(0, 100, s);
+
+            var lst = new List<int>();
+            var done = false;
+            xs.Subscribe(x => { lst.Add(x); }, () => done = true);
+
+            end.WaitOne();
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(0, 100)));
+            Assert.True(done);
+        }
+
+        [Fact]
+        [MethodImpl(MethodImplOptions.NoOptimization)]
+        public void Range_LongRunning2()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Range(0, int.MaxValue, s);
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(x => { lst.Add(x); });
+
+            start.WaitOne();
+
+            while (lst.Count < 100)
+                ;
+
+            d.Dispose();
+            end.WaitOne();
+
+            Assert.True(true);
+        }
+
+        [Fact]
+        public void Range_LongRunning_Empty()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var scheduler = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Range(5, 0, scheduler);
+
+            var lst = new List<int>();
+            xs.ForEach(lst.Add);
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(5, 0)));
+        }
+
+        [Fact]
+        public void Range_LongRunning_Regular()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var scheduler = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Range(5, 17, scheduler);
+
+            var lst = new List<int>();
+            xs.ForEach(lst.Add);
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(5, 17)));
+        }
+
+        [Fact]
+        public void Range_LongRunning_Boundaries()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var scheduler = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Range(int.MaxValue, 1, scheduler);
+
+            var lst = new List<int>();
+            xs.ForEach(lst.Add);
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(int.MaxValue, 1)));
+        }
+#endif
+
+    }
+}

+ 188 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RefCountTest.cs

@@ -0,0 +1,188 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class RefCountTest : ReactiveTest
+    {
+
+        [Fact]
+        public void RefCount_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.RefCount<int>(null));
+        }
+
+        [Fact]
+        public void RefCount_ConnectsOnFirst()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            var subject = new MySubject();
+
+            var conn = new ConnectableObservable<int>(xs, subject);
+
+            var res = scheduler.Start(() =>
+                conn.RefCount()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnCompleted<int>(250)
+            );
+
+            Assert.True(subject.Disposed);
+        }
+
+        [Fact]
+        public void RefCount_NotConnected()
+        {
+            var disconnected = false;
+            var count = 0;
+            var xs = Observable.Defer(() =>
+            {
+                count++;
+                return Observable.Create<int>(obs =>
+                {
+                    return () => { disconnected = true; };
+                });
+            });
+
+            var subject = new MySubject();
+
+            var conn = new ConnectableObservable<int>(xs, subject);
+            var refd = conn.RefCount();
+
+            var dis1 = refd.Subscribe();
+            Assert.Equal(1, count);
+            Assert.Equal(1, subject.SubscribeCount);
+            Assert.False(disconnected);
+
+            var dis2 = refd.Subscribe();
+            Assert.Equal(1, count);
+            Assert.Equal(2, subject.SubscribeCount);
+            Assert.False(disconnected);
+
+            dis1.Dispose();
+            Assert.False(disconnected);
+            dis2.Dispose();
+            Assert.True(disconnected);
+            disconnected = false;
+
+            var dis3 = refd.Subscribe();
+            Assert.Equal(2, count);
+            Assert.Equal(3, subject.SubscribeCount);
+            Assert.False(disconnected);
+
+            dis3.Dispose();
+            Assert.True(disconnected);
+        }
+
+        [Fact]
+        public void RefCount_OnError()
+        {
+            var ex = new Exception();
+            var xs = Observable.Throw<int>(ex, Scheduler.Immediate);
+
+            var res = xs.Publish().RefCount();
+
+            res.Subscribe(_ => { Assert.True(false); }, ex_ => { Assert.Same(ex, ex_); }, () => { Assert.True(false); });
+            res.Subscribe(_ => { Assert.True(false); }, ex_ => { Assert.Same(ex, ex_); }, () => { Assert.True(false); });
+        }
+
+        [Fact]
+        public void RefCount_Publish()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnNext(270, 7),
+                OnNext(280, 8),
+                OnNext(290, 9),
+                OnCompleted<int>(300)
+            );
+
+            var res = xs.Publish().RefCount();
+
+            var d1 = default(IDisposable);
+            var o1 = scheduler.CreateObserver<int>();
+            scheduler.ScheduleAbsolute(215, () => { d1 = res.Subscribe(o1); });
+            scheduler.ScheduleAbsolute(235, () => { d1.Dispose(); });
+
+            var d2 = default(IDisposable);
+            var o2 = scheduler.CreateObserver<int>();
+            scheduler.ScheduleAbsolute(225, () => { d2 = res.Subscribe(o2); });
+            scheduler.ScheduleAbsolute(275, () => { d2.Dispose(); });
+
+            var d3 = default(IDisposable);
+            var o3 = scheduler.CreateObserver<int>();
+            scheduler.ScheduleAbsolute(255, () => { d3 = res.Subscribe(o3); });
+            scheduler.ScheduleAbsolute(265, () => { d3.Dispose(); });
+
+            var d4 = default(IDisposable);
+            var o4 = scheduler.CreateObserver<int>();
+            scheduler.ScheduleAbsolute(285, () => { d4 = res.Subscribe(o4); });
+            scheduler.ScheduleAbsolute(320, () => { d4.Dispose(); });
+
+            scheduler.Start();
+
+            o1.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnNext(230, 3)
+            );
+
+            o2.Messages.AssertEqual(
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnNext(270, 7)
+            );
+
+            o3.Messages.AssertEqual(
+                OnNext(260, 6)
+            );
+
+            o4.Messages.AssertEqual(
+                OnNext(290, 9),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(215, 275),
+                Subscribe(285, 300)
+            );
+        }
+
+    }
+}

+ 548 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RepeatTest.cs

@@ -0,0 +1,548 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+using System.Runtime.CompilerServices;
+
+namespace ReactiveTests.Tests
+{
+    public class RepeatTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Repeat_Value_Count_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat(1, 0, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Repeat(1, -1, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat(1, 1, DummyScheduler.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Value_Count_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Repeat(42, 0, scheduler)
+            );
+
+#if !NO_PERF
+            res.Messages.AssertEqual(
+                OnCompleted<int>(201)
+            );
+#else
+            res.Messages.AssertEqual(
+                OnCompleted<int>(200)
+            );
+#endif
+        }
+
+        [Fact]
+        public void Repeat_Value_Count_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Repeat(42, 1, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 42),
+                OnCompleted<int>(201)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Value_Count_Ten()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Repeat(42, 10, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 42),
+                OnNext(202, 42),
+                OnNext(203, 42),
+                OnNext(204, 42),
+                OnNext(205, 42),
+                OnNext(206, 42),
+                OnNext(207, 42),
+                OnNext(208, 42),
+                OnNext(209, 42),
+                OnNext(210, 42),
+                OnCompleted<int>(210)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Value_Count_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Repeat(42, 10, scheduler),
+                207
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 42),
+                OnNext(202, 42),
+                OnNext(203, 42),
+                OnNext(204, 42),
+                OnNext(205, 42),
+                OnNext(206, 42)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Value_Count_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Repeat(1, -1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat(1, 1).Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Value_Count_Default()
+        {
+            Observable.Repeat(42, 10).AssertEqual(Observable.Repeat(42, 10, DefaultScheduler.Instance));
+        }
+
+        [Fact]
+        public void Repeat_Value_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat(1, (IScheduler)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat(DummyScheduler.Instance, 1).Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Value()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Repeat(42, scheduler),
+                207
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 42),
+                OnNext(202, 42),
+                OnNext(203, 42),
+                OnNext(204, 42),
+                OnNext(205, 42),
+                OnNext(206, 42)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Value_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat(1).Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Value_Default()
+        {
+            Observable.Repeat(42).Take(100).AssertEqual(Observable.Repeat(42, DefaultScheduler.Instance).Take(100));
+        }
+
+#if !NO_PERF
+        [Fact]
+        public void Repeat_Count_LongRunning1()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Repeat(42, 100, s);
+
+            var lst = new List<int>();
+            var done = false;
+            xs.Subscribe(x => { lst.Add(x); }, () => done = true);
+
+            end.WaitOne();
+
+            Assert.True(lst.SequenceEqual(Enumerable.Repeat(42, 100)));
+            Assert.True(done);
+        }
+
+        [Fact]
+        [MethodImpl(MethodImplOptions.NoOptimization)]
+        public void Repeat_Count_LongRunning2()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Repeat(42, int.MaxValue, s);
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(x => { lst.Add(x); });
+
+            start.WaitOne();
+
+            while (lst.Count < 100)
+                ;
+
+            d.Dispose();
+            end.WaitOne();
+
+            Assert.True(true);
+        }
+
+        [Fact]
+        [MethodImpl(MethodImplOptions.NoOptimization)]
+        public void Repeat_Inf_LongRunning()
+        {
+            var start = default(ManualResetEvent);
+            var end = default(ManualResetEvent);
+            var s = new TestLongRunningScheduler(x => start = x, x => end = x);
+
+            var xs = Observable.Repeat(42, s);
+
+            var lst = new List<int>();
+            var d = xs.Subscribe(x => { lst.Add(x); });
+
+            start.WaitOne();
+
+            while (lst.Count < 100)
+                ;
+
+            d.Dispose();
+            end.WaitOne();
+
+            Assert.True(true);
+        }
+#endif
+
+        [Fact]
+        public void Repeat_Observable_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat<int>(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Repeat().Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Observable_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3),
+                OnNext(550, 1),
+                OnNext(600, 2),
+                OnNext(650, 3),
+                OnNext(800, 1),
+                OnNext(850, 2),
+                OnNext(900, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450),
+                Subscribe(450, 700),
+                Subscribe(700, 950),
+                Subscribe(950, 1000)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_Infinite()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3),
+                OnError<int>(450, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_Throws()
+        {
+            var scheduler1 = new TestScheduler();
+
+            var xs = Observable.Return(1, scheduler1).Repeat();
+
+            xs.Subscribe(x => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler1.Start());
+
+            var scheduler2 = new TestScheduler();
+
+            var ys = Observable.Throw<int>(new Exception(), scheduler2).Repeat();
+
+            ys.Subscribe(x => { }, ex => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler2.Start());
+
+            var scheduler3 = new TestScheduler();
+
+            var zs = Observable.Return(1, scheduler3).Repeat();
+
+            var d = zs.Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); });
+
+            scheduler3.ScheduleAbsolute(210, () => d.Dispose());
+
+            scheduler3.Start();
+
+            var xss = Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Repeat();
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => xss.Subscribe());
+        }
+
+        [Fact]
+        public void Repeat_Observable_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat<int>((IObservable<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Repeat().Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat<int>(null, 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.Repeat(-1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Repeat(0).Subscribe(null));
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(5, 1),
+                OnNext(10, 2),
+                OnNext(15, 3),
+                OnCompleted<int>(20)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(225, 1),
+                OnNext(230, 2),
+                OnNext(235, 3),
+                OnNext(245, 1),
+                OnNext(250, 2),
+                OnNext(255, 3),
+                OnCompleted<int>(260)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220),
+                Subscribe(220, 240),
+                Subscribe(240, 260)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(5, 1),
+                OnNext(10, 2),
+                OnNext(15, 3),
+                OnCompleted<int>(20)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat(3), 231
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(225, 1),
+                OnNext(230, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220),
+                Subscribe(220, 231)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_Infinite()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Repeat(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3),
+                OnError<int>(450, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_Throws()
+        {
+            var scheduler1 = new TestScheduler();
+
+            var xs = Observable.Return(1, scheduler1).Repeat(3);
+
+            xs.Subscribe(x => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler1.Start());
+
+            var scheduler2 = new TestScheduler();
+
+            var ys = Observable.Throw<int>(new Exception(), scheduler2).Repeat(3);
+
+            ys.Subscribe(x => { }, ex => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler2.Start());
+
+            var scheduler3 = new TestScheduler();
+
+            var zs = Observable.Return(1, scheduler3).Repeat(100);
+
+            var d = zs.Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); });
+
+            scheduler3.ScheduleAbsolute(10, () => d.Dispose());
+
+            scheduler3.Start();
+
+            var xss = Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Repeat(3);
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => xss.Subscribe());
+        }
+
+        [Fact]
+        public void Repeat_Observable_RepeatCount_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Repeat<int>(default(IObservable<int>), 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.Repeat(-1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Repeat(0).Subscribe(null));
+        }
+
+    }
+}

+ 1005 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ReplayTest.cs

@@ -0,0 +1,1005 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ReplayTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Replay_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+            var scheduler = new TestScheduler();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int>(null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int>(DummyObservable<int>.Instance, (IScheduler)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(null, DummyFunc<IObservable<int>, IObservable<int>>.Instance, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(DummyObservable<int>.Instance, null, DummyScheduler.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(DummyObservable<int>.Instance, DummyFunc<IObservable<int>, IObservable<int>>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay<int, int>(someObservable, x => x, TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, TimeSpan.FromSeconds(-1), scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(someObservable, TimeSpan.FromSeconds(1), default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, TimeSpan.FromSeconds(-1), scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(someObservable, x => x, TimeSpan.FromSeconds(1), default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), 1, scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, -2, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(someObservable, 1, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x, 1, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null, -2, scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, -2, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(someObservable, x => x, 1, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, -2));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x, 1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null, 1));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, -2));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), 1, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, -2, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, 1, TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x, 1, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null, 1, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, -2, TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, 1, TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), 1, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, -2, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, 1, TimeSpan.FromSeconds(-1), scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(someObservable, 1, TimeSpan.FromSeconds(1), null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(default(IObservable<int>), x => x, 1, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay<int, int>(someObservable, null, 1, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, -2, TimeSpan.FromSeconds(1), scheduler));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Replay(someObservable, x => x, 1, TimeSpan.FromSeconds(-1), scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Replay(someObservable, x => x, 1, TimeSpan.FromSeconds(1), null));
+        }
+
+        [Fact]
+        public void ReplayCount_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(3, scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 5),
+                OnNext(452, 6),
+                OnNext(453, 7),
+                OnNext(521, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void ReplayCount_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(3, scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 5),
+                OnNext(452, 6),
+                OnNext(453, 7),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnError<int>(601, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayCount_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(3, scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 5),
+                OnNext(452, 6),
+                OnNext(453, 7),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnCompleted<int>(601)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayCount_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(3, scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(475, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 5),
+                OnNext(452, 6),
+                OnNext(453, 7)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void ReplayCount_MultipleConnections()
+        {
+            var xs = Observable.Never<int>();
+            var ys = xs.Replay(3, new TestScheduler());
+
+            var connection1 = ys.Connect();
+            var connection2 = ys.Connect();
+
+            Assert.Same(connection1, connection2);
+
+            connection1.Dispose();
+            connection2.Dispose();
+
+            var connection3 = ys.Connect();
+
+            Assert.NotSame(connection1, connection3);
+
+            connection3.Dispose();
+        }
+
+        [Fact]
+        public void ReplayCountLambda_Zip_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Replay(_xs => _xs.Take(6).Repeat(), 3, scheduler),
+                610
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 3),
+                OnNext(281, 4),
+                OnNext(291, 1),
+                OnNext(341, 8),
+                OnNext(361, 5),
+                OnNext(371, 6),
+                OnNext(372, 8),
+                OnNext(373, 5),
+                OnNext(374, 6),
+                OnNext(391, 7),
+                OnNext(411, 13),
+                OnNext(431, 2),
+                OnNext(432, 7),
+                OnNext(433, 13),
+                OnNext(434, 2),
+                OnNext(451, 9),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnNext(562, 9),
+                OnNext(563, 11),
+                OnNext(564, 20),
+                OnNext(602, 9),
+                OnNext(603, 11),
+                OnNext(604, 20),
+                OnNext(606, 9),
+                OnNext(607, 11),
+                OnNext(608, 20)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayCountLambda_Zip_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Replay(_xs => _xs.Take(6).Repeat(), 3, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 3),
+                OnNext(281, 4),
+                OnNext(291, 1),
+                OnNext(341, 8),
+                OnNext(361, 5),
+                OnNext(371, 6),
+                OnNext(372, 8),
+                OnNext(373, 5),
+                OnNext(374, 6),
+                OnNext(391, 7),
+                OnNext(411, 13),
+                OnNext(431, 2),
+                OnNext(432, 7),
+                OnNext(433, 13),
+                OnNext(434, 2),
+                OnNext(451, 9),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnNext(562, 9),
+                OnNext(563, 11),
+                OnNext(564, 20),
+                OnError<int>(601, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayCountLambda_Zip_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Replay(_xs => _xs.Take(6).Repeat(), 3, scheduler),
+                470
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 3),
+                OnNext(281, 4),
+                OnNext(291, 1),
+                OnNext(341, 8),
+                OnNext(361, 5),
+                OnNext(371, 6),
+                OnNext(372, 8),
+                OnNext(373, 5),
+                OnNext(374, 6),
+                OnNext(391, 7),
+                OnNext(411, 13),
+                OnNext(431, 2),
+                OnNext(432, 7),
+                OnNext(433, 13),
+                OnNext(434, 2),
+                OnNext(451, 9)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+        [Fact]
+        public void ReplayTime_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(TimeSpan.FromTicks(150), scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 8),
+                OnNext(452, 5),
+                OnNext(453, 6),
+                OnNext(454, 7),
+                OnNext(521, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void ReplayTime_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(TimeSpan.FromTicks(75), scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 7),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnError<int>(601, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayTime_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(TimeSpan.FromTicks(85), scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(Disposed, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 6),
+                OnNext(452, 7),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnCompleted<int>(601)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayTime_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var ys = default(IConnectableObservable<int>);
+            var subscription = default(IDisposable);
+            var connection = default(IDisposable);
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(Created, () => ys = xs.Replay(TimeSpan.FromTicks(100), scheduler));
+            scheduler.ScheduleAbsolute(450, () => subscription = ys.Subscribe(res));
+            scheduler.ScheduleAbsolute(475, () => subscription.Dispose());
+
+            scheduler.ScheduleAbsolute(300, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(400, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(500, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(550, () => connection.Dispose());
+
+            scheduler.ScheduleAbsolute(650, () => connection = ys.Connect());
+            scheduler.ScheduleAbsolute(800, () => connection.Dispose());
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(451, 5),
+                OnNext(452, 6),
+                OnNext(453, 7)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(300, 400),
+                Subscribe(500, 550),
+                Subscribe(650, 800)
+            );
+        }
+
+        [Fact]
+        public void ReplayTime_MultipleConnections()
+        {
+            var xs = Observable.Never<int>();
+            var ys = xs.Replay(TimeSpan.FromTicks(100), new TestScheduler());
+
+            var connection1 = ys.Connect();
+            var connection2 = ys.Connect();
+
+            Assert.Same(connection1, connection2);
+
+            connection1.Dispose();
+            connection2.Dispose();
+
+            var connection3 = ys.Connect();
+
+            Assert.NotSame(connection1, connection3);
+
+            connection3.Dispose();
+        }
+
+        [Fact]
+        public void ReplayTimeLambda_Zip_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Replay(_xs => _xs.Take(6).Repeat(), TimeSpan.FromTicks(50), scheduler),
+                610
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 3),
+                OnNext(281, 4),
+                OnNext(291, 1),
+                OnNext(341, 8),
+                OnNext(361, 5),
+                OnNext(371, 6),
+                OnNext(372, 8),
+                OnNext(373, 5),
+                OnNext(374, 6),
+                OnNext(391, 7),
+                OnNext(411, 13),
+                OnNext(431, 2),
+                OnNext(432, 7),
+                OnNext(433, 13),
+                OnNext(434, 2),
+                OnNext(451, 9),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnNext(562, 11),
+                OnNext(563, 20),
+                OnNext(602, 20),
+                OnNext(604, 20),
+                OnNext(606, 20),
+                OnNext(608, 20)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayTimeLambda_Zip_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Replay(_xs => _xs.Take(6).Repeat(), TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 3),
+                OnNext(281, 4),
+                OnNext(291, 1),
+                OnNext(341, 8),
+                OnNext(361, 5),
+                OnNext(371, 6),
+                OnNext(372, 8),
+                OnNext(373, 5),
+                OnNext(374, 6),
+                OnNext(391, 7),
+                OnNext(411, 13),
+                OnNext(431, 2),
+                OnNext(432, 7),
+                OnNext(433, 13),
+                OnNext(434, 2),
+                OnNext(451, 9),
+                OnNext(521, 11),
+                OnNext(561, 20),
+                OnNext(562, 11),
+                OnNext(563, 20),
+                OnError<int>(601, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void ReplayTimeLambda_Zip_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 7),
+                OnNext(220, 3),
+                OnNext(280, 4),
+                OnNext(290, 1),
+                OnNext(340, 8),
+                OnNext(360, 5),
+                OnNext(370, 6),
+                OnNext(390, 7),
+                OnNext(410, 13),
+                OnNext(430, 2),
+                OnNext(450, 9),
+                OnNext(520, 11),
+                OnNext(560, 20),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Replay(_xs => _xs.Take(6).Repeat(), TimeSpan.FromTicks(50), scheduler),
+                470
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 3),
+                OnNext(281, 4),
+                OnNext(291, 1),
+                OnNext(341, 8),
+                OnNext(361, 5),
+                OnNext(371, 6),
+                OnNext(372, 8),
+                OnNext(373, 5),
+                OnNext(374, 6),
+                OnNext(391, 7),
+                OnNext(411, 13),
+                OnNext(431, 2),
+                OnNext(432, 7),
+                OnNext(433, 13),
+                OnNext(434, 2),
+                OnNext(451, 9)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+        }
+
+        [Fact]
+        public void Replay_Default1()
+        {
+            var s = new Subject<int>();
+            var xs = s.Replay(100, DefaultScheduler.Instance);
+            var ys = s.Replay(100);
+
+            xs.Connect();
+            ys.Connect();
+
+            s.OnNext(1);
+            s.OnNext(2);
+            s.OnCompleted();
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void Replay_Default2()
+        {
+            var s = new Subject<int>();
+            var xs = s.Replay(TimeSpan.FromHours(1), DefaultScheduler.Instance);
+            var ys = s.Replay(TimeSpan.FromHours(1));
+
+            xs.Connect();
+            ys.Connect();
+
+            s.OnNext(1);
+            s.OnNext(2);
+            s.OnCompleted();
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void Replay_Default3()
+        {
+            var s = new Subject<int>();
+            var xs = s.Replay(100, TimeSpan.FromHours(1), DefaultScheduler.Instance);
+            var ys = s.Replay(100, TimeSpan.FromHours(1));
+
+            xs.Connect();
+            ys.Connect();
+
+            s.OnNext(1);
+            s.OnNext(2);
+            s.OnCompleted();
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void Replay_Default4()
+        {
+            var s = new Subject<int>();
+            var xs = s.Replay(DefaultScheduler.Instance);
+            var ys = s.Replay();
+
+            xs.Connect();
+            ys.Connect();
+
+            s.OnNext(1);
+            s.OnNext(2);
+            s.OnCompleted();
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void ReplayLambda_Default1()
+        {
+            var xs = Observable.Range(1, 10).Replay(_xs => _xs, 100, DefaultScheduler.Instance);
+            var ys = Observable.Range(1, 10).Replay(_xs => _xs, 100);
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void ReplayLambda_Default2()
+        {
+            var xs = Observable.Range(1, 10).Replay(_xs => _xs, TimeSpan.FromHours(1), DefaultScheduler.Instance);
+            var ys = Observable.Range(1, 10).Replay(_xs => _xs, TimeSpan.FromHours(1));
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void ReplayLambda_Default3()
+        {
+            var xs = Observable.Range(1, 10).Replay(_xs => _xs, 100, TimeSpan.FromHours(1), DefaultScheduler.Instance);
+            var ys = Observable.Range(1, 10).Replay(_xs => _xs, 100, TimeSpan.FromHours(1));
+
+            xs.AssertEqual(ys);
+        }
+
+        [Fact]
+        public void ReplayLambda_Default4()
+        {
+            var xs = Observable.Range(1, 10).Replay(_xs => _xs, DefaultScheduler.Instance);
+            var ys = Observable.Range(1, 10).Replay(_xs => _xs);
+
+            xs.AssertEqual(ys);
+        }
+
+    }
+}

+ 363 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RetryTest.cs

@@ -0,0 +1,363 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class RetryTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Retry_Observable_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Retry<int>(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Retry().Subscribe(null));
+        }
+
+        [Fact]
+        public void Retry_Observable_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3),
+                OnCompleted<int>(450)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_Infinite()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry(), 1100
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3),
+                OnNext(550, 1),
+                OnNext(600, 2),
+                OnNext(650, 3),
+                OnNext(800, 1),
+                OnNext(850, 2),
+                OnNext(900, 3),
+                OnNext(1050, 1)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450),
+                Subscribe(450, 700),
+                Subscribe(700, 950),
+                Subscribe(950, 1100)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_Throws1()
+        {
+            var scheduler1 = new TestScheduler();
+
+            var xs = Observable.Return(1, scheduler1).Retry();
+
+            xs.Subscribe(x => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler1.Start());
+        }
+
+        [Fact]
+        public void Retry_Observable_Throws2()
+        {
+            var scheduler2 = new TestScheduler();
+
+            var ys = Observable.Throw<int>(new Exception(), scheduler2).Retry();
+
+            var d = ys.Subscribe(x => { }, ex => { throw new InvalidOperationException(); });
+
+            scheduler2.ScheduleAbsolute(210, () => d.Dispose());
+
+            scheduler2.Start();
+        }
+
+        [Fact]
+        public void Retry_Observable_Throws3()
+        {
+            var scheduler3 = new TestScheduler();
+
+            var zs = Observable.Return(1, scheduler3).Retry();
+
+            zs.Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler3.Start());
+        }
+
+        /*
+         * BREAKING CHANGE v2.0 > v1.x - The code below will loop endlessly, trying to repeat the failing subscription,
+         *                               whose exception is propagated through OnError starting from v2.0.
+         * 
+        [Fact]
+        public void Retry_Observable_Throws4()
+        {
+            var xss = Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Retry();
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => xss.Subscribe());
+        }
+         */
+
+        [Fact]
+        public void Retry_Observable_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Retry<int>((IObservable<int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Retry().Subscribe(null));
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Retry<int>(null, 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.Retry(-1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Retry(0).Subscribe(null));
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(5, 1),
+                OnNext(10, 2),
+                OnNext(15, 3),
+                OnError<int>(20, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(225, 1),
+                OnNext(230, 2),
+                OnNext(235, 3),
+                OnNext(245, 1),
+                OnNext(250, 2),
+                OnNext(255, 3),
+                OnError<int>(260, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220),
+                Subscribe(220, 240),
+                Subscribe(240, 260)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(5, 1),
+                OnNext(10, 2),
+                OnNext(15, 3),
+                OnError<int>(20, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry(3), 231
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(205, 1),
+                OnNext(210, 2),
+                OnNext(215, 3),
+                OnNext(225, 1),
+                OnNext(230, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220),
+                Subscribe(220, 231)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Infinite()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 1),
+                OnNext(150, 2),
+                OnNext(200, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Retry(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(300, 1),
+                OnNext(350, 2),
+                OnNext(400, 3),
+                OnCompleted<int>(450)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Throws()
+        {
+            var scheduler1 = new TestScheduler();
+
+            var xs = Observable.Return(1, scheduler1).Retry(3);
+
+            xs.Subscribe(x => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler1.Start());
+
+            var scheduler2 = new TestScheduler();
+
+            var ys = Observable.Throw<int>(new Exception(), scheduler2).Retry(100);
+
+            var d = ys.Subscribe(x => { }, ex => { throw new InvalidOperationException(); });
+
+            scheduler2.ScheduleAbsolute(10, () => d.Dispose());
+
+            scheduler2.Start();
+
+            var scheduler3 = new TestScheduler();
+
+            var zs = Observable.Return(1, scheduler3).Retry(100);
+
+            zs.Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler3.Start());
+
+            var xss = Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Retry(3);
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => xss.Subscribe());
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Default_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Retry<int>(default(IObservable<int>), 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.Retry(-1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Retry(0).Subscribe(null));
+        }
+
+        [Fact]
+        public void Retry_Observable_RetryCount_Default()
+        {
+            Observable.Range(1, 3).Retry(3).AssertEqual(Observable.Range(1, 3).Retry(3));
+        }
+
+    }
+}

+ 119 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ReturnTest.cs

@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ReturnTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Return_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Return(0, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Return(0, DummyScheduler.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Return_Basic()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Return(42, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(201, 42),
+                OnCompleted<int>(201)
+            );
+        }
+
+        [Fact]
+        public void Return_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var res = scheduler.Start(() =>
+                Observable.Return(42, scheduler),
+                200
+            );
+
+            res.Messages.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void Return_DisposedAfterNext()
+        {
+            var scheduler = new TestScheduler();
+
+            var d = new SerialDisposable();
+
+            var xs = Observable.Return(42, scheduler);
+
+            var res = scheduler.CreateObserver<int>();
+
+            scheduler.ScheduleAbsolute(100, () =>
+                d.Disposable = xs.Subscribe(
+                    x =>
+                    {
+                        d.Dispose();
+                        res.OnNext(x);
+                    },
+                    res.OnError,
+                    res.OnCompleted
+                )
+            );
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(101, 42)
+            );
+        }
+
+        [Fact]
+        public void Return_ObserverThrows()
+        {
+            var scheduler1 = new TestScheduler();
+
+            var xs = Observable.Return(1, scheduler1);
+
+            xs.Subscribe(x => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler1.Start());
+
+            var scheduler2 = new TestScheduler();
+
+            var ys = Observable.Return(1, scheduler2);
+
+            ys.Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); });
+
+            ReactiveAssert.Throws<InvalidOperationException>(() => scheduler2.Start());
+        }
+
+        [Fact]
+        public void Return_DefaultScheduler()
+        {
+            Observable.Return(42).AssertEqual(Observable.Return(42, DefaultScheduler.Instance));
+        }
+
+    }
+}

+ 8 - 174
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/ObservableAwaiterTest.cs → Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/RunAsyncTest.cs

@@ -2,192 +2,26 @@
 // The .NET Foundation licenses this file to you under the Apache 2.0 License.
 // See the LICENSE file in the project root for more information. 
 
-
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
 using System.Reactive;
 using System.Reactive.Concurrency;
 using System.Reactive.Linq;
-using System.Reactive.Subjects;
-using System.Threading;
 using Microsoft.Reactive.Testing;
 using Xunit;
 using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Subjects;
 using System.Reactive.Disposables;
 
 namespace ReactiveTests.Tests
 {
-    
-    public class ObservableAwaiterTest : ReactiveTest
+    public class RunAsyncTest : ReactiveTest
     {
-        [Fact]
-        public void Await_ArgumentChecking()
-        {
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetAwaiter<int>(default(IObservable<int>)));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetAwaiter<int>(default(IConnectableObservable<int>)));
-
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.GetAwaiter(Observable.Empty<int>()).OnCompleted(null));
-        }
-
-        [Fact]
-        public void Await()
-        {
-            SynchronizationContext.SetSynchronizationContext(null);
-
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(20, -1),
-                OnNext(150, 0),
-                OnNext(220, 1),
-                OnNext(290, 2),
-                OnNext(340, 3),
-                OnCompleted<int>(410)
-            );
-
-            var awaiter = default(AsyncSubject<int>);
-            var result = default(int);
-            var t = long.MaxValue;
-
-            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
-            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; result = awaiter.GetResult(); }));
-
-            scheduler.Start();
-
-            Assert.Equal(410, t);
-            Assert.Equal(3, result);
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(100)
-            );
-        }
-
-        [Fact]
-        public void Await_Connectable()
-        {
-            SynchronizationContext.SetSynchronizationContext(null);
-
-            var scheduler = new TestScheduler();
-
-            var s = default(long);
-
-            var xs = Observable.Create<int>(observer =>
-            {
-                s = scheduler.Clock;
-
-                return StableCompositeDisposable.Create(
-                    scheduler.ScheduleAbsolute(250, () => { observer.OnNext(42); }),
-                    scheduler.ScheduleAbsolute(260, () => { observer.OnCompleted(); })
-                );
-            });
-
-            var ys = xs.Publish();
-
-            var awaiter = default(AsyncSubject<int>);
-            var result = default(int);
-            var t = long.MaxValue;
-
-            scheduler.ScheduleAbsolute(100, () => awaiter = ys.GetAwaiter());
-            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; result = awaiter.GetResult(); }));
-
-            scheduler.Start();
-
-            Assert.Equal(100, s);
-            Assert.Equal(260, t);
-            Assert.Equal(42, result);
-        }
-
-        [Fact]
-        public void Await_Error()
-        {
-            SynchronizationContext.SetSynchronizationContext(null);
-
-            var scheduler = new TestScheduler();
-
-            var ex = new Exception();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(20, -1),
-                OnNext(150, 0),
-                OnNext(220, 1),
-                OnNext(290, 2),
-                OnNext(340, 3),
-                OnError<int>(410, ex)
-            );
-
-            var awaiter = default(AsyncSubject<int>);
-            var t = long.MaxValue;
-
-            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
-            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; ReactiveAssert.Throws(ex, () => awaiter.GetResult()); }));
-
-            scheduler.Start();
-
-            Assert.Equal(410, t);
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(100)
-            );
-        }
-
-        [Fact]
-        public void Await_Never()
-        {
-            SynchronizationContext.SetSynchronizationContext(null);
-
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnNext(20, -1),
-                OnNext(150, 0),
-                OnNext(220, 1),
-                OnNext(290, 2),
-                OnNext(340, 3)
-            );
-
-            var awaiter = default(AsyncSubject<int>);
-            var hasValue = default(bool);
-            var t = long.MaxValue;
-
-            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
-            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; awaiter.GetResult(); hasValue = true; }));
-
-            scheduler.Start();
-
-            Assert.Equal(long.MaxValue, t);
-            Assert.False(hasValue);
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(100)
-            );
-        }
-
-        [Fact]
-        public void Await_Empty()
-        {
-            SynchronizationContext.SetSynchronizationContext(null);
-
-            var scheduler = new TestScheduler();
-
-            var xs = scheduler.CreateHotObservable(
-                OnCompleted<int>(300)
-            );
-
-            var awaiter = default(AsyncSubject<int>);
-            var t = long.MaxValue;
-
-            scheduler.ScheduleAbsolute(100, () => awaiter = xs.GetAwaiter());
-            scheduler.ScheduleAbsolute(200, () => awaiter.OnCompleted(() => { t = scheduler.Clock; ReactiveAssert.Throws<InvalidOperationException>(() => awaiter.GetResult()); }));
-
-            scheduler.Start();
-
-            Assert.Equal(300, t);
-
-            xs.Subscriptions.AssertEqual(
-                Subscribe(100)
-            );
-        }
-
         [Fact]
         public void RunAsync_ArgumentChecking()
         {
@@ -431,4 +265,4 @@ namespace ReactiveTests.Tests
             Assert.Equal(210, t);
         }
     }
-}
+}

+ 615 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SampleTest.cs

@@ -0,0 +1,615 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class SampleTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Sample_ArgumentChecking()
+        {
+            var scheduler = new TestScheduler();
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Sample(default(IObservable<int>), TimeSpan.Zero));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Sample(default(IObservable<int>), TimeSpan.Zero, scheduler));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Sample(someObservable, TimeSpan.Zero, null));
+
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Sample(someObservable, TimeSpan.FromSeconds(-1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Sample(someObservable, TimeSpan.FromSeconds(-1), scheduler));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Sample(default(IObservable<int>), someObservable));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Sample(someObservable, default(IObservable<int>)));
+        }
+
+        [Fact]
+        public void Sample_Regular()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnNext(260, 4),
+                OnNext(300, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnCompleted<int>(390)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(300, 5), /* CHECK: boundary of sampling */
+                OnNext(350, 6),
+                OnNext(400, 7), /* Sample in last bucket */
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+        }
+
+        [Fact]
+        public void Sample_Periodic_Regular()
+        {
+            var scheduler = new PeriodicTestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnNext(260, 4),
+                OnNext(300, 5),
+                OnNext(350, 6),
+                OnNext(380, 7),
+                OnCompleted<int>(390)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(300, 5), /* CHECK: boundary of sampling */
+                OnNext(350, 6),
+                OnNext(400, 7), /* Sample in last bucket */
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 390)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 400) { 250, 300, 350, 400 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void Sample_ErrorInFlight()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnNext(260, 4),
+                OnNext(300, 5),
+                OnNext(310, 6),
+                OnError<int>(330, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(300, 5), /* CHECK: boundary of sampling */
+                OnError<int>(330, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 330)
+            );
+        }
+
+        [Fact]
+        public void Sample_Periodic_ErrorInFlight()
+        {
+            var scheduler = new PeriodicTestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(230, 3),
+                OnNext(260, 4),
+                OnNext(300, 5),
+                OnNext(310, 6),
+                OnError<int>(330, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(300, 5), /* CHECK: boundary of sampling */
+                OnError<int>(330, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 330)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 330) { 250, 300 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void Sample_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void Sample_Periodic_Empty()
+        {
+            var scheduler = new PeriodicTestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 300) { 250, 300 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void Sample_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void Sample_Periodic_Error()
+        {
+            var scheduler = new PeriodicTestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 300) { 250 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void Sample_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Sample_Periodic_Never()
+        {
+            var scheduler = new PeriodicTestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+#if !WINDOWS
+            scheduler.Timers.AssertEqual(
+                new TimerRun(200, 1000) { 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950 }
+            );
+#endif
+        }
+
+        [Fact]
+        public void Sample_DefaultScheduler_Periodic()
+        {
+            var res = Observable.Return(42).Sample(TimeSpan.FromMilliseconds(1)).ToEnumerable().Single();
+            Assert.Equal(42, res);
+        }
+
+        [Fact]
+        public void Sample_DefaultScheduler_PeriodicDisabled()
+        {
+            var res = Observable.Return(42).Sample(TimeSpan.FromMilliseconds(1), Scheduler.Default.DisableOptimizations()).ToEnumerable().Single();
+            Assert.Equal(42, res);
+        }
+
+        [Fact]
+        public void Sample_Sampler_Simple1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(300, 5),
+                OnNext(310, 6),
+                OnCompleted<int>(400)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(150, ""),
+                OnNext(210, "bar"),
+                OnNext(250, "foo"),
+                OnNext(260, "qux"),
+                OnNext(320, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(320, 6),
+                OnCompleted<int>(500 /* on sampling boundaries only */)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void Sample_Sampler_Simple2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(300, 5),
+                OnNext(310, 6),
+                OnNext(360, 7),
+                OnCompleted<int>(400)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(150, ""),
+                OnNext(210, "bar"),
+                OnNext(250, "foo"),
+                OnNext(260, "qux"),
+                OnNext(320, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(320, 6),
+                OnNext(500, 7),
+                OnCompleted<int>(500 /* on sampling boundaries only */)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void Sample_Sampler_Simple3()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(150, ""),
+                OnNext(210, "bar"),
+                OnNext(250, "foo"),
+                OnNext(260, "qux"),
+                OnNext(320, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(320, 4),
+                OnCompleted<int>(320 /* on sampling boundaries only */)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 320)
+            );
+        }
+
+        [Fact]
+        public void Sample_Sampler_completes_first()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnCompleted<int>(600)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(150, ""),
+                OnNext(210, "bar"),
+                OnNext(250, "foo"),
+                OnNext(260, "qux"),
+                OnNext(320, "baz"),
+                OnCompleted<string>(500)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnNext(320, 4),
+                OnCompleted<int>(600 /* on sampling boundaries only */)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 500)
+            );
+        }
+
+        [Fact]
+        public void Sample_Sampler_SourceThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(300, 5),
+                OnNext(310, 6),
+                OnError<int>(320, ex)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(150, ""),
+                OnNext(210, "bar"),
+                OnNext(250, "foo"),
+                OnNext(260, "qux"),
+                OnNext(330, "baz"),
+                OnCompleted<string>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnError<int>(320, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 320)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 320)
+            );
+        }
+
+#if !NO_PERF // BREAKING CHANGE v2 > v1.x - behavior when sampler throws
+        [Fact]
+        public void Sample_Sampler_SamplerThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(300, 5),
+                OnNext(310, 6),
+                OnCompleted<int>(400)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(150, ""),
+                OnNext(210, "bar"),
+                OnNext(250, "foo"),
+                OnNext(260, "qux"),
+                OnError<string>(320, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Sample(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 3),
+                OnError<int>(320, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 320)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 320)
+            );
+        }
+#endif
+
+    }
+}

+ 348 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/ScanTest.cs

@@ -0,0 +1,348 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class ScanTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Scan_ArgumentChecking()
+        {
+            var someObservable = Observable.Empty<int>();
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Scan<int>(null, (_, __) => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Scan<int>(someObservable, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Scan<int, int>(null, 0, (_, __) => 0));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Scan<int, int>(someObservable, 0, null));
+        }
+
+        [Fact]
+        public void Scan_Seed_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>();
+
+            var seed = 42;
+            var res = scheduler.Start(() =>
+                xs.Scan(seed, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Scan_Seed_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var seed = 42;
+            var res = scheduler.Start(() =>
+                xs.Scan(seed, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_Seed_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            var seed = 42;
+            var res = scheduler.Start(() =>
+                xs.Scan(seed, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, seed + 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_Seed_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var seed = 42;
+            var res = scheduler.Start(() =>
+                xs.Scan(seed, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_Seed_SomeData()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var seed = 1;
+            var res = scheduler.Start(() =>
+                xs.Scan(seed, (acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, seed + 2),
+                OnNext(220, seed + 2 + 3),
+                OnNext(230, seed + 2 + 3 + 4),
+                OnNext(240, seed + 2 + 3 + 4 + 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_Seed_AccumulatorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var ex = new Exception();
+            var seed = 1;
+            var res = scheduler.Start(() =>
+                xs.Scan(seed, (acc, x) => { if (x == 4) throw ex; return acc + x; })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, seed + 2),
+                OnNext(220, seed + 2 + 3),
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Scan_NoSeed_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>();
+
+            var res = scheduler.Start(() =>
+                xs.Scan((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Scan_NoSeed_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Scan((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_NoSeed_Return()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Scan((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_NoSeed_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Scan((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_NoSeed_SomeData()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Scan((acc, x) => acc + x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 2 + 3),
+                OnNext(230, 2 + 3 + 4),
+                OnNext(240, 2 + 3 + 4 + 5),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Scan_NoSeed_AccumulatorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var ex = new Exception();
+            var res = scheduler.Start(() =>
+                xs.Scan((acc, x) => { if (x == 4) throw ex; return acc + x; })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(220, 2 + 3),
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 6842 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SelectManyTest.cs

@@ -0,0 +1,6842 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class SelectManyTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SelectMany_Then_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany(DummyObservable<string>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(((IObservable<string>)null)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyObservable<string>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectMany_Then_Complete_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnCompleted<int>(500)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnNext(550, "baz"),
+                OnNext(550, "foo"),
+                OnNext(600, "qux"),
+                OnNext(600, "bar"),
+                OnNext(650, "baz"),
+                OnNext(650, "foo"),
+                OnNext(700, "qux"),
+                OnNext(700, "bar"),
+                OnNext(750, "baz"),
+                OnNext(800, "qux"),
+                OnCompleted<string>(850)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 700)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 550),
+                Subscribe(400, 650),
+                Subscribe(500, 750),
+                Subscribe(600, 850)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Then_Complete_Complete_2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnCompleted<int>(700)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnNext(550, "baz"),
+                OnNext(550, "foo"),
+                OnNext(600, "qux"),
+                OnNext(600, "bar"),
+                OnNext(650, "baz"),
+                OnNext(650, "foo"),
+                OnNext(700, "qux"),
+                OnNext(700, "bar"),
+                OnNext(750, "baz"),
+                OnNext(800, "qux"),
+                OnCompleted<string>(900)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 550),
+                Subscribe(400, 650),
+                Subscribe(500, 750),
+                Subscribe(600, 850)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Then_Never_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnNext(500, 5),
+                OnNext(700, 0)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnNext(550, "baz"),
+                OnNext(550, "foo"),
+                OnNext(600, "qux"),
+                OnNext(600, "bar"),
+                OnNext(650, "baz"),
+                OnNext(650, "foo"),
+                OnNext(700, "qux"),
+                OnNext(700, "bar"),
+                OnNext(750, "baz"),
+                OnNext(750, "foo"),
+                OnNext(800, "qux"),
+                OnNext(800, "bar"),
+                OnNext(850, "baz"),
+                OnNext(900, "qux"),
+                OnNext(950, "foo")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 550),
+                Subscribe(400, 650),
+                Subscribe(500, 750),
+                Subscribe(600, 850),
+                Subscribe(700, 950),
+                Subscribe(900, 1000)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Then_Complete_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnCompleted<int>(500)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux")
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnNext(550, "baz"),
+                OnNext(550, "foo"),
+                OnNext(600, "qux"),
+                OnNext(600, "bar"),
+                OnNext(650, "baz"),
+                OnNext(650, "foo"),
+                OnNext(700, "qux"),
+                OnNext(700, "bar"),
+                OnNext(750, "baz"),
+                OnNext(800, "qux")
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 700)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 1000),
+                Subscribe(400, 1000),
+                Subscribe(500, 1000),
+                Subscribe(600, 1000)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Then_Complete_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnCompleted<int>(500)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux"),
+                OnError<string>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnNext(550, "baz"),
+                OnNext(550, "foo"),
+                OnError<string>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 600),
+                Subscribe(400, 600),
+                Subscribe(500, 600),
+                Subscribe(600, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Then_Error_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnError<int>(500, ex)
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux"),
+                OnCompleted<string>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnNext(550, "baz"),
+                OnNext(550, "foo"),
+                OnNext(600, "qux"),
+                OnNext(600, "bar"),
+                OnNext(650, "baz"),
+                OnNext(650, "foo"),
+                OnError<string>(700, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 700)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 550),
+                Subscribe(400, 650),
+                Subscribe(500, 700),
+                Subscribe(600, 700)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Then_Error_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateColdObservable(
+                OnNext(100, 4),
+                OnNext(200, 2),
+                OnNext(300, 3),
+                OnNext(400, 1),
+                OnError<int>(500, new Exception())
+            );
+
+            var ys = scheduler.CreateColdObservable(
+                OnNext(50, "foo"),
+                OnNext(100, "bar"),
+                OnNext(150, "baz"),
+                OnNext(200, "qux"),
+                OnError<string>(250, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, "foo"),
+                OnNext(400, "bar"),
+                OnNext(450, "baz"),
+                OnNext(450, "foo"),
+                OnNext(500, "qux"),
+                OnNext(500, "bar"),
+                OnError<string>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(300, 550),
+                Subscribe(400, 550),
+                Subscribe(500, 550)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, IObservable<int>>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectMany_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnNext(930, 401),
+                OnNext(940, 402),
+                OnCompleted<int>(960)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 960));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 950));
+        }
+
+        [Fact]
+        public void SelectMany_Complete_InnerNotComplete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnNext(930, 401),
+                OnNext(940, 402)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 1000));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 960));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 950));
+        }
+
+        [Fact]
+        public void SelectMany_Complete_OuterNotComplete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100)))
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnNext(930, 401),
+                OnNext(940, 402)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 960));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 950));
+        }
+
+        [Fact]
+        public void SelectMany_Error_Outer()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnError<ITestableObservable<int>>(900, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnError<int>(900, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 900));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 900));
+        }
+
+        [Fact]
+        public void SelectMany_Error_Inner()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnError<int>(460, ex))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnError<int>(760, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 760));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 760));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 760));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => x),
+                700
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 700));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 700));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 700));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var invoked = 0;
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+                    return x;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 550));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 550));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectMany_UseFunction()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 4),
+                OnNext(220, 3),
+                OnNext(250, 5),
+                OnNext(270, 1),
+                OnCompleted<int>(290)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => Observable.Interval(TimeSpan.FromTicks(10), scheduler).Select(_ => x).Take(x))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 4),
+                OnNext(230, 3),
+                OnNext(230, 4),
+                OnNext(240, 3),
+                OnNext(240, 4),
+                OnNext(250, 3),
+                OnNext(250, 4),
+                OnNext(260, 5),
+                OnNext(270, 5),
+                OnNext(280, 1),
+                OnNext(280, 5),
+                OnNext(290, 5),
+                OnNext(300, 5),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, int, IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, IObservable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, IObservable<int>>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Index()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 4),
+                OnNext(220, 3),
+                OnNext(250, 5),
+                OnNext(270, 1),
+                OnCompleted<int>(290)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, i) => Observable.Return(new { x, i }))
+            );
+
+            var witness = new { x = 0, i = 0 };
+
+            res.Messages.AssertEqual(
+                OnNext(210, new { x = 4, i = 0 }),
+                OnNext(220, new { x = 3, i = 1 }),
+                OnNext(250, new { x = 5, i = 2 }),
+                OnNext(270, new { x = 1, i = 3 }),
+                OnCompleted(290, witness)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnNext(930, 401),
+                OnNext(940, 402),
+                OnCompleted<int>(960)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 960));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 950));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Complete_InnerNotComplete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnNext(930, 401),
+                OnNext(940, 402)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 1000));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 960));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 950));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Complete_OuterNotComplete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100)))
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnNext(930, 401),
+                OnNext(940, 402)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 960));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 950));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Error_Outer()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnError<ITestableObservable<int>>(900, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnNext(810, 304),
+                OnNext(860, 305),
+                OnError<int>(900, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 900));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 900));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 790));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(850, 900));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Error_Inner()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnError<int>(460, ex))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => x)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303),
+                OnNext(740, 106),
+                OnError<int>(760, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 760));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 760));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 760));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(750, 760));
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => x),
+                700
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnNext(560, 301),
+                OnNext(580, 202),
+                OnNext(590, 203),
+                OnNext(600, 302),
+                OnNext(620, 303)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 700));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 700));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 605));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(550, 700));
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                    OnNext(5, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(105, scheduler.CreateColdObservable(
+                        OnError<int>(1, new InvalidOperationException()))),
+                    OnNext(300, scheduler.CreateColdObservable(
+                        OnNext(10, 102),
+                        OnNext(90, 103),
+                        OnNext(110, 104),
+                        OnNext(190, 105),
+                        OnNext(440, 106),
+                        OnCompleted<int>(460))),
+                    OnNext(400, scheduler.CreateColdObservable(
+                        OnNext(180, 202),
+                        OnNext(190, 203),
+                        OnCompleted<int>(205))),
+                    OnNext(550, scheduler.CreateColdObservable(
+                        OnNext(10, 301),
+                        OnNext(50, 302),
+                        OnNext(70, 303),
+                        OnNext(260, 304),
+                        OnNext(310, 305),
+                        OnCompleted<int>(410))),
+                    OnNext(750, scheduler.CreateColdObservable(
+                        OnCompleted<int>(40))),
+                    OnNext(850, scheduler.CreateColdObservable(
+                        OnNext(80, 401),
+                        OnNext(90, 402),
+                        OnCompleted<int>(100))),
+                    OnCompleted<ITestableObservable<int>>(900)
+            );
+
+            var invoked = 0;
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+                    return x;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 102),
+                OnNext(390, 103),
+                OnNext(410, 104),
+                OnNext(490, 105),
+                OnError<int>(550, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 550));
+
+            xs.Messages[2].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(300, 550));
+
+            xs.Messages[3].Value.Value.Subscriptions.AssertEqual(
+                Subscribe(400, 550));
+
+            xs.Messages[4].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            xs.Messages[5].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            xs.Messages[6].Value.Value.Subscriptions.AssertEqual(
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_UseFunction()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 4),
+                OnNext(220, 3),
+                OnNext(250, 5),
+                OnNext(270, 1),
+                OnCompleted<int>(290)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Observable.Interval(TimeSpan.FromTicks(10), scheduler).Select(__ => x).Take(x))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 4),
+                OnNext(230, 3),
+                OnNext(230, 4),
+                OnNext(240, 3),
+                OnNext(240, 4),
+                OnNext(250, 3),
+                OnNext(250, 4),
+                OnNext(260, 5),
+                OnNext(270, 5),
+                OnNext(280, 1),
+                OnNext(280, 5),
+                OnNext(290, 5),
+                OnNext(300, 5),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, IEnumerable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, IEnumerable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, IEnumerable<int>>.Instance).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, IEnumerable<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, IEnumerable<int>>)null, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, IEnumerable<int>>.Instance, (Func<int, int, int>)null));
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var inners = new List<MockEnumerable<int>>();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x =>
+                {
+                    var ys = new MockEnumerable<int>(scheduler, Enumerable.Repeat(x, x));
+                    inners.Add(ys);
+                    return ys;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            Assert.Equal(4, inners.Count);
+
+            inners[0].Subscriptions.AssertEqual(
+                Subscribe(210, 210)
+            );
+
+            inners[1].Subscriptions.AssertEqual(
+                Subscribe(340, 340)
+            );
+
+            inners[2].Subscriptions.AssertEqual(
+                Subscribe(420, 420)
+            );
+
+            inners[3].Subscriptions.AssertEqual(
+                Subscribe(510, 510)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_Complete_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => Enumerable.Repeat(x, x), (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(510, 4),
+                OnNext(510, 4),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => Enumerable.Repeat(x, x))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnNext(510, 2),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_Error_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => Enumerable.Repeat(x, x), (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(510, 4),
+                OnNext(510, 4),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => Enumerable.Repeat(x, x)),
+                350
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_Dispose_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => Enumerable.Repeat(x, x), (x, y) => x + y),
+                350
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+
+                    return Enumerable.Repeat(x, x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_ResultSelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var inners = new List<MockEnumerable<int>>();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x =>
+                    {
+                        var ys = new MockEnumerable<int>(scheduler, Enumerable.Repeat(x, x));
+                        inners.Add(ys);
+                        return ys;
+                    },
+                    (x, y) =>
+                    {
+                        if (x == 3)
+                            throw ex;
+
+                        return x + y;
+                    }
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(3, inners.Count);
+
+            inners[0].Subscriptions.AssertEqual(
+                Subscribe(210, 210)
+            );
+
+            inners[1].Subscriptions.AssertEqual(
+                Subscribe(340, 340)
+            );
+
+            inners[2].Subscriptions.AssertEqual(
+                Subscribe(420, 420)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_ResultSelector_GetEnumeratorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => new RogueEnumerable<int>(ex), (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_SelectorThrows_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x =>
+                    {
+                        invoked++;
+                        if (invoked == 3)
+                            throw ex;
+
+                        return Enumerable.Repeat(x, x);
+                    },
+                    (x, y) => x + y
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        class CurrentThrowsEnumerable<T> : IEnumerable<T>
+        {
+            IEnumerable<T> e;
+            Exception ex;
+
+            public CurrentThrowsEnumerable(IEnumerable<T> e, Exception ex)
+            {
+                this.e = e;
+                this.ex = ex;
+            }
+
+            public IEnumerator<T> GetEnumerator()
+            {
+                return new Enumerator(e.GetEnumerator(), ex);
+            }
+
+            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+
+            class Enumerator : IEnumerator<T>
+            {
+                IEnumerator<T> e;
+                Exception ex;
+
+                public Enumerator(IEnumerator<T> e, Exception ex)
+                {
+                    this.e = e;
+                    this.ex = ex;
+                }
+
+                public T Current
+                {
+                    get { throw ex; }
+                }
+
+                public void Dispose()
+                {
+                    e.Dispose();
+                }
+
+                object System.Collections.IEnumerator.Current
+                {
+                    get { return Current; }
+                }
+
+                public bool MoveNext()
+                {
+                    return e.MoveNext();
+                }
+
+                public void Reset()
+                {
+                    e.Reset();
+                }
+            }
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_CurrentThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => new CurrentThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_CurrentThrows_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => new CurrentThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex), (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        class MoveNextThrowsEnumerable<T> : IEnumerable<T>
+        {
+            IEnumerable<T> e;
+            Exception ex;
+
+            public MoveNextThrowsEnumerable(IEnumerable<T> e, Exception ex)
+            {
+                this.e = e;
+                this.ex = ex;
+            }
+
+            public IEnumerator<T> GetEnumerator()
+            {
+                return new Enumerator(e.GetEnumerator(), ex);
+            }
+
+            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+
+            class Enumerator : IEnumerator<T>
+            {
+                IEnumerator<T> e;
+                Exception ex;
+
+                public Enumerator(IEnumerator<T> e, Exception ex)
+                {
+                    this.e = e;
+                    this.ex = ex;
+                }
+
+                public T Current
+                {
+                    get { return e.Current; }
+                }
+
+                public void Dispose()
+                {
+                    e.Dispose();
+                }
+
+                object System.Collections.IEnumerator.Current
+                {
+                    get { return Current; }
+                }
+
+                public bool MoveNext()
+                {
+                    throw ex;
+                }
+
+                public void Reset()
+                {
+                    e.Reset();
+                }
+            }
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_GetEnumeratorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => new RogueEnumerable<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_MoveNextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => new MoveNextThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Enumerable_MoveNextThrows_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(x => new MoveNextThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex), (x, y) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, int, IEnumerable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, IEnumerable<int>>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, IEnumerable<int>>.Instance).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, int, IEnumerable<int>>.Instance, DummyFunc<int, int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, IEnumerable<int>>)null, DummyFunc<int, int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, IEnumerable<int>>.Instance, (Func<int, int, int, int, int>)null));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Index()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 4),
+                OnNext(220, 3),
+                OnNext(250, 5),
+                OnNext(270, 1),
+                OnCompleted<int>(290)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, i) => new[] { new { x, i } })
+            );
+
+            var witness = new { x = 0, i = 0 };
+
+            res.Messages.AssertEqual(
+                OnNext(210, new { x = 4, i = 0 }),
+                OnNext(220, new { x = 3, i = 1 }),
+                OnNext(250, new { x = 5, i = 2 }),
+                OnNext(270, new { x = 1, i = 3 }),
+                OnCompleted(290, witness)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_ResultSelector_Index()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 4),
+                OnNext(220, 3),
+                OnNext(250, 5),
+                OnNext(270, 1),
+                OnCompleted<int>(290)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, i) => Enumerable.Range(10, i + 1), (x, i, y, j) => new { x, i, y, j })
+            );
+
+            var witness = new { x = 0, i = 0, y = 0, j = 0 };
+
+            res.Messages.AssertEqual(
+                OnNext(210, new { x = 4, i = 0, y = 10, j = 0 }),
+                OnNext(220, new { x = 3, i = 1, y = 10, j = 0 }),
+                OnNext(220, new { x = 3, i = 1, y = 11, j = 1 }),
+                OnNext(250, new { x = 5, i = 2, y = 10, j = 0 }),
+                OnNext(250, new { x = 5, i = 2, y = 11, j = 1 }),
+                OnNext(250, new { x = 5, i = 2, y = 12, j = 2 }),
+                OnNext(270, new { x = 1, i = 3, y = 10, j = 0 }),
+                OnNext(270, new { x = 1, i = 3, y = 11, j = 1 }),
+                OnNext(270, new { x = 1, i = 3, y = 12, j = 2 }),
+                OnNext(270, new { x = 1, i = 3, y = 13, j = 3 }),
+                OnCompleted(290, witness)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Complete()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var inners = new List<MockEnumerable<int>>();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) =>
+                {
+                    var ys = new MockEnumerable<int>(scheduler, Enumerable.Repeat(x, x));
+                    inners.Add(ys);
+                    return ys;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            Assert.Equal(4, inners.Count);
+
+            inners[0].Subscriptions.AssertEqual(
+                Subscribe(210, 210)
+            );
+
+            inners[1].Subscriptions.AssertEqual(
+                Subscribe(340, 340)
+            );
+
+            inners[2].Subscriptions.AssertEqual(
+                Subscribe(420, 420)
+            );
+
+            inners[3].Subscriptions.AssertEqual(
+                Subscribe(510, 510)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Complete_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Enumerable.Repeat(x, x), (x, _, y, __) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(510, 4),
+                OnNext(510, 4),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Enumerable.Repeat(x, x))
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnNext(510, 2),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Error_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnError<int>(600, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Enumerable.Repeat(x, x), (x, _, y, __) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(420, 6),
+                OnNext(510, 4),
+                OnNext(510, 4),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Enumerable.Repeat(x, x)),
+                350
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_Dispose_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Enumerable.Repeat(x, x), (x, _, y, __) => x + y),
+                350
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+
+                    return Enumerable.Repeat(x, x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnNext(340, 4),
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_ResultSelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var inners = new List<MockEnumerable<int>>();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) =>
+                    {
+                        var ys = new MockEnumerable<int>(scheduler, Enumerable.Repeat(x, x));
+                        inners.Add(ys);
+                        return ys;
+                    },
+                    (x, _, y, __) =>
+                    {
+                        if (x == 3)
+                            throw ex;
+
+                        return x + y;
+                    }
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(3, inners.Count);
+
+            inners[0].Subscriptions.AssertEqual(
+                Subscribe(210, 210)
+            );
+
+            inners[1].Subscriptions.AssertEqual(
+                Subscribe(340, 340)
+            );
+
+            inners[2].Subscriptions.AssertEqual(
+                Subscribe(420, 420)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_ResultSelector_GetEnumeratorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => new RogueEnumerable<int>(ex), (x, _, y, __) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_SelectorThrows_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) =>
+                    {
+                        invoked++;
+                        if (invoked == 3)
+                            throw ex;
+
+                        return Enumerable.Repeat(x, x);
+                    },
+                    (x, _, y, __) => x + y
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(210, 4),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnNext(340, 8),
+                OnError<int>(420, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_CurrentThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => new CurrentThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_CurrentThrows_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => new CurrentThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex), (x, _, y, __) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_GetEnumeratorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => new RogueEnumerable<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_MoveNextThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => new MoveNextThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Enumerable_MoveNextThrows_ResultSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 2),
+                OnNext(340, 4),
+                OnNext(420, 3),
+                OnNext(510, 2),
+                OnCompleted<int>(600)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => new MoveNextThrowsEnumerable<int>(Enumerable.Repeat(x, x), ex), (x, _, y, __) => x + y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, IObservable<int>>)null, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, IObservable<int>>.Instance, ((Func<int, int, int>)null)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, IObservable<int>>.Instance, DummyFunc<int, int, int>.Instance).Subscribe(null));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, Task<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, Task<int>>)null, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, Task<int>>.Instance, ((Func<int, int, int>)null)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, CancellationToken, Task<int>>.Instance, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, CancellationToken, Task<int>>)null, DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, CancellationToken, Task<int>>.Instance, ((Func<int, int, int>)null)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, Task<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, Task<int>>)null));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, CancellationToken, Task<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, CancellationToken, Task<int>>)null));
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_CompleteOuterFirst()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x)
+                select x * 10 + (int)y
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnNext(223, 20),
+                OnNext(223, 31),
+                OnNext(223, 42),
+                OnNext(224, 50),
+                OnNext(224, 21),
+                OnNext(224, 32),
+                OnNext(224, 43),
+                OnNext(225, 51),
+                OnNext(226, 52),
+                OnNext(227, 53),
+                OnNext(228, 54),
+                OnCompleted<int>(228)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 224)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_CompleteInnerFirst()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x)
+                select x * 10 + (int)y
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnNext(223, 20),
+                OnNext(223, 31),
+                OnNext(223, 42),
+                OnNext(224, 50),
+                OnNext(224, 21),
+                OnNext(224, 32),
+                OnNext(224, 43),
+                OnNext(225, 51),
+                OnNext(226, 52),
+                OnNext(227, 53),
+                OnNext(228, 54),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_ErrorOuter()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnError<int>(224, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x)
+                select x * 10 + (int)y
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnNext(223, 20),
+                OnNext(223, 31),
+                OnNext(223, 42),
+                OnError<int>(224, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 224)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_ErrorInner()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in x == 2 ? Observable.Throw<long>(ex, scheduler)
+                                 : Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x)
+                select x * 10 + (int)y
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnError<int>(223, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 223)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x)
+                select x * 10 + (int)y,
+                223
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 223)
+            );
+        }
+
+        static T Throw<T>(Exception ex)
+        {
+            throw ex;
+        }
+
+
+        [Fact]
+        public void SelectMany_QueryOperator_ThrowSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in Throw<IObservable<long>>(ex)
+                select x * 10 + (int)y
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_QueryOperator_ThrowResult()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                from x in xs
+                from y in Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x)
+                select Throw<int>(ex)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(221, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 221)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, int, IObservable<int>>.Instance, DummyFunc<int, int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, IObservable<int>>)null, DummyFunc<int, int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, IObservable<int>>.Instance, ((Func<int, int, int, int, int>)null)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, IObservable<int>>.Instance, DummyFunc<int, int, int, int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_Index()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(210, 4),
+                OnNext(220, 3),
+                OnNext(250, 5),
+                OnNext(270, 1),
+                OnCompleted<int>(290)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, i) => Observable.Range(10, i + 1), (x, i, y, j) => new { x, i, y, j })
+            );
+
+            var witness = new { x = 0, i = 0, y = 0, j = 0 };
+
+            res.Messages.AssertEqual(
+                OnNext(210, new { x = 4, i = 0, y = 10, j = 0 }),
+                OnNext(220, new { x = 3, i = 1, y = 10, j = 0 }),
+                OnNext(220, new { x = 3, i = 1, y = 11, j = 1 }),
+                OnNext(250, new { x = 5, i = 2, y = 10, j = 0 }),
+                OnNext(250, new { x = 5, i = 2, y = 11, j = 1 }),
+                OnNext(250, new { x = 5, i = 2, y = 12, j = 2 }),
+                OnNext(270, new { x = 1, i = 3, y = 10, j = 0 }),
+                OnNext(270, new { x = 1, i = 3, y = 11, j = 1 }),
+                OnNext(270, new { x = 1, i = 3, y = 12, j = 2 }),
+                OnNext(270, new { x = 1, i = 3, y = 13, j = 3 }),
+                OnCompleted(290, witness)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_CompleteOuterFirst()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x), (x, _, y, __) => x * 10 + (int)y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnNext(223, 20),
+                OnNext(223, 31),
+                OnNext(223, 42),
+                OnNext(224, 50),
+                OnNext(224, 21),
+                OnNext(224, 32),
+                OnNext(224, 43),
+                OnNext(225, 51),
+                OnNext(226, 52),
+                OnNext(227, 53),
+                OnNext(228, 54),
+                OnCompleted<int>(228)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 224)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_CompleteInnerFirst()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x), (x, _, y, __) => x * 10 + (int)y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnNext(223, 20),
+                OnNext(223, 31),
+                OnNext(223, 42),
+                OnNext(224, 50),
+                OnNext(224, 21),
+                OnNext(224, 32),
+                OnNext(224, 43),
+                OnNext(225, 51),
+                OnNext(226, 52),
+                OnNext(227, 53),
+                OnNext(228, 54),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_ErrorOuter()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnError<int>(224, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x), (x, _, y, __) => x * 10 + (int)y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnNext(223, 20),
+                OnNext(223, 31),
+                OnNext(223, 42),
+                OnError<int>(224, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 224)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_ErrorInner()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => x == 2
+                            ? Observable.Throw<long>(ex, scheduler)
+                            : Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x),
+                    (x, _, y, __) => x * 10 + (int)y)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41),
+                OnError<int>(223, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 223)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x), (x, _, y, __) => x * 10 + (int)y),
+                223
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(221, 40),
+                OnNext(222, 30),
+                OnNext(222, 41)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 223)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_ThrowSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Throw<IObservable<long>>(ex), (x, _, y, __) => x * 10 + (int)y)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_QueryOperator_ThrowResult()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(220, 4),
+                OnNext(221, 3),
+                OnNext(222, 2),
+                OnNext(223, 5),
+                OnCompleted<int>(224)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany((x, _) => Observable.Interval(TimeSpan.FromTicks(1), scheduler).Take(x), (x, _, y, __) => Throw<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(221, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 221)
+            );
+        }
+
+
+        [Fact]
+        public void SelectMany_Triple_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(null, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<Exception, IObservable<int>>.Instance, DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, (Func<int, IObservable<int>>)null, DummyFunc<Exception, IObservable<int>>.Instance, DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, null, DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<Exception, IObservable<int>>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, DummyFunc<int, IObservable<int>>.Instance, DummyFunc<Exception, IObservable<int>>.Instance, DummyFunc<IObservable<int>>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectMany_Triple_Identity()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Return(x, scheduler),
+                    ex => Observable.Throw<int>(ex, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnCompleted<int>(306)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_InnersWithTiming1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ysn = scheduler.CreateColdObservable(
+                OnNext(10, 10),
+                OnNext(20, 11),
+                OnNext(30, 12),
+                OnCompleted<int>(40)
+            );
+
+            var yse = scheduler.CreateColdObservable(
+                OnNext(0, 99),
+                OnCompleted<int>(10)
+            );
+
+            var ysc = scheduler.CreateColdObservable(
+                OnNext(10, 42),
+                OnCompleted<int>(20)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => ysn,
+                    ex => yse,
+                    () => ysc
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 10),
+                OnNext(311, 10),
+                OnNext(312, 10),
+                OnNext(313, 10),
+                OnNext(314, 10),
+                OnNext(315, 42),
+                OnNext(320, 11),
+                OnNext(321, 11),
+                OnNext(322, 11),
+                OnNext(323, 11),
+                OnNext(324, 11),
+                OnNext(330, 12),
+                OnNext(331, 12),
+                OnNext(332, 12),
+                OnNext(333, 12),
+                OnNext(334, 12),
+                OnCompleted<int>(344)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+
+            ysn.Subscriptions.AssertEqual(
+                Subscribe(300, 340),
+                Subscribe(301, 341),
+                Subscribe(302, 342),
+                Subscribe(303, 343),
+                Subscribe(304, 344)
+            );
+
+            yse.Subscriptions.AssertEqual(
+            );
+
+            ysc.Subscriptions.AssertEqual(
+                Subscribe(305, 325)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_InnersWithTiming2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ysn = scheduler.CreateColdObservable(
+                OnNext(10, 10),
+                OnNext(20, 11),
+                OnNext(30, 12),
+                OnCompleted<int>(40)
+            );
+
+            var yse = scheduler.CreateColdObservable(
+                OnNext(0, 99),
+                OnCompleted<int>(10)
+            );
+
+            var ysc = scheduler.CreateColdObservable(
+                OnNext(10, 42),
+                OnCompleted<int>(50)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => ysn,
+                    ex => yse,
+                    () => ysc
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 10),
+                OnNext(311, 10),
+                OnNext(312, 10),
+                OnNext(313, 10),
+                OnNext(314, 10),
+                OnNext(315, 42),
+                OnNext(320, 11),
+                OnNext(321, 11),
+                OnNext(322, 11),
+                OnNext(323, 11),
+                OnNext(324, 11),
+                OnNext(330, 12),
+                OnNext(331, 12),
+                OnNext(332, 12),
+                OnNext(333, 12),
+                OnNext(334, 12),
+                OnCompleted<int>(355)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+
+            ysn.Subscriptions.AssertEqual(
+                Subscribe(300, 340),
+                Subscribe(301, 341),
+                Subscribe(302, 342),
+                Subscribe(303, 343),
+                Subscribe(304, 344)
+            );
+
+            yse.Subscriptions.AssertEqual(
+            );
+
+            ysc.Subscriptions.AssertEqual(
+                Subscribe(305, 355)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_InnersWithTiming3()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(400, 1),
+                OnNext(500, 2),
+                OnNext(600, 3),
+                OnNext(700, 4),
+                OnCompleted<int>(800)
+            );
+
+            var ysn = scheduler.CreateColdObservable(
+                OnNext(10, 10),
+                OnNext(20, 11),
+                OnNext(30, 12),
+                OnCompleted<int>(40)
+            );
+
+            var yse = scheduler.CreateColdObservable(
+                OnNext(0, 99),
+                OnCompleted<int>(10)
+            );
+
+            var ysc = scheduler.CreateColdObservable(
+                OnNext(10, 42),
+                OnCompleted<int>(100)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => ysn,
+                    ex => yse,
+                    () => ysc
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 10),
+                OnNext(320, 11),
+                OnNext(330, 12),
+                OnNext(410, 10),
+                OnNext(420, 11),
+                OnNext(430, 12),
+                OnNext(510, 10),
+                OnNext(520, 11),
+                OnNext(530, 12),
+                OnNext(610, 10),
+                OnNext(620, 11),
+                OnNext(630, 12),
+                OnNext(710, 10),
+                OnNext(720, 11),
+                OnNext(730, 12),
+                OnNext(810, 42),
+                OnCompleted<int>(900)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 800)
+            );
+
+            ysn.Subscriptions.AssertEqual(
+                Subscribe(300, 340),
+                Subscribe(400, 440),
+                Subscribe(500, 540),
+                Subscribe(600, 640),
+                Subscribe(700, 740)
+            );
+
+            yse.Subscriptions.AssertEqual(
+            );
+
+            ysc.Subscriptions.AssertEqual(
+                Subscribe(800, 900)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_Error_Identity()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Return(x, scheduler),
+                    ex1 => Observable.Throw<int>(ex1, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnError<int>(306, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_SelectMany()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Throw<int>(ex, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, 4),
+                OnNext(306, 3),
+                OnNext(307, 4),
+                OnNext(308, 4),
+                OnCompleted<int>(308)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+
+        [Fact]
+        public void SelectMany_Triple_Concat()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Return(x, scheduler),
+                    ex => Observable.Throw<int>(ex, scheduler),
+                    () => Observable.Range(1, 3, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnNext(306, 1),
+                OnNext(307, 2),
+                OnNext(308, 3),
+                OnCompleted<int>(309)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_Catch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Return(x, scheduler),
+                    ex => Observable.Range(1, 3, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnCompleted<int>(306)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_Error_Catch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Return(x, scheduler),
+                    ex => Observable.Range(1, 3, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnNext(306, 1),
+                OnNext(307, 2),
+                OnNext(308, 3),
+                OnCompleted<int>(309)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, -1),
+                OnNext(306, 4),
+                OnNext(306, 3),
+                OnNext(307, -1),
+                OnNext(307, 4),
+                OnNext(308, 4),
+                OnCompleted<int>(308)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_Error_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, 0),
+                OnNext(306, 4),
+                OnNext(306, 3),
+                OnNext(307, 0),
+                OnNext(307, 4),
+                OnNext(308, 4),
+                OnCompleted<int>(308)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_All_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                ),
+                307
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, -1),
+                OnNext(306, 4),
+                OnNext(306, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_All_Dispose_Before_First()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                ),
+                304
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 304)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_OnNextThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Throw<IObservable<int>>(ex),
+                    ex1 => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_OnErrorThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex1 => Throw<IObservable<int>>(ex),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnError<int>(305, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Triple_OnCompletedThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    x => Observable.Repeat(x, x, scheduler),
+                    ex1 => Observable.Repeat(0, 2, scheduler),
+                    () => Throw<IObservable<int>>(ex)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnError<int>(305, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(null, DummyFunc<int, int, IObservable<int>>.Instance, DummyFunc<Exception, IObservable<int>>.Instance, DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, (Func<int, int, IObservable<int>>)null, DummyFunc<Exception, IObservable<int>>.Instance, DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, DummyFunc<int, int, IObservable<int>>.Instance, null, DummyFunc<IObservable<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, DummyFunc<int, int, IObservable<int>>.Instance, DummyFunc<Exception, IObservable<int>>.Instance, null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, DummyFunc<int, int, IObservable<int>>.Instance, DummyFunc<Exception, IObservable<int>>.Instance, DummyFunc<IObservable<int>>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Index()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var witness = new { x = 0, i = 0 };
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, i) => Observable.Return(new { x, i }, scheduler),
+                    ex => Observable.Throw(ex, scheduler, witness),
+                    () => Observable.Empty(scheduler, witness)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, new { x = 0, i = 0 }),
+                OnNext(302, new { x = 1, i = 1 }),
+                OnNext(303, new { x = 2, i = 2 }),
+                OnNext(304, new { x = 3, i = 3 }),
+                OnNext(305, new { x = 4, i = 4 }),
+                OnCompleted(306, witness)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Identity()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Return(x, scheduler),
+                    ex => Observable.Throw<int>(ex, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnCompleted<int>(306)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_InnersWithTiming1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ysn = scheduler.CreateColdObservable(
+                OnNext(10, 10),
+                OnNext(20, 11),
+                OnNext(30, 12),
+                OnCompleted<int>(40)
+            );
+
+            var yse = scheduler.CreateColdObservable(
+                OnNext(0, 99),
+                OnCompleted<int>(10)
+            );
+
+            var ysc = scheduler.CreateColdObservable(
+                OnNext(10, 42),
+                OnCompleted<int>(20)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => ysn,
+                    ex => yse,
+                    () => ysc
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 10),
+                OnNext(311, 10),
+                OnNext(312, 10),
+                OnNext(313, 10),
+                OnNext(314, 10),
+                OnNext(315, 42),
+                OnNext(320, 11),
+                OnNext(321, 11),
+                OnNext(322, 11),
+                OnNext(323, 11),
+                OnNext(324, 11),
+                OnNext(330, 12),
+                OnNext(331, 12),
+                OnNext(332, 12),
+                OnNext(333, 12),
+                OnNext(334, 12),
+                OnCompleted<int>(344)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+
+            ysn.Subscriptions.AssertEqual(
+                Subscribe(300, 340),
+                Subscribe(301, 341),
+                Subscribe(302, 342),
+                Subscribe(303, 343),
+                Subscribe(304, 344)
+            );
+
+            yse.Subscriptions.AssertEqual(
+            );
+
+            ysc.Subscriptions.AssertEqual(
+                Subscribe(305, 325)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_InnersWithTiming2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ysn = scheduler.CreateColdObservable(
+                OnNext(10, 10),
+                OnNext(20, 11),
+                OnNext(30, 12),
+                OnCompleted<int>(40)
+            );
+
+            var yse = scheduler.CreateColdObservable(
+                OnNext(0, 99),
+                OnCompleted<int>(10)
+            );
+
+            var ysc = scheduler.CreateColdObservable(
+                OnNext(10, 42),
+                OnCompleted<int>(50)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => ysn,
+                    ex => yse,
+                    () => ysc
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 10),
+                OnNext(311, 10),
+                OnNext(312, 10),
+                OnNext(313, 10),
+                OnNext(314, 10),
+                OnNext(315, 42),
+                OnNext(320, 11),
+                OnNext(321, 11),
+                OnNext(322, 11),
+                OnNext(323, 11),
+                OnNext(324, 11),
+                OnNext(330, 12),
+                OnNext(331, 12),
+                OnNext(332, 12),
+                OnNext(333, 12),
+                OnNext(334, 12),
+                OnCompleted<int>(355)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+
+            ysn.Subscriptions.AssertEqual(
+                Subscribe(300, 340),
+                Subscribe(301, 341),
+                Subscribe(302, 342),
+                Subscribe(303, 343),
+                Subscribe(304, 344)
+            );
+
+            yse.Subscriptions.AssertEqual(
+            );
+
+            ysc.Subscriptions.AssertEqual(
+                Subscribe(305, 355)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_InnersWithTiming3()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(400, 1),
+                OnNext(500, 2),
+                OnNext(600, 3),
+                OnNext(700, 4),
+                OnCompleted<int>(800)
+            );
+
+            var ysn = scheduler.CreateColdObservable(
+                OnNext(10, 10),
+                OnNext(20, 11),
+                OnNext(30, 12),
+                OnCompleted<int>(40)
+            );
+
+            var yse = scheduler.CreateColdObservable(
+                OnNext(0, 99),
+                OnCompleted<int>(10)
+            );
+
+            var ysc = scheduler.CreateColdObservable(
+                OnNext(10, 42),
+                OnCompleted<int>(100)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => ysn,
+                    ex => yse,
+                    () => ysc
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 10),
+                OnNext(320, 11),
+                OnNext(330, 12),
+                OnNext(410, 10),
+                OnNext(420, 11),
+                OnNext(430, 12),
+                OnNext(510, 10),
+                OnNext(520, 11),
+                OnNext(530, 12),
+                OnNext(610, 10),
+                OnNext(620, 11),
+                OnNext(630, 12),
+                OnNext(710, 10),
+                OnNext(720, 11),
+                OnNext(730, 12),
+                OnNext(810, 42),
+                OnCompleted<int>(900)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 800)
+            );
+
+            ysn.Subscriptions.AssertEqual(
+                Subscribe(300, 340),
+                Subscribe(400, 440),
+                Subscribe(500, 540),
+                Subscribe(600, 640),
+                Subscribe(700, 740)
+            );
+
+            yse.Subscriptions.AssertEqual(
+            );
+
+            ysc.Subscriptions.AssertEqual(
+                Subscribe(800, 900)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Error_Identity()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Return(x, scheduler),
+                    ex1 => Observable.Throw<int>(ex1, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnError<int>(306, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_SelectMany()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Throw<int>(ex, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, 4),
+                OnNext(306, 3),
+                OnNext(307, 4),
+                OnNext(308, 4),
+                OnCompleted<int>(308)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Concat()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Return(x, scheduler),
+                    ex => Observable.Throw<int>(ex, scheduler),
+                    () => Observable.Range(1, 3, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnNext(306, 1),
+                OnNext(307, 2),
+                OnNext(308, 3),
+                OnCompleted<int>(309)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Catch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Return(x, scheduler),
+                    ex => Observable.Range(1, 3, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnCompleted<int>(306)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Error_Catch()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Return(x, scheduler),
+                    ex => Observable.Range(1, 3, scheduler),
+                    () => Observable.Empty<int>(scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(301, 0),
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(305, 4),
+                OnNext(306, 1),
+                OnNext(307, 2),
+                OnNext(308, 3),
+                OnCompleted<int>(309)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, -1),
+                OnNext(306, 4),
+                OnNext(306, 3),
+                OnNext(307, -1),
+                OnNext(307, 4),
+                OnNext(308, 4),
+                OnCompleted<int>(308)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_Error_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, 0),
+                OnNext(306, 4),
+                OnNext(306, 3),
+                OnNext(307, 0),
+                OnNext(307, 4),
+                OnNext(308, 4),
+                OnCompleted<int>(308)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_All_Dispose()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                ),
+                307
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnNext(305, 4),
+                OnNext(305, 3),
+                OnNext(306, -1),
+                OnNext(306, 4),
+                OnNext(306, 3)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_All_Dispose_Before_First()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                ),
+                304
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 304)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_OnNextThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Throw<IObservable<int>>(ex),
+                    ex1 => Observable.Repeat(0, 2, scheduler),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_OnErrorThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnError<int>(305, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex1 => Throw<IObservable<int>>(ex),
+                    () => Observable.Repeat(-1, 2, scheduler)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnError<int>(305, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Triple_OnCompletedThrow()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(300, 0),
+                OnNext(301, 1),
+                OnNext(302, 2),
+                OnNext(303, 3),
+                OnNext(304, 4),
+                OnCompleted<int>(305)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SelectMany(
+                    (x, _) => Observable.Repeat(x, x, scheduler),
+                    ex1 => Observable.Repeat(0, 2, scheduler),
+                    () => Throw<IObservable<int>>(ex)
+                )
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(302, 1),
+                OnNext(303, 2),
+                OnNext(304, 3),
+                OnNext(304, 2),
+                OnError<int>(305, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 305)
+            );
+        }
+
+        [Fact]
+        public void SelectMany_Task_ArgumentChecking()
+        {
+            var t = new Task<int>(() => 42);
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(default(IObservable<int>), x => t));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, default(Func<int, Task<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(default(IObservable<int>), (int x, CancellationToken ct) => t));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, default(Func<int, CancellationToken, Task<int>>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(default(IObservable<int>), x => t, (x, y) => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, default(Func<int, Task<int>>), (x, y) => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, x => t, default(Func<int, int, int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(default(IObservable<int>), (x, ct) => t, (x, y) => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, default(Func<int, CancellationToken, Task<int>>), (x, y) => x));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SelectMany(DummyObservable<int>.Instance, (x, ct) => t, default(Func<int, int, int>)));
+        }
+
+        [Fact]
+        public void SelectMany_Task1()
+        {
+            var res = Observable.Range(0, 10).SelectMany(x => Task.Factory.StartNew(() => x + 1)).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectMany_Task2()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, ct) => Task.Factory.StartNew(() => x + 1, ct)).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectMany_Task_TaskThrows()
+        {
+            var ex = new Exception();
+
+            var res = Observable.Range(0, 10).SelectMany(x => Task.Factory.StartNew(() =>
+            {
+                if (x > 5)
+                    throw ex;
+                return x + 1;
+            })).ToEnumerable();
+
+            ReactiveAssert.Throws(ex, () =>
+            {
+                foreach (var x in res)
+                    ;
+            });
+        }
+
+        [Fact]
+        public void SelectMany_Task_SelectorThrows()
+        {
+            var ex = new Exception();
+
+            var res = Observable.Range(0, 10).SelectMany(x =>
+            {
+                if (x > 5)
+                    throw ex;
+                return Task.Factory.StartNew(() => x + 1);
+            }).ToEnumerable();
+
+            ReactiveAssert.Throws(ex, () =>
+            {
+                foreach (var x in res)
+                    ;
+            });
+        }
+
+        [Fact]
+        public void SelectMany_Task_ResultSelector1()
+        {
+            var res = Observable.Range(0, 10).SelectMany(x => Task.Factory.StartNew(() => x + 1), (x, y) => x + y).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { 2 * x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectMany_Task_ResultSelector2()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, ct) => Task.Factory.StartNew(() => x + 1, ct), (x, y) => x + y).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { 2 * x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectMany_Task_ResultSelectorThrows()
+        {
+            var ex = new Exception();
+
+            var res = Observable.Range(0, 10).SelectMany(x => Task.Factory.StartNew(() => x + 1), (x, y) =>
+            {
+                if (x > 5)
+                    throw ex;
+                return x + y;
+            }).ToEnumerable();
+
+            ReactiveAssert.Throws(ex, () =>
+            {
+                foreach (var x in res)
+                    ;
+            });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_RanToCompletion_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_RanToCompletion_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Faulted_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Faulted_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Canceled_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            tcss[1].SetCanceled();
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Canceled_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            tcss[1].SetCanceled();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_InnerCompleteBeforeOuter()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            xs.OnCompleted();
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_OuterCompleteBeforeInner()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, x => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Cancellation_NeverInvoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => tcs.SetCanceled());
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            var d = res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Cancellation_Invoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = false;
+            var d = res.Subscribe(lst.Add, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            d.Dispose();
+
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // never observed because xs.OnNext(2) happened after dispose
+
+            lst.AssertEqual(new[] { 42 });
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Cancellation_AfterOuterError()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = false;
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => err = ex_, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            var ex = new Exception();
+            xs.OnError(ex);
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // no-op
+
+            lst.AssertEqual(new[] { 42 });
+            Assert.Same(ex, err);
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_Simple_Cancellation_AfterSelectorThrows()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[4];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+            tcss[3] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var ex = new Exception();
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                if (x == 2)
+                    throw ex;
+
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = false;
+            var evt = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; evt.Set(); }, () => { done = true; evt.Set(); });
+
+            tcss[1].SetResult(43);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            tcss[0].SetResult(42);
+
+            xs.OnNext(2); // causes error
+            xs.OnCompleted();
+
+            evt.WaitOne();
+
+            Assert.False(done);
+            Assert.Same(ex, err);
+            Assert.Equal(2, n);
+            Assert.Equal(0, m);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_RanToCompletion_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 0, 43 + 1 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_RanToCompletion_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 0, 43 + 1 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Faulted_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Faulted_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Canceled_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            tcss[1].SetCanceled();
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Canceled_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            tcss[1].SetCanceled();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_InnerCompleteBeforeOuter()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            xs.OnCompleted();
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 1, 43 + 0, 44 + 2 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_OuterCompleteBeforeInner()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, x => tcss[x].Task, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 1, 43 + 0, 44 + 2 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Cancellation_NeverInvoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => tcs.SetCanceled());
+
+                return tcs.Task;
+            }, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            var d = res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 1, 43 + 0, 44 + 2 });
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Cancellation_Invoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            }, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = false;
+            var d = res.Subscribe(lst.Add, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            d.Dispose();
+
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // never observed because xs.OnNext(2) happened after dispose
+
+            lst.AssertEqual(new[] { 42 + 1 });
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Cancellation_AfterOuterError()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            }, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = false;
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => err = ex_, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            var ex = new Exception();
+            xs.OnError(ex);
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // no-op
+
+            lst.AssertEqual(new[] { 42 + 1 });
+            Assert.Same(ex, err);
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectMany_TaskWithCompletionSource_WithResultSelector_Cancellation_AfterSelectorThrows()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[4];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+            tcss[3] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var ex = new Exception();
+
+            var res = Observable.SelectMany(xs, (x, token) =>
+            {
+                if (x == 2)
+                    throw ex;
+
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            }, (x, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = false;
+            var evt = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; evt.Set(); }, () => { done = true; evt.Set(); });
+
+            tcss[1].SetResult(43);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            tcss[0].SetResult(42);
+
+            xs.OnNext(2); // causes error
+            xs.OnCompleted();
+
+            evt.WaitOne();
+
+            Assert.False(done);
+            Assert.Same(ex, err);
+            Assert.Equal(2, n);
+            Assert.Equal(0, m);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, int, Task<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, Task<int>>)null));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int>(DummyFunc<int, int, CancellationToken, Task<int>>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, CancellationToken, Task<int>>)null));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, int, Task<int>>.Instance, DummyFunc<int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, Task<int>>)null, DummyFunc<int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, Task<int>>.Instance, ((Func<int, int, int, int>)null)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SelectMany<int, int, int>(DummyFunc<int, int, CancellationToken, Task<int>>.Instance, DummyFunc<int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany((Func<int, int, CancellationToken, Task<int>>)null, DummyFunc<int, int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SelectMany(DummyFunc<int, int, CancellationToken, Task<int>>.Instance, ((Func<int, int, int, int>)null)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_Index()
+        {
+            var res = Observable.Range(0, 10).SelectMany((int x, int i) => Task.Factory.StartNew(() => new { x, i })).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany((x, i) => new[] { new { x, i } }).SequenceEqual(res.OrderBy(v => v.i)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_Cancellation_Index()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, i, ctx) => Task.Factory.StartNew(() => new { x, i }, ctx)).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany((x, i) => new[] { new { x, i } }).SequenceEqual(res.OrderBy(v => v.i)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_ResultSelector_Index()
+        {
+            var res = Observable.Range(0, 10).SelectMany((int x, int i) => Task.Factory.StartNew(() => new { x, i }), (x, i, r) => r).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany((x, i) => new[] { new { x, i } }).SequenceEqual(res.OrderBy(v => v.i)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_ResultSelector_Cancellation_Index()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, i, ctx) => Task.Factory.StartNew(() => new { x, i }, ctx), (x, i, r) => r).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany((x, i) => new[] { new { x, i } }).SequenceEqual(res.OrderBy(v => v.i)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task1()
+        {
+            var res = Observable.Range(0, 10).SelectMany((int x, int _) => Task.Factory.StartNew(() => x + 1)).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task2()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, _, ct) => Task.Factory.StartNew(() => x + 1, ct)).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_TaskThrows()
+        {
+            var ex = new Exception();
+
+            var res = Observable.Range(0, 10).SelectMany((int x, int _) => Task.Factory.StartNew(() =>
+            {
+                if (x > 5)
+                    throw ex;
+                return x + 1;
+            })).ToEnumerable();
+
+            ReactiveAssert.Throws(ex, () =>
+            {
+                foreach (var x in res)
+                    ;
+            });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_SelectorThrows()
+        {
+            var ex = new Exception();
+
+            var res = Observable.Range(0, 10).SelectMany((int x, int _) =>
+            {
+                if (x > 5)
+                    throw ex;
+                return Task.Factory.StartNew(() => x + 1);
+            }).ToEnumerable();
+
+            ReactiveAssert.Throws(ex, () =>
+            {
+                foreach (var x in res)
+                    ;
+            });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_ResultSelector1()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, _) => Task.Factory.StartNew(() => x + 1), (x, _, y) => x + y).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { 2 * x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_ResultSelector2()
+        {
+            var res = Observable.Range(0, 10).SelectMany((x, _, ct) => Task.Factory.StartNew(() => x + 1, ct), (x, _, y) => x + y).ToEnumerable();
+            Assert.True(Enumerable.Range(0, 10).SelectMany(x => new[] { 2 * x + 1 }).SequenceEqual(res.OrderBy(x => x)));
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_Task_ResultSelectorThrows()
+        {
+            var ex = new Exception();
+
+            var res = Observable.Range(0, 10).SelectMany((x, _) => Task.Factory.StartNew(() => x + 1), (x, _, y) =>
+            {
+                if (x > 5)
+                    throw ex;
+                return x + y;
+            }).ToEnumerable();
+
+            ReactiveAssert.Throws(ex, () =>
+            {
+                foreach (var x in res)
+                    ;
+            });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_RanToCompletion_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_RanToCompletion_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Faulted_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Faulted_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Canceled_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            tcss[1].SetCanceled();
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Canceled_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            tcss[1].SetCanceled();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_InnerCompleteBeforeOuter()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            xs.OnCompleted();
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_OuterCompleteBeforeInner()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (int x, int _) => tcss[x].Task);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Cancellation_NeverInvoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => tcs.SetCanceled());
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            var d = res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42, 43, 44 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Cancellation_Invoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = false;
+            var d = res.Subscribe(lst.Add, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            d.Dispose();
+
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // never observed because xs.OnNext(2) happened after dispose
+
+            lst.AssertEqual(new[] { 42 });
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Cancellation_AfterOuterError()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = false;
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => err = ex_, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            var ex = new Exception();
+            xs.OnError(ex);
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // no-op
+
+            lst.AssertEqual(new[] { 42 });
+            Assert.Same(ex, err);
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_Simple_Cancellation_AfterSelectorThrows()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[4];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+            tcss[3] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var ex = new Exception();
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                if (x == 2)
+                    throw ex;
+
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            });
+
+            var lst = new List<int>();
+
+            var done = false;
+            var evt = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; evt.Set(); }, () => { done = true; evt.Set(); });
+
+            tcss[1].SetResult(43);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            tcss[0].SetResult(42);
+
+            xs.OnNext(2); // causes error
+            xs.OnCompleted();
+
+            evt.WaitOne();
+
+            Assert.False(done);
+            Assert.Same(ex, err);
+            Assert.Equal(2, n);
+            Assert.Equal(0, m);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_RanToCompletion_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 0, 43 + 1 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_RanToCompletion_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[2];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+
+            tcss[0].SetResult(42);
+            tcss[1].SetResult(43);
+
+            var res = Observable.SelectMany(Observable.Range(0, 2), (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 0, 43 + 1 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Faulted_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Faulted_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var ex = new Exception();
+            tcss[1].SetException(ex);
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.Same(ex, err);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Canceled_Async()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            tcss[1].SetCanceled();
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Canceled_Sync()
+        {
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            tcss[1].SetCanceled();
+
+            var res = Observable.SelectMany(Observable.Range(0, 3), (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var err = default(Exception);
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; done.Set(); }, () => done.Set());
+
+            done.WaitOne();
+
+            lst.AssertEqual(new int[0]);
+            Assert.True(err is TaskCanceledException && ((TaskCanceledException)err).Task == tcss[1].Task);
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_InnerCompleteBeforeOuter()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            xs.OnCompleted();
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 1, 43 + 0, 44 + 2 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_OuterCompleteBeforeInner()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (x, _) => tcss[x].Task, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 1, 43 + 0, 44 + 2 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Cancellation_NeverInvoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => tcs.SetCanceled());
+
+                return tcs.Task;
+            }, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = new ManualResetEvent(false);
+            var d = res.Subscribe(lst.Add, () => done.Set());
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            tcss[0].SetResult(43);
+            tcss[2].SetResult(44);
+
+            done.WaitOne();
+
+            lst.OrderBy(x => x).AssertEqual(new[] { 42 + 1, 43 + 0, 44 + 2 });
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Cancellation_Invoked()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            }, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = false;
+            var d = res.Subscribe(lst.Add, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            d.Dispose();
+
+            xs.OnNext(2);
+            xs.OnCompleted();
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // never observed because xs.OnNext(2) happened after dispose
+
+            lst.AssertEqual(new[] { 42 + 1 });
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Cancellation_AfterOuterError()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[3];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            }, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = false;
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => err = ex_, () => done = true);
+
+            tcss[1].SetResult(42);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            var ex = new Exception();
+            xs.OnError(ex);
+
+            Assert.False(tcss[0].TrySetResult(43));
+            tcss[2].SetResult(44); // no-op
+
+            lst.AssertEqual(new[] { 42 + 1 });
+            Assert.Same(ex, err);
+            Assert.False(done);
+            Assert.Equal(2, n);
+            Assert.Equal(1, m); // tcss[1] was already finished
+        }
+
+        [Fact]
+        public void SelectManyWithIndex_TaskWithCompletionSource_WithResultSelector_Cancellation_AfterSelectorThrows()
+        {
+            var xs = new Subject<int>();
+
+            var tcss = new TaskCompletionSource<int>[4];
+            tcss[0] = new TaskCompletionSource<int>();
+            tcss[1] = new TaskCompletionSource<int>();
+            tcss[2] = new TaskCompletionSource<int>();
+            tcss[3] = new TaskCompletionSource<int>();
+
+            var n = 0;
+            var m = 0;
+
+            var ex = new Exception();
+
+            var res = Observable.SelectMany(xs, (x, _, token) =>
+            {
+                if (x == 2)
+                    throw ex;
+
+                var tcs = tcss[x];
+
+                token.Register(() => { n++; m += tcs.TrySetCanceled() ? 1 : 0; });
+
+                return tcs.Task;
+            }, (x, _, y) => x + y);
+
+            var lst = new List<int>();
+
+            var done = false;
+            var evt = new ManualResetEvent(false);
+            var err = default(Exception);
+            res.Subscribe(lst.Add, ex_ => { err = ex_; evt.Set(); }, () => { done = true; evt.Set(); });
+
+            tcss[1].SetResult(43);
+
+            xs.OnNext(0);
+            xs.OnNext(1);
+
+            tcss[0].SetResult(42);
+
+            xs.OnNext(2); // causes error
+            xs.OnCompleted();
+
+            evt.WaitOne();
+
+            Assert.False(done);
+            Assert.Same(ex, err);
+            Assert.Equal(2, n);
+            Assert.Equal(0, m);
+        }
+
+    }
+}

+ 636 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SelectTest.cs

@@ -0,0 +1,636 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class SelectTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Select_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).Select<int, int>(DummyFunc<int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Select<int, int>((Func<int, int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Select<int, int>(DummyFunc<int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void Select_Throws()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Return(1).Select<int, int>(x => x).Subscribe(
+                 x =>
+                 {
+                     throw new InvalidOperationException();
+                 }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Throw<int>(new Exception()).Select<int, int>(x => x).Subscribe(
+                 x => { },
+                 exception =>
+                 {
+                     throw new InvalidOperationException();
+                 }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                 Observable.Empty<int>().Select<int, int>(x => x).Subscribe(
+                 x => { },
+                 exception => { },
+                 () =>
+                 {
+                     throw new InvalidOperationException();
+                 }));
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Select(x => x).Subscribe());
+        }
+
+        [Fact]
+        public void Select_DisposeInsideSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 1),
+                OnNext(200, 2),
+                OnNext(500, 3),
+                OnNext(600, 4)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.CreateObserver<int>();
+
+            var d = new SerialDisposable();
+            d.Disposable = xs.Select(x =>
+            {
+                invoked++;
+                if (scheduler.Clock > 400)
+                    d.Dispose();
+                return x;
+            }).Subscribe(res);
+
+            scheduler.ScheduleAbsolute(Disposed, d.Dispose);
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(100, 1),
+                OnNext(200, 2)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(0, 500)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void Select_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(350, 5),
+                OnCompleted<int>(400),
+                OnNext(410, -1),
+                OnCompleted<int>(420),
+                OnError<int>(430, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select(x =>
+                {
+                    invoked++;
+                    return x + 1;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(290, 5),
+                OnNext(350, 6),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void Select_NotCompleted()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(350, 5)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select(x =>
+                {
+                    invoked++;
+                    return x + 1;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(290, 5),
+                OnNext(350, 6)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void Select_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(350, 5),
+                OnError<int>(400, ex),
+                OnNext(410, -1),
+                OnCompleted<int>(420),
+                OnError<int>(430, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select(x =>
+                {
+                    invoked++;
+                    return x + 1;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnNext(290, 5),
+                OnNext(350, 6),
+                OnError<int>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void Select_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(350, 5),
+                OnCompleted<int>(400),
+                OnNext(410, -1),
+                OnCompleted<int>(420),
+                OnError<int>(430, new Exception())
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Select(x =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+                    return x + 1;
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 3),
+                OnNext(240, 4),
+                OnError<int>(290, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectWithIndex_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).Select<int, int>(DummyFunc<int, int, int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Select<int, int>((Func<int, int, int>)null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Select<int, int>(DummyFunc<int, int, int>.Instance).Subscribe(null));
+        }
+
+        [Fact]
+        public void SelectWithIndex_Throws()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Return(1).Select<int, int>((x, index) => x).Subscribe(
+                 x =>
+                 {
+                     throw new InvalidOperationException();
+                 }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                Observable.Throw<int>(new Exception()).Select<int, int>((x, index) => x).Subscribe(
+                 x => { },
+                 exception =>
+                 {
+                     throw new InvalidOperationException();
+                 }));
+            ReactiveAssert.Throws<InvalidOperationException>(() =>
+                 Observable.Empty<int>().Select<int, int>((x, index) => x).Subscribe(
+                 x => { },
+                 exception => { },
+                 () =>
+                 {
+                     throw new InvalidOperationException();
+                 }));
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Create<int>(new Func<IObserver<int>, Action>(o => { throw new InvalidOperationException(); })).Select((x, index) => x).Subscribe());
+        }
+
+        [Fact]
+        public void SelectWithIndex_DisposeInsideSelector()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(100, 4),
+                OnNext(200, 3),
+                OnNext(500, 2),
+                OnNext(600, 1)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.CreateObserver<int>();
+
+            var d = new SerialDisposable();
+            d.Disposable = xs.Select((x, index) =>
+            {
+                invoked++;
+                if (scheduler.Clock > 400)
+                    d.Dispose();
+                return x + index * 10;
+            }).Subscribe(res);
+
+            scheduler.ScheduleAbsolute(Disposed, d.Dispose);
+
+            scheduler.Start();
+
+            res.Messages.AssertEqual(
+                OnNext(100, 4),
+                OnNext(200, 13)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(0, 500)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SelectWithIndex_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400),
+                OnNext(410, -1),
+                OnCompleted<int>(420),
+                OnError<int>(430, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select((x, index) =>
+                {
+                    invoked++;
+                    return (x + 1) + (index * 10);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 5),
+                OnNext(240, 14),
+                OnNext(290, 23),
+                OnNext(350, 32),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void SelectWithIndex_NotCompleted()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select((x, index) =>
+                {
+                    invoked++;
+                    return (x + 1) + (index * 10);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 5),
+                OnNext(240, 14),
+                OnNext(290, 23),
+                OnNext(350, 32)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void SelectWithIndex_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnError<int>(400, ex),
+                OnNext(410, -1),
+                OnCompleted<int>(420),
+                OnError<int>(430, new Exception())
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select((x, index) =>
+                {
+                    invoked++;
+                    return (x + 1) + (index * 10);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 5),
+                OnNext(240, 14),
+                OnNext(290, 23),
+                OnNext(350, 32),
+                OnError<int>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void SelectWithIndex_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var invoked = 0;
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400),
+                OnNext(410, -1),
+                OnCompleted<int>(420),
+                OnError<int>(430, new Exception())
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.Select((x, index) =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+                    return (x + 1) + (index * 10);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 5),
+                OnNext(240, 14),
+                OnError<int>(290, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void Select_Select1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select(x => x + 1).Select(x => x - 2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4 + 1 - 2),
+                OnNext(240, 3 + 1 - 2),
+                OnNext(290, 2 + 1 - 2),
+                OnNext(350, 1 + 1 - 2),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Select_Select2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select((x, i) => x + i).Select(x => x - 2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4 + 0 - 2),
+                OnNext(240, 3 + 1 - 2),
+                OnNext(290, 2 + 2 - 2),
+                OnNext(350, 1 + 3 - 2),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Select_Select3()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select(x => x + 1).Select((x, i) => x - i)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4 + 1 - 0),
+                OnNext(240, 3 + 1 - 1),
+                OnNext(290, 2 + 1 - 2),
+                OnNext(350, 1 + 1 - 3),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Select_Select4()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 5),
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Select((x, i) => x + i).Select((x, i) => x - i)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 4),
+                OnNext(240, 3),
+                OnNext(290, 2),
+                OnNext(350, 1),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+    }
+}

+ 1088 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SequenceEqualTest.cs

@@ -0,0 +1,1088 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class SequenceEqualTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SequenceEqual_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(default(IObservable<int>), DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(DummyObservable<int>.Instance, default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(default(IObservable<int>), DummyObservable<int>.Instance, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(DummyObservable<int>.Instance, default(IObservable<int>), EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(DummyObservable<int>.Instance, DummyObservable<int>.Instance, default(IEqualityComparer<int>)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(default(IObservable<int>), new[] { 42 }));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(DummyObservable<int>.Instance, default(IEnumerable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(default(IObservable<int>), new[] { 42 }, EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(DummyObservable<int>.Instance, default(IEnumerable<int>), EqualityComparer<int>.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SequenceEqual<int>(DummyObservable<int>.Instance, new[] { 42 }, default(IEqualityComparer<int>)));
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_Equal()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(720)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(720, true),
+                OnCompleted<bool>(720)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 720)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 720)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_Equal_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(720)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(720, true),
+                OnCompleted<bool>(720)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 720)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 720)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_Left()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 0),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(720)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, false),
+                OnCompleted<bool>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_Left_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 0),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnCompleted<int>(720)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, false),
+                OnCompleted<bool>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_Right()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnNext(350, 8)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(510, false),
+                OnCompleted<bool>(510)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_Right_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnNext(350, 8)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(510, false),
+                OnCompleted<bool>(510)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnNext(490, 8),
+                OnNext(520, 9),
+                OnNext(580, 10),
+                OnNext(600, 11)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnNext(350, 9),
+                OnNext(400, 9),
+                OnNext(410, 10),
+                OnNext(490, 11),
+                OnNext(550, 12),
+                OnNext(560, 13)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(490, false),
+                OnCompleted<bool>(490)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 490)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 490)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_2_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnNext(490, 8),
+                OnNext(520, 9),
+                OnNext(580, 10),
+                OnNext(600, 11)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(280, 4),
+                OnNext(300, 5),
+                OnNext(330, 6),
+                OnNext(340, 7),
+                OnNext(350, 9),
+                OnNext(400, 9),
+                OnNext(410, 10),
+                OnNext(490, 11),
+                OnNext(550, 12),
+                OnNext(560, 13)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(490, false),
+                OnCompleted<bool>(490)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 490)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 490)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_3()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnCompleted<int>(330)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(420, false),
+                OnCompleted<bool>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_3_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnCompleted<int>(330)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(420)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(420, false),
+                OnCompleted<bool>(420)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 420)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_ComparerThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnCompleted<int>(330)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(420)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys, new ThrowComparer(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(270, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_ComparerThrows_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnCompleted<int>(330)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(90, 1),
+                OnNext(270, 3),
+                OnNext(400, 4),
+                OnCompleted<int>(420)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs, new ThrowComparer(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(270, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+        }
+
+        class ThrowComparer : IEqualityComparer<int>
+        {
+            private Exception _ex;
+
+            public ThrowComparer(Exception ex)
+            {
+                _ex = ex;
+            }
+
+            public bool Equals(int x, int y)
+            {
+                throw _ex;
+            }
+
+            public int GetHashCode(int obj)
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_4()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(250, 1),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(290, 1),
+                OnNext(310, 2),
+                OnCompleted<int>(350)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, false),
+                OnCompleted<bool>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_NotEqual_4_Sym()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(250, 1),
+                OnCompleted<int>(300)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(290, 1),
+                OnNext(310, 2),
+                OnCompleted<int>(350)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, false),
+                OnCompleted<bool>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_Left_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(250, 1),
+                OnError<int>(300, ex)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(290, 1),
+                OnNext(310, 2),
+                OnCompleted<int>(350)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Observable_Right_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(290, 1),
+                OnNext(310, 2),
+                OnCompleted<int>(350)
+            );
+
+            var ys = scheduler.CreateHotObservable(
+                OnNext(250, 1),
+                OnError<int>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                ys.SequenceEqual(xs)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(300, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            ys.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_Equal()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3, 4, 5, 6, 7 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(510, true),
+                OnCompleted<bool>(510)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_NotEqual_Elements()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3, 4, 9, 6, 7 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, false),
+                OnCompleted<bool>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_Comparer_Equal()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3 - 2, 4, 5, 6 + 42, 7 - 6 }, new OddEvenComparer())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(510, true),
+                OnCompleted<bool>(510)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_Comparer_NotEqual()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3 - 2, 4, 5 + 9, 6 + 42, 7 - 6 }, new OddEvenComparer())
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, false),
+                OnCompleted<bool>(310)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        class OddEvenComparer : IEqualityComparer<int>
+        {
+            public bool Equals(int x, int y)
+            {
+                return x % 2 == y % 2;
+            }
+
+            public int GetHashCode(int obj)
+            {
+                return (obj % 2).GetHashCode();
+            }
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_Comparer_Throws()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3, 4, 5, 6, 7 }, new ThrowingComparer(5, ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(310, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        class ThrowingComparer : IEqualityComparer<int>
+        {
+            private int _x;
+            private Exception _ex;
+
+            public ThrowingComparer(int x, Exception ex)
+            {
+                _x = x;
+                _ex = ex;
+            }
+
+            public bool Equals(int x, int y)
+            {
+                if (x == _x)
+                    throw _ex;
+
+                return x == y;
+            }
+
+            public int GetHashCode(int obj)
+            {
+                return obj.GetHashCode();
+            }
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_NotEqual_TooLong()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3, 4, 5, 6, 7, 8 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(510, false),
+                OnCompleted<bool>(510)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 510)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_NotEqual_TooShort()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnNext(310, 5),
+                OnNext(340, 6),
+                OnNext(450, 7),
+                OnCompleted<int>(510)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3, 4, 5, 6 })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(450, false),
+                OnCompleted<bool>(450)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 450)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_OnError()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnError<int>(310, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new[] { 3, 4 })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(310, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_IteratorThrows1()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnNext(290, 4),
+                OnCompleted<int>(310)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(Throw(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(290, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_IteratorThrows2()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnCompleted<int>(310)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(Throw(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(310, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 310)
+            );
+        }
+
+        private IEnumerable<int> Throw(Exception ex)
+        {
+            yield return 3;
+            throw ex;
+        }
+
+        [Fact]
+        public void SequenceEqual_Enumerable_GetEnumeratorThrows()
+        {
+            var ex = new Exception();
+
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(110, 1),
+                OnNext(190, 2),
+                OnNext(240, 3),
+                OnCompleted<int>(310)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SequenceEqual(new RogueEnumerable<int>(ex))
+            );
+
+            res.Messages.AssertEqual(
+                OnError<bool>(200, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+            );
+        }
+
+    }
+}

+ 264 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleAsyncTest.cs

@@ -0,0 +1,264 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class SingleAsyncTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SingleAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleAsync(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleAsync(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleAsync(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void SingleAsync_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_Many()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_Predicate()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(240, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_Predicate_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(250, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_Predicate_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync(x => x == 4)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_Predicate_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SingleAsync_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleAsync(x => { if (x < 4) return false; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 300 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleOrDefaultAsyncTest.cs

@@ -0,0 +1,300 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class SingleOrAsyncTestTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SingleOrDefaultAsync_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleOrDefaultAsync(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleOrDefaultAsync(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleOrDefaultAsync(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Many()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync()
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Predicate()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(240, e => e is InvalidOperationException)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 240)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Predicate_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync(x => x % 2 == 1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Predicate_One()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync(x => x == 4)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 4),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Predicate_None()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 0),
+                OnCompleted<int>(250)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_Predicate_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync(x => x > 10)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SingleOrDefaultAsync_PredicateThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SingleOrDefaultAsync(x => { if (x < 4) return false; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(230, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+    }
+}

+ 89 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleOrDefaultTest.cs

@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class SingleOrDefaultTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SingleOrDefault_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleOrDefault(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleOrDefault(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SingleOrDefault(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void SingleOrDefault_Empty()
+        {
+            Assert.Equal(default(int), Observable.Empty<int>().SingleOrDefault());
+        }
+
+        [Fact]
+        public void SingleOrDefaultPredicate_Empty()
+        {
+            Assert.Equal(default(int), Observable.Empty<int>().SingleOrDefault(_ => true));
+        }
+
+        [Fact]
+        public void SingleOrDefault_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).SingleOrDefault());
+        }
+
+        [Fact]
+        public void SingleOrDefault_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.SingleOrDefault());
+        }
+
+        [Fact]
+        public void SingleOrDefault_Range()
+        {
+            var value = 42;
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Range(value, 10).SingleOrDefault());
+        }
+
+        [Fact]
+        public void SingleOrDefaultPredicate_Range()
+        {
+            var value = 42;
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Range(value, 10).SingleOrDefault(i => i % 2 == 0));
+        }
+
+        [Fact]
+        public void SingleOrDefault_Range_ReducesToSingle()
+        {
+            var value = 42;
+            Assert.Equal(45, Observable.Range(value, 10).SingleOrDefault(i => i == 45));
+        }
+
+        [Fact]
+        public void SingleOrDefault_Range_ReducesToNone()
+        {
+            var value = 42;
+            Assert.Equal(0, Observable.Range(value, 10).SingleOrDefault(i => i > 100));
+        }
+
+    }
+}

+ 83 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleTest.cs

@@ -0,0 +1,83 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+
+namespace ReactiveTests.Tests
+{
+    public class SingleTest : ReactiveTest
+    {
+
+        [Fact]
+        public void Single_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Single(default(IObservable<int>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Single(default(IObservable<int>), _ => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Single(DummyObservable<int>.Instance, default(Func<int, bool>)));
+        }
+
+        [Fact]
+        public void Single_Empty()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Empty<int>().Single());
+        }
+
+        [Fact]
+        public void SinglePredicate_Empty()
+        {
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Empty<int>().Single(_ => true));
+        }
+
+        [Fact]
+        public void Single_Return()
+        {
+            var value = 42;
+            Assert.Equal(value, Observable.Return<int>(value).Single());
+        }
+
+        [Fact]
+        public void Single_Throw()
+        {
+            var ex = new Exception();
+
+            var xs = Observable.Throw<int>(ex);
+
+            ReactiveAssert.Throws(ex, () => xs.Single());
+        }
+
+        [Fact]
+        public void Single_Range()
+        {
+            var value = 42;
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Range(value, 10).Single());
+        }
+
+        [Fact]
+        public void SinglePredicate_Range()
+        {
+            var value = 42;
+            ReactiveAssert.Throws<InvalidOperationException>(() => Observable.Range(value, 10).Single(i => i % 2 == 0));
+        }
+
+        [Fact]
+        public void SinglePredicate_Range_ReducesToSingle()
+        {
+            var value = 42;
+            Assert.Equal(45, Observable.Range(value, 10).Single(i => i == 45));
+        }
+
+
+    }
+}

+ 659 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipLastTest.cs

@@ -0,0 +1,659 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    #region + Count +
+
+    public class SkipLastTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SkipLast_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipLast<int>(null, 0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.SkipLast(DummyObservable<int>.Instance, -1));
+        }
+
+        [Fact]
+        public void SkipLast_Zero_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnCompleted<int>(650)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnCompleted<int>(650)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Zero_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnError<int>(650, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnError<int>(650, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Zero_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_One_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnCompleted<int>(650)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnNext(270, 3),
+                OnNext(310, 4),
+                OnNext(360, 5),
+                OnNext(380, 6),
+                OnNext(410, 7),
+                OnNext(590, 8),
+                OnCompleted<int>(650)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_One_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnError<int>(650, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnNext(270, 3),
+                OnNext(310, 4),
+                OnNext(360, 5),
+                OnNext(380, 6),
+                OnNext(410, 7),
+                OnNext(590, 8),
+                OnError<int>(650, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_One_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(1)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(250, 2),
+                OnNext(270, 3),
+                OnNext(310, 4),
+                OnNext(360, 5),
+                OnNext(380, 6),
+                OnNext(410, 7),
+                OnNext(590, 8)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Three_Completed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnCompleted<int>(650)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 2),
+                OnNext(360, 3),
+                OnNext(380, 4),
+                OnNext(410, 5),
+                OnNext(590, 6),
+                OnCompleted<int>(650)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Three_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9),
+                OnError<int>(650, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 2),
+                OnNext(360, 3),
+                OnNext(380, 4),
+                OnNext(410, 5),
+                OnNext(590, 6),
+                OnError<int>(650, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 650)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Three_Disposed()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(180, 1),
+                OnNext(210, 2),
+                OnNext(250, 3),
+                OnNext(270, 4),
+                OnNext(310, 5),
+                OnNext(360, 6),
+                OnNext(380, 7),
+                OnNext(410, 8),
+                OnNext(590, 9)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 2),
+                OnNext(360, 3),
+                OnNext(380, 4),
+                OnNext(410, 5),
+                OnNext(590, 6)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        #endregion
+
+        #region + Timed +
+
+        [Fact]
+        public void SkipLast_Timed_ArgumentChecking()
+        {
+            var xs = Observable.Return(42);
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipLast(default(IObservable<int>), TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.SkipLast(xs, TimeSpan.FromSeconds(-1)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipLast(default(IObservable<int>), TimeSpan.FromSeconds(1), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipLast(xs, TimeSpan.FromSeconds(1), default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.SkipLast(xs, TimeSpan.FromSeconds(-1), Scheduler.Default));
+        }
+
+        [Fact]
+        public void SkipLast_Zero1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.Zero, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Zero2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.Zero, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Some1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.FromTicks(15), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(230, 1),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Some2()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnNext(270, 7),
+                OnNext(280, 8),
+                OnNext(290, 9),
+                OnCompleted<int>(300)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.FromTicks(45), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(260, 1),
+                OnNext(270, 2),
+                OnNext(280, 3),
+                OnNext(290, 4),
+                OnNext(300, 5),
+                OnCompleted<int>(300)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_All()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipLast(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void SkipLast_Default1()
+        {
+            var xs = Observable.Range(0, 10, Scheduler.Default);
+
+            var res = xs.SkipLast(TimeSpan.FromSeconds(60));
+
+            var e = new ManualResetEvent(false);
+
+            var lst = new List<int>();
+            res.Subscribe(
+                lst.Add,
+                () => e.Set()
+            );
+
+            e.WaitOne();
+
+            Assert.True(lst.Count == 0);
+        }
+
+        [Fact]
+        public void SkipLast_Default2()
+        {
+            var xs = Observable.Range(0, 10, Scheduler.Default);
+
+            var res = xs.SkipLast(TimeSpan.FromSeconds(60), Scheduler.Default.DisableOptimizations());
+
+            var e = new ManualResetEvent(false);
+
+            var lst = new List<int>();
+            res.Subscribe(
+                lst.Add,
+                () => e.Set()
+            );
+
+            e.WaitOne();
+
+            Assert.True(lst.Count == 0);
+        }
+
+        [Fact]
+        public void SkipLast_Default3()
+        {
+            var xs = Observable.Range(0, 10, Scheduler.Default);
+
+            var res = xs.SkipLast(TimeSpan.Zero);
+
+            var e = new ManualResetEvent(false);
+
+            var lst = new List<int>();
+            res.Subscribe(
+                lst.Add,
+                () => e.Set()
+            );
+
+            e.WaitOne();
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(0, 10)));
+        }
+
+        [Fact]
+        public void SkipLast_Default4()
+        {
+            var xs = Observable.Range(0, 10, Scheduler.Default);
+
+            var res = xs.SkipLast(TimeSpan.Zero, Scheduler.Default.DisableOptimizations());
+
+            var e = new ManualResetEvent(false);
+
+            var lst = new List<int>();
+            res.Subscribe(
+                lst.Add,
+                () => e.Set()
+            );
+
+            e.WaitOne();
+
+            Assert.True(lst.SequenceEqual(Enumerable.Range(0, 10)));
+        }
+
+        #endregion
+
+    }
+}

+ 714 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipTest.cs

@@ -0,0 +1,714 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class SkipTest : ReactiveTest
+    {
+
+        #region + Count +
+
+        [Fact]
+        public void Skip_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).Skip(0));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DummyObservable<int>.Instance.Skip(-1));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.Skip(0).Subscribe(null));
+        }
+
+        [Fact]
+        public void Skip_Complete_After()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnCompleted<int>(690)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(20)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(690)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Complete_Same()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnCompleted<int>(690)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(17)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(690)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Complete_Before()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnCompleted<int>(690)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(10)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnCompleted<int>(690)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Complete_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnCompleted<int>(690)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(0)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnCompleted<int>(690)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Error_After()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnError<int>(690, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(20)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(690, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Error_Same()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnError<int>(690, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(17)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(690, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Error_Before()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnError<int>(690, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(3)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10),
+                OnError<int>(690, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 690)
+            );
+        }
+
+        [Fact]
+        public void Skip_Dispose_Before()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(3),
+                250
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+        }
+
+        [Fact]
+        public void Skip_Dispose_After()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnNext(410, 15),
+                OnNext(415, 16),
+                OnNext(460, 72),
+                OnNext(510, 76),
+                OnNext(560, 32),
+                OnNext(570, -100),
+                OnNext(580, -3),
+                OnNext(590, 5),
+                OnNext(630, 10)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(3),
+                400
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void Skip_Skip1()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(70, 6),
+                OnNext(150, 4),
+                OnNext(210, 9),
+                OnNext(230, 13),
+                OnNext(270, 7),
+                OnNext(280, 1),
+                OnNext(300, -1),
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnCompleted<int>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(3).Skip(2)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(310, 3),
+                OnNext(340, 8),
+                OnNext(370, 11),
+                OnCompleted<int>(400)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        #endregion
+
+        #region + Timed +
+
+        [Fact]
+        public void Skip_Timed_ArgumentChecking()
+        {
+            var xs = Observable.Return(42);
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Skip(default(IObservable<int>), TimeSpan.FromSeconds(1)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Skip(xs, TimeSpan.FromSeconds(-1)));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Skip(default(IObservable<int>), TimeSpan.FromSeconds(1), Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Skip(xs, TimeSpan.FromSeconds(1), default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Observable.Skip(xs, TimeSpan.FromSeconds(-1), Scheduler.Default));
+        }
+
+        [Fact]
+        public void Skip_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.Zero, scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Skip_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.FromTicks(15), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Skip_Late()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void Skip_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void Skip_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.FromTicks(50), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void Skip_Twice1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.FromTicks(15), scheduler).Skip(TimeSpan.FromTicks(30), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+        }
+
+        [Fact]
+        public void Skip_Twice2()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.Skip(TimeSpan.FromTicks(30), scheduler).Skip(TimeSpan.FromTicks(15), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+        }
+
+        [Fact]
+        public void Skip_Default()
+        {
+            var xs = Observable.Range(0, 10, Scheduler.Default);
+
+            var res = xs.Skip(TimeSpan.FromSeconds(60));
+
+            var e = new ManualResetEvent(false);
+
+            var lst = new List<int>();
+            res.Subscribe(
+                lst.Add,
+                () => e.Set()
+            );
+
+            e.WaitOne();
+
+            Assert.True(lst.Count == 0);
+        }
+
+        #endregion
+
+    }
+}

+ 645 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipUntilTest.cs

@@ -0,0 +1,645 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class SkipUntilTest : ReactiveTest
+    {
+        #region + Observable +
+        [Fact]
+        public void SkipUntil_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipUntil<int, int>(null, DummyObservable<int>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipUntil<int, int>(DummyObservable<int>.Instance, null));
+        }
+
+        [Fact]
+        public void SkipUntil_SomeData_Next()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4), //!
+                OnNext(240, 5), //!
+                OnCompleted<int>(250)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(225, 99),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_SomeData_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(225, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(225, ex)
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Error_SomeData()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnError<int>(220, ex)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(230, 2),
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(220, ex)
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 220)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_SomeData_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(225)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Never_Next()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(225, 2), //!
+                OnCompleted<int>(250)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Never_Error1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(225, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(225, ex)
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_SomeData_Error2()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnError<int>(300, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(300, ex)
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_SomeData_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 250)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 1000 /* can't dispose prematurely, could be in flight to dispatch OnError */)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Never_Empty()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnCompleted<int>(225)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 225)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Never_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var r = scheduler.CreateHotObservable(
+                OnNext(150, 1)
+            );
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            l.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+
+            r.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_HasCompletedCausesDisposal()
+        {
+            var scheduler = new TestScheduler();
+
+            bool disposed = false;
+
+            var l = scheduler.CreateHotObservable(
+                OnNext(150, 1),
+                OnNext(210, 2),
+                OnNext(220, 3),
+                OnNext(230, 4),
+                OnNext(240, 5),
+                OnCompleted<int>(250)
+            );
+
+            var r = Observable.Create<int>(obs => () => { disposed = true; });
+
+            var res = scheduler.Start(() =>
+                l.SkipUntil(r)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            Assert.True(disposed, "disposed");
+        }
+
+        [Fact]
+        public void SkipUntil_Immediate()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = Observable.Return(1);
+            var ys = Observable.Return("bar");
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(ys)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(200, 1),
+                OnCompleted<int>(200)
+            );
+        }
+        #endregion
+
+        #region + Timed +
+
+        [Fact]
+        public void SkipUntil_Timed_ArgumentChecking()
+        {
+            var xs = Observable.Return(42);
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipUntil(default(IObservable<int>), DateTimeOffset.Now));
+
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipUntil(default(IObservable<int>), DateTimeOffset.Now, Scheduler.Default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SkipUntil(xs, DateTimeOffset.Now, default(IScheduler)));
+        }
+
+        [Fact]
+        public void SkipUntil_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Some()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(215, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Late()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnCompleted<int>(230)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(250, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(230)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 230)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Error()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnError<int>(210, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(250, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(210, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 210)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Never()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(250, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 1000)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Twice1()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(215, TimeSpan.Zero), scheduler).SkipUntil(new DateTimeOffset(230, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Twice2()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable<int>(
+                OnNext(210, 1),
+                OnNext(220, 2),
+                OnNext(230, 3),
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipUntil(new DateTimeOffset(230, TimeSpan.Zero), scheduler).SkipUntil(new DateTimeOffset(215, TimeSpan.Zero), scheduler)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(240, 4),
+                OnNext(250, 5),
+                OnNext(260, 6),
+                OnCompleted<int>(270)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+        }
+
+        [Fact]
+        public void SkipUntil_Default()
+        {
+            var xs = Observable.Range(0, 10, Scheduler.Default);
+
+            var res = xs.SkipUntil(DateTimeOffset.UtcNow.AddMinutes(1));
+
+            var e = new ManualResetEvent(false);
+
+            var lst = new List<int>();
+            res.Subscribe(
+                lst.Add,
+                () => e.Set()
+            );
+
+            e.WaitOne();
+
+            Assert.True(lst.Count == 0);
+        }
+
+        #endregion
+    }
+}

+ 514 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SkipWhileTest.cs

@@ -0,0 +1,514 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reactive;
+using System.Reactive.Concurrency;
+using System.Reactive.Linq;
+using Microsoft.Reactive.Testing;
+using Xunit;
+using ReactiveTests.Dummies;
+using System.Reflection;
+using System.Threading;
+using System.Reactive.Disposables;
+using System.Reactive.Subjects;
+
+namespace ReactiveTests.Tests
+{
+    public class SkipWhileTest : ReactiveTest
+    {
+
+        [Fact]
+        public void SkipWhile_ArgumentChecking()
+        {
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SkipWhile(DummyFunc<int, bool>.Instance));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SkipWhile(default(Func<int, bool>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SkipWhile(DummyFunc<int, bool>.Instance).Subscribe(null));
+            ReactiveAssert.Throws<ArgumentNullException>(() => ((IObservable<int>)null).SkipWhile((x, i) => true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => DummyObservable<int>.Instance.SkipWhile(default(Func<int, int, bool>)));
+        }
+
+        [Fact]
+        public void SkipWhile_Complete_Before()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnCompleted<int>(330),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnCompleted<int>(330)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 330)
+            );
+
+            Assert.Equal(4, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Complete_After()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            Assert.Equal(6, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Error_Before()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnError<int>(270, ex),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(270, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 270)
+            );
+
+            Assert.Equal(2, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Error_After()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnError<int>(600, ex)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnError<int>(600, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            Assert.Equal(6, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Dispose_Before()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                }),
+                300
+            );
+
+            res.Messages.AssertEqual(
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 300)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Dispose_After()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                }),
+                470
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 470)
+            );
+
+            Assert.Equal(6, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Zero()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(205, 100),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    return IsPrime(x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(205, 100),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+
+            Assert.Equal(1, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var invoked = 0;
+            var ex = new Exception();
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile(x =>
+                {
+                    invoked++;
+                    if (invoked == 3)
+                        throw ex;
+                    return IsPrime(x);
+                })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(290, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 290)
+            );
+
+            Assert.Equal(3, invoked);
+        }
+
+        [Fact]
+        public void SkipWhile_Index()
+        {
+            var scheduler = new TestScheduler();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(205, 100),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile((x, i) => i < 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnNext(410, 17),
+                OnNext(450, 8),
+                OnNext(500, 23),
+                OnCompleted<int>(600)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 600)
+            );
+        }
+
+        [Fact]
+        public void SkipWhile_Index_Throw()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(205, 100),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnError<int>(400, ex)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile((x, i) => i < 5)
+            );
+
+            res.Messages.AssertEqual(
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnError<int>(400, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 400)
+            );
+        }
+
+        [Fact]
+        public void SkipWhile_Index_SelectorThrows()
+        {
+            var scheduler = new TestScheduler();
+
+            var ex = new Exception();
+
+            var xs = scheduler.CreateHotObservable(
+                OnNext(90, -1),
+                OnNext(110, -1),
+                OnNext(205, 100),
+                OnNext(210, 2),
+                OnNext(260, 5),
+                OnNext(290, 13),
+                OnNext(320, 3),
+                OnNext(350, 7),
+                OnNext(390, 4),
+                OnCompleted<int>(400)
+            );
+
+            var res = scheduler.Start(() =>
+                xs.SkipWhile((x, i) => { if (i < 5) return true; throw ex; })
+            );
+
+            res.Messages.AssertEqual(
+                OnError<int>(350, ex)
+            );
+
+            xs.Subscriptions.AssertEqual(
+                Subscribe(200, 350)
+            );
+        }
+
+        static bool IsPrime(int i)
+        {
+            if (i <= 1)
+                return false;
+
+            var max = (int)Math.Sqrt(i);
+            for (var j = 2; j <= max; ++j)
+                if (i % j == 0)
+                    return false;
+
+            return true;
+        }
+    }
+}

Неке датотеке нису приказане због велике количине промена